1 /*
2 Copyright (C) 2009-2021 Parallel Realities
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "../headers.h"
21
22 #include "../entity.h"
23 #include "../system/error.h"
24 #include "../system/properties.h"
25 #include "../system/random.h"
26 #include "animation.h"
27 #include "decoration.h"
28 #include "graphics.h"
29
30 static EntityList *decoration;
31
32 extern Entity *self, player;
33 extern Game game;
34
35 static void move(void);
36 static void entityWait(void);
37 static void finish(void);
38 static void timeout(void);
39 static int drawPixel(void);
40
41 static Constructor decorations[] = {
42 {"decoration/chimney_smoke", &addSmoke},
43 {"decoration/multiple_sparkles", &addMultipleSparkles}
44 };
45
46 static int length = sizeof(decorations) / sizeof(Constructor);
47
addDecoration(char * name,int x,int y)48 Entity *addDecoration(char *name, int x, int y)
49 {
50 int i;
51
52 for (i=0;i<length;i++)
53 {
54 if (strcmpignorecase(decorations[i].name, name) == 0)
55 {
56 return decorations[i].construct(x, y, name);
57 }
58 }
59
60 showErrorAndExit("Could not find decoration %s", name);
61
62 return NULL;
63 }
64
freeDecorations()65 void freeDecorations()
66 {
67 EntityList *p, *q;
68
69 if (decoration != NULL)
70 {
71 for (p=decoration->next;p!=NULL;p=q)
72 {
73 free(p->entity);
74
75 q = p->next;
76
77 free(p);
78 }
79
80 free(decoration);
81 }
82
83 decoration = malloc(sizeof(EntityList));
84
85 if (decoration == NULL)
86 {
87 showErrorAndExit("Failed to allocate a whole %d bytes for Entity List", (int)sizeof(EntityList));
88 }
89
90 decoration->next = NULL;
91 }
92
getFreeDecoration()93 Entity *getFreeDecoration()
94 {
95 Entity *e;
96
97 e = malloc(sizeof(Entity));
98
99 if (e == NULL)
100 {
101 showErrorAndExit("Failed to allocate %d bytes for a Decoration", (int)sizeof(Entity));
102 }
103
104 memset(e, 0, sizeof(Entity));
105
106 e->inUse = TRUE;
107
108 e->active = TRUE;
109
110 e->frameSpeed = 1;
111
112 e->alpha = 255;
113
114 addEntityToList(decoration, e);
115
116 return e;
117 }
118
doDecorations()119 void doDecorations()
120 {
121 EntityList *el;
122
123 /* Loop through the Decorations and perform their action */
124
125 for (el=decoration->next;el!=NULL;el=el->next)
126 {
127 self = el->entity;
128
129 if (self->inUse == TRUE)
130 {
131 self->action();
132 }
133 }
134 }
135
drawDecorations()136 void drawDecorations()
137 {
138 int removeCount;
139 EntityList *el, *prev, *el2;
140
141 for (el=decoration->next;el!=NULL;el=el->next)
142 {
143 self = el->entity;
144
145 if (self->inUse == TRUE)
146 {
147 self->draw();
148 }
149 }
150
151 if (game.frames % 300 == 0)
152 {
153 removeCount = 0;
154
155 prev = decoration;
156
157 for (el=decoration->next;el!=NULL;el=el2)
158 {
159 el2 = el->next;
160
161 if (el->entity->inUse == FALSE)
162 {
163 prev->next = el2;
164
165 removeCount++;
166
167 free(el->entity);
168
169 el->entity = NULL;
170
171 free(el);
172
173 el = NULL;
174 }
175
176 else
177 {
178 prev = prev->next;
179 }
180 }
181
182 #if DEV == 1
183 if (removeCount != 0)
184 {
185 printf("Removed %d decorations taking up %d bytes\n", removeCount, (int)sizeof(Entity) * removeCount);
186 }
187 #endif
188 }
189 }
190
addStarExplosion(int x,int y)191 void addStarExplosion(int x, int y)
192 {
193 int i;
194 Entity *e;
195
196 for (i=0;i<40;i++)
197 {
198 e = getFreeDecoration();
199
200 if (e == NULL)
201 {
202 return;
203 }
204
205 loadProperties("decoration/star", e);
206
207 e->dirX = prand() % 20;
208 e->dirY = prand() % 20;
209
210 if (prand() % 2 == 0)
211 {
212 e->dirX *= -1;
213 }
214
215 if (prand() % 2 == 0)
216 {
217 e->dirY *= -1;
218 }
219
220 e->dirX /= 10;
221 e->dirY /= 10;
222
223 e->thinkTime = 20 + (prand() % 30);
224
225 e->x = x;
226 e->y = y;
227
228 e->action = &move;
229 e->draw = &drawLoopingAnimationToMap;
230 }
231 }
232
addParticleExplosion(int x,int y)233 void addParticleExplosion(int x, int y)
234 {
235 int i;
236 Entity *e;
237
238 for (i=0;i<40;i++)
239 {
240 e = getFreeDecoration();
241
242 if (e == NULL)
243 {
244 return;
245 }
246
247 loadProperties("decoration/particle", e);
248
249 e->dirX = prand() % 20;
250 e->dirY = prand() % 20;
251
252 if (prand() % 2 == 0)
253 {
254 e->dirX *= -1;
255 }
256
257 if (prand() % 2 == 0)
258 {
259 e->dirY *= -1;
260 }
261
262 e->dirX /= 10;
263 e->dirY /= 10;
264
265 e->thinkTime = 20 + (prand() % 30);
266
267 e->x = x;
268 e->y = y;
269
270 e->action = &move;
271 e->draw = &drawLoopingAnimationToMap;
272
273 setEntityAnimationByID(e, prand() % 5);
274 }
275 }
276
addParticle(int x,int y)277 Entity *addParticle(int x, int y)
278 {
279 Entity *e;
280
281 e = getFreeDecoration();
282
283 if (e == NULL)
284 {
285 return NULL;
286 }
287
288 loadProperties("decoration/particle", e);
289
290 e->thinkTime = 20 + (prand() % 30);
291
292 e->x = x;
293 e->y = y;
294
295 e->action = &move;
296 e->draw = &drawLoopingAnimationToMap;
297
298 setEntityAnimationByID(e, prand() % 5);
299
300 return e;
301 }
302
addMultipleSparkles(int x,int y,char * name)303 Entity *addMultipleSparkles(int x, int y, char *name)
304 {
305 int i, xx, yy;
306
307 for (i=0;i<5;i++)
308 {
309 xx = x + (prand() % 30) * (prand() % 2 == 0 ? -1 : 1);
310 yy = y + (prand() % 30) * (prand() % 2 == 0 ? -1 : 1);
311
312 addSparkle(xx, yy);
313 }
314
315 return NULL;
316 }
317
addSparkle(int x,int y)318 void addSparkle(int x, int y)
319 {
320 Entity *e = getFreeDecoration();
321
322 if (e == NULL)
323 {
324 return;
325 }
326
327 loadProperties("decoration/sparkle", e);
328
329 e->thinkTime = 15;
330
331 e->x = x;
332 e->y = y;
333
334 e->action = &entityWait;
335 e->draw = &drawLoopingAnimationToMap;
336 e->animationCallback = &finish;
337 }
338
addTrail(int x,int y,char * name,int thinkTime)339 Entity *addTrail(int x, int y, char *name, int thinkTime)
340 {
341 Entity *e = getFreeDecoration();
342
343 if (e == NULL)
344 {
345 return NULL;
346 }
347
348 loadProperties(name, e);
349
350 e->thinkTime = thinkTime;
351
352 e->x = x;
353 e->y = y;
354
355 e->action = &timeout;
356 e->draw = &drawLoopingAnimationToMap;
357
358 return e;
359 }
360
addSmoke(int x,int y,char * name)361 Entity *addSmoke(int x, int y, char *name)
362 {
363 Entity *e = getFreeDecoration();
364
365 if (e == NULL)
366 {
367 return NULL;
368 }
369
370 loadProperties(name, e);
371
372 e->x = x;
373 e->y = y;
374
375 e->dirX = 0;
376
377 e->dirY = -e->speed;
378
379 e->frameSpeed *= 10;
380
381 e->frameSpeed /= 1 + prand() % 10;
382
383 e->thinkTime = 300;
384
385 e->action = &move;
386 e->draw = &drawLoopingAnimationToMap;
387 e->animationCallback = &finish;
388
389 return e;
390 }
391
finish()392 static void finish()
393 {
394 self->inUse = FALSE;
395 }
396
timeout()397 static void timeout()
398 {
399 self->thinkTime--;
400
401 if (self->thinkTime <= 0)
402 {
403 finish();
404 }
405 }
406
entityWait()407 static void entityWait()
408 {
409 self->x += self->dirX;
410 self->y += self->dirY;
411 }
412
move()413 static void move()
414 {
415 self->x += self->dirX;
416 self->y += self->dirY;
417
418 self->thinkTime--;
419
420 if (self->thinkTime <= 0)
421 {
422 self->inUse = FALSE;
423 }
424 }
425
addBlood(int x,int y)426 void addBlood(int x, int y)
427 {
428 Entity *e = getFreeDecoration();
429
430 if (e == NULL)
431 {
432 return;
433 }
434
435 loadProperties("decoration/blood", e);
436
437 e->thinkTime = 60;
438
439 e->x = x;
440 e->y = y;
441
442 e->dirY = 0.1;
443
444 e->action = &move;
445 e->draw = &drawLoopingAnimationToMap;
446 }
447
addBasicDecoration(int x,int y,char * name)448 Entity *addBasicDecoration(int x, int y, char *name)
449 {
450 Entity *e = getFreeDecoration();
451
452 if (e == NULL)
453 {
454 return NULL;
455 }
456
457 loadProperties(name, e);
458
459 e->x = x;
460 e->y = y;
461
462 e->action = &move;
463 e->draw = &drawLoopingAnimationToMap;
464
465 return e;
466 }
467
addDecorationFromScript(char * line)468 void addDecorationFromScript(char *line)
469 {
470 char decorationName[MAX_VALUE_LENGTH], entityName[MAX_VALUE_LENGTH];
471 Entity *e;
472
473 sscanf(line, "%s \"%[^\"]\"", decorationName, entityName);
474
475 if (strcmpignorecase(entityName, "Edgar") == 0)
476 {
477 e = &player;
478 }
479
480 else
481 {
482 e = getEntityByObjectiveName(entityName);
483 }
484
485 if (e == NULL)
486 {
487 showErrorAndExit("Decoration could not find Entity %s", entityName);
488 }
489
490 addDecoration(decorationName, e->x + e->w / 2, e->y + e->h / 2);
491 }
492
addPixelDecoration(int x,int y)493 Entity *addPixelDecoration(int x, int y)
494 {
495 Entity *e = getFreeDecoration();
496
497 if (e == NULL)
498 {
499 return NULL;
500 }
501
502 e->x = x;
503 e->y = y;
504
505 e->action = &move;
506 e->draw = &drawPixel;
507
508 return e;
509 }
510
drawPixel()511 static int drawPixel()
512 {
513 putPixelToMap(self->x, self->y, self->health, self->maxHealth, self->mental);
514
515 return TRUE;
516 }
517