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 "../enemy/enemies.h"
23 #include "../entity.h"
24 #include "../graphics/animation.h"
25 #include "../graphics/decoration.h"
26 #include "../item/item.h"
27 #include "../item/key_items.h"
28 #include "../player.h"
29 #include "../projectile.h"
30 #include "../system/error.h"
31 #include "../system/properties.h"
32 #include "../system/random.h"
33 
34 extern Entity *self;
35 
36 static void spawn(void);
37 static void init(void);
38 
addSpawner(int x,int y,char * entityToSpawn)39 Entity *addSpawner(int x, int y, char *entityToSpawn)
40 {
41 	Entity *e = getFreeEntity();
42 
43 	if (e == NULL)
44 	{
45 		showErrorAndExit("No free slots to add a Spawner");
46 	}
47 
48 	loadProperties(entityToSpawn, e);
49 
50 	e->x = x;
51 	e->y = y;
52 
53 	e->draw = &drawLoopingAnimationToMap;
54 	e->touch = NULL;
55 	e->action = &init;
56 
57 	e->type = SPAWNER;
58 
59 	setEntityAnimation(e, "STAND");
60 
61 	return e;
62 }
63 
init()64 static void init()
65 {
66 	Entity *e;
67 	char spawnList[MAX_VALUE_LENGTH], name[MAX_VALUE_LENGTH];
68 	char *token;
69 
70 	#if DEV == 1
71 	if (strlen(self->objectiveName) == 0)
72 	{
73 		showErrorAndExit("Spawner at %f %f is not set correctly", self->x, self->y);
74 	}
75 	#endif
76 
77 	/* Precache the Entities to spawn */
78 
79 	e = getFreeEntity();
80 
81 	if (e == NULL)
82 	{
83 		showErrorAndExit("No free slots to add spawner entity");
84 	}
85 
86 	STRNCPY(spawnList, self->objectiveName, MAX_VALUE_LENGTH);
87 
88 	token = strtok(spawnList, "|");
89 
90 	while (token != NULL)
91 	{
92 		if (strcmpignorecase(self->name, "common/spawner") == 0 && strstr(token, "/") == NULL)
93 		{
94 			SNPRINTF(name, sizeof(name), "enemy/%s", token);
95 		}
96 
97 		else
98 		{
99 			SNPRINTF(name, sizeof(name), "%s", token);
100 		}
101 
102 		loadProperties(name, e);
103 
104 		token = strtok(NULL, "|");
105 	}
106 
107 	e->inUse = FALSE;
108 
109 	self->action = &spawn;
110 
111 	self->action();
112 }
113 
spawn()114 static void spawn()
115 {
116 	int distance;
117 	char spawnList[MAX_VALUE_LENGTH], name[MAX_VALUE_LENGTH];
118 	char *token;
119 	int spawnIndex = 0, spawnCount = 0;
120 	Entity *e;
121 
122 	if (self->active == TRUE)
123 	{
124 		self->thinkTime--;
125 
126 		if (self->thinkTime <= 0)
127 		{
128 			self->thinkTime = 0;
129 
130 			if (self->health < 0)
131 			{
132 				/* Don't spawn if the player is too close or too far away */
133 
134 				distance = self->health == -2 ? getDistanceFromPlayer(self) : 0;
135 
136 				if (self->health == -1 || self->health == -3 || (self->health == -2 && distance > SCREEN_WIDTH && distance < SCREEN_WIDTH + TILE_SIZE))
137 				{
138 					if (strcmpignorecase(self->name, "common/decoration_spawner") == 0)
139 					{
140 						e = addDecoration(self->objectiveName, self->x, self->y);
141 
142 						e->x += (self->w - e->w) / 2;
143 						e->y += (self->h - e->h) / 2;
144 					}
145 
146 					else if (strcmpignorecase(self->name, "common/projectile_spawner") == 0)
147 					{
148 						e = addProjectile(self->objectiveName, self, self->x, self->y, 0, 0);
149 
150 						e->x += (self->w - e->w) / 2;
151 
152 						if (e->flags & FLY)
153 						{
154 							e->dirX = (self->face == LEFT ? -e->speed : e->speed);
155 
156 							e->face = self->face;
157 
158 							e->y += (self->h - e->h) / 2;
159 						}
160 					}
161 
162 					else if (strcmpignorecase(self->name, "common/item_spawner") == 0)
163 					{
164 						e = addPermanentItem(self->objectiveName, self->x, self->y);
165 
166 						e->x += (self->w - e->w) / 2;
167 
168 						e->face = self->face;
169 					}
170 
171 					else if (strcmpignorecase(self->name, "common/key_item_spawner") == 0)
172 					{
173 						e = addKeyItem(self->objectiveName, self->x, self->y);
174 
175 						e->x += (self->w - e->w) / 2;
176 
177 						e->face = self->face;
178 					}
179 
180 					else
181 					{
182 						STRNCPY(spawnList, self->objectiveName, MAX_VALUE_LENGTH);
183 
184 						token = strtok(spawnList, "|");
185 
186 						while (token != NULL)
187 						{
188 							token = strtok(NULL, "|");
189 
190 							spawnCount++;
191 						}
192 
193 						if (spawnCount == 0)
194 						{
195 							showErrorAndExit("Spawner at %f %f has no spawn list", self->x, self->y);
196 						}
197 
198 						spawnIndex = prand() % spawnCount;
199 
200 						STRNCPY(spawnList, self->objectiveName, MAX_VALUE_LENGTH);
201 
202 						spawnCount = 0;
203 
204 						token = strtok(spawnList, "|");
205 
206 						while (token != NULL)
207 						{
208 							if (spawnCount == spawnIndex)
209 							{
210 								break;
211 							}
212 
213 							token = strtok(NULL, "|");
214 
215 							spawnCount++;
216 						}
217 
218 						if (strstr(token, "/") == NULL)
219 						{
220 							SNPRINTF(name, sizeof(name), "enemy/%s", token);
221 						}
222 
223 						else
224 						{
225 							SNPRINTF(name, sizeof(name), "%s", token);
226 						}
227 
228 						e = addEnemy(name, self->x, self->y);
229 
230 						e->x += (self->w - e->w) / 2;
231 						e->y += (self->h - e->h) / 2;
232 
233 						e->startX = self->startX;
234 						e->startY = self->startY;
235 
236 						e->endX = self->endX;
237 						e->endY = self->endY;
238 
239 						e->face = self->face;
240 
241 						if (self->health == -2)
242 						{
243 							e->flags |= SPAWNED_IN;
244 
245 							e->spawnTime = SPAWNED_IN_TIME;
246 						}
247 					}
248 
249 					if (self->speed != 0)
250 					{
251 						e->speed = self->speed;
252 					}
253 
254 					self->thinkTime = self->maxThinkTime;
255 
256 					if (self->health == -3)
257 					{
258 						self->active = FALSE;
259 					}
260 				}
261 			}
262 
263 			else
264 			{
265 				STRNCPY(spawnList, self->objectiveName, MAX_VALUE_LENGTH);
266 
267 				token = strtok(spawnList, "|");
268 
269 				while (token != NULL)
270 				{
271 					token = strtok(NULL, "|");
272 
273 					spawnCount++;
274 				}
275 
276 				if (spawnCount == 0)
277 				{
278 					showErrorAndExit("Spawner at %f %f has no spawn list", self->x, self->y);
279 				}
280 
281 				spawnIndex = prand() % spawnCount;
282 
283 				STRNCPY(spawnList, self->objectiveName, MAX_VALUE_LENGTH);
284 
285 				spawnCount = 0;
286 
287 				token = strtok(spawnList, "|");
288 
289 				while (token != NULL)
290 				{
291 					if (spawnCount == spawnIndex)
292 					{
293 						break;
294 					}
295 
296 					token = strtok(NULL, "|");
297 
298 					spawnCount++;
299 				}
300 
301 				if (strstr(token, "/") == NULL)
302 				{
303 					SNPRINTF(name, sizeof(name), "enemy/%s", token);
304 				}
305 
306 				else
307 				{
308 					SNPRINTF(name, sizeof(name), "%s", token);
309 				}
310 
311 				e = addEnemy(name, self->x, self->y);
312 
313 				e->x += (self->w - e->w) / 2;
314 				e->y += (self->h - e->h) / 2;
315 
316 				e->startX = self->startX;
317 				e->startY = self->startY;
318 
319 				e->endX = self->endX;
320 				e->endY = self->endY;
321 
322 				e->face = self->face;
323 
324 				if (self->speed != 0)
325 				{
326 					e->speed = self->speed;
327 				}
328 
329 				self->health--;
330 
331 				if (self->health == 0)
332 				{
333 					self->inUse = FALSE;
334 				}
335 
336 				self->thinkTime = self->maxThinkTime;
337 			}
338 		}
339 	}
340 }
341