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