1 /*
2 Copyright (C) 2004-2011 Parallel Realities
3 Copyright (C) 2011-2015 Perpendicular Dimensions
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 
14 See the GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 
20 */
21 
22 #include "spawnPoints.h"
23 
okayToSpawnEnemy(const char * name,int x,int y)24 bool okayToSpawnEnemy(const char *name, int x, int y)
25 {
26 	// Don't summon other monsters!!
27 	if (map.fightingGaldov)
28 	{
29 		return false;
30 	}
31 
32 	if (engine.devNoMonsters)
33 	{
34 		return false;
35 	}
36 
37 	// stop enemies from appearing in the middile of doors
38 	Train *train = (Train*)map.trainList.getHead();
39 
40 	while (train->next != NULL)
41 	{
42 		train = (Train*)train->next;
43 
44 		// assume enemy is 20 x 20 pixels (most are at least) and trains are 64 x 64
45 		if (Collision::collision(x * BRICKSHIFT, y * BRICKSHIFT, 20, 20, train->x, train->y, 64, 64))
46 		{
47 			debug(("Couldn't add enemy '%s' - Collided with train\n", name));
48 			return false;
49 		}
50 	}
51 
52 	Entity *enemy = getDefinedEnemy(name);
53 
54 	if (map.isLiquid(x, y))
55 	{
56 		if (enemy->flags & ENT_SWIMS)
57 		{
58 			return true;
59 		}
60 
61 		debug(("Couldn't add enemy '%s' - Would drown\n", name));
62 
63 		return false;
64 	}
65 	else
66 	{
67 		if (enemy->flags & ENT_SWIMS)
68 		{
69 			debug(("Couldn't add enemy '%s' - Not in water\n", name));
70 
71 			return false;
72 		}
73 	}
74 
75 	if (enemy->flags & ENT_FLIES)
76 	{
77 		return true;
78 	}
79 
80 	for (int i = 0 ; i < 30 ; i++)
81 	{
82 		y++;
83 
84 		if (y > map.limitDown)
85 		{
86 			debug(("Couldn't add enemy '%s' - Outside map limits\n", name));
87 
88 			return false;
89 		}
90 
91 		if (map.isLiquid(x, y))
92 		{
93 			debug(("Couldn't add enemy '%s' - Would drown after free fall\n", name));
94 
95 			return false;
96 		}
97 
98 		if (map.isSolid(x, y))
99 		{
100 			return true;
101 		}
102 	}
103 
104 	debug(("Couldn't add enemy '%s' - Just couldn't!\n", name));
105 
106 	return false;
107 }
108 
doSpawnPoints()109 void doSpawnPoints()
110 {
111 	SpawnPoint *sp = (SpawnPoint*)map.spawnList.getHead();
112 
113 	int x, y;
114 
115 	char *enemy;
116 
117 	while (sp->next != NULL)
118 	{
119 		sp = (SpawnPoint*)sp->next;
120 
121 		sp->think();
122 
123 		if (!sp->active)
124 		{
125 			continue;
126 		}
127 
128 		if (sp->spawnType == SPW_HAZARD)
129 		{
130 			if (sp->spawnSubType == HAZARD_ROCKFALL)
131 			{
132 				x = (int)fabs(sp->x - player.x);
133 				y = (int)fabs(sp->y - player.y);
134 
135 				if ((x <= 640) && (y <= 480))
136 				{
137 					engine.setPlayerPosition((int)player.x + Math::rrand(-MAP_SHAKEAMOUNT, MAP_SHAKEAMOUNT), (int)player.y + Math::rrand(-MAP_SHAKEAMOUNT, MAP_SHAKEAMOUNT), map.limitLeft, map.limitRight, map.limitUp, map.limitDown);
138 				}
139 			}
140 			else if (sp->spawnSubType == HAZARD_STALAGTITES)
141 			{
142 				x = (int)fabs(sp->x - player.x);
143 				y = (int)fabs(sp->y - player.y);
144 
145 				if ((x <= 320) && (y <= 480))
146 				{
147 					engine.setPlayerPosition((int)player.x + Math::rrand(-MAP_SHAKEAMOUNT, MAP_SHAKEAMOUNT), (int)player.y + Math::rrand(-MAP_SHAKEAMOUNT, MAP_SHAKEAMOUNT), map.limitLeft, map.limitRight, map.limitUp, map.limitDown);
148 				}
149 			}
150 		}
151 
152 		if (sp->readyToSpawn())
153 		{
154 			if ((sp->spawnType != SPW_ENEMY) && (sp->spawnType != SPW_ITEM))
155 			{
156 				// If the player is too far away, don't spawn (unless it's random enemy / item spawning)
157 				x = (int)fabs(sp->x - player.x);
158 				y = (int)fabs(sp->y - player.y);
159 
160 				if ((x > 700) || (y > 500))
161 				{
162 					sp->reset();
163 					continue;
164 				}
165 			}
166 
167 			switch (sp->spawnType)
168 			{
169 				case SPW_HAZARD:
170 					switch (sp->spawnSubType)
171 					{
172 						case HAZARD_LAVABALL:
173 							engine.world.place(sp->x, sp->y);
174 							engine.world.currentWeapon = &weapon[WP_LAVABALL1];
175 							addBullet(&engine.world, Math::rrand(-5, 5), Math::rrand(-(5 + game.skill), -(2 + game.skill)));
176 							break;
177 						case HAZARD_ROCKFALL:
178 							engine.world.place(sp->x, sp->y);
179 							engine.world.currentWeapon = &weapon[WP_ROCK1];
180 							addBullet(&engine.world, Math::rrand(-2, 2), Math::rrand(0, 2));
181 							break;
182 						case HAZARD_BOMBS:
183 							engine.world.place(sp->x, sp->y);
184 							engine.world.currentWeapon = &weapon[WP_BOMB];
185 							addBullet(&engine.world, Math::rrand(-2, 2), Math::rrand(0, 2));
186 							break;
187 						case HAZARD_EXPLOSION:
188 							x = sp->x + Math::rrand(-128, 128);
189 							y = sp->y + Math::rrand(-128, 128);
190 							addExplosion(x, y, 50, &engine.world);
191 							x = x >> BRICKSHIFT;
192 							y = y >> BRICKSHIFT;
193 							if (map.isSolid(x, y))
194 							{
195 								int waterLevel = (int)map.waterLevel;
196 								if (waterLevel == -1 || y < waterLevel)
197 								{
198 									map.data[x][y] = MAP_AIR;
199 								}
200 								else if (y == waterLevel)
201 								{
202 									map.data[x][y] = MAP_WATERANIM;
203 								}
204 								else
205 								{
206 									map.data[x][y] = MAP_WATER;
207 								}
208 							}
209 							break;
210 						case HAZARD_POWERBULLETS:
211 							engine.world.place(sp->x, sp->y);
212 							engine.world.currentWeapon = &weapon[WP_SHELLS];
213 							x = engine.world.currentWeapon->dx;
214 							if (player.x < sp->x) x = -x;
215 							addBullet(&engine.world, x, 0);
216 							break;
217 						case HAZARD_STALAGTITES:
218 							engine.world.place(sp->x, sp->y);
219 							engine.world.currentWeapon = &weapon[WP_STALAGTITE];
220 							addBullet(&engine.world, 0, 2);
221 							break;
222 						default:
223 							printf("Spawn Subtype is unknown!\n");
224 							break;
225 					}
226 					break;
227 
228 				case SPW_ENEMY:
229 					if (game.missionOverReason != MIS_INPROGRESS)
230 						break;
231 
232 					enemy = map.getSpawnableEnemy();
233 
234 					x = (int)(player.x) >> BRICKSHIFT;
235 					y = (int)(player.y) >> BRICKSHIFT;
236 
237 					x += Math::rrand(-10, 10);
238 					y += Math::rrand(-10, 10);
239 
240 					if ((x >= 0) && (y >= 0) && (x < MAPWIDTH) && (y < MAPHEIGHT))
241 					{
242 						if ((map.data[x][y] == MAP_AIR) || (map.data[x][y] == MAP_WATER))
243 						{
244 							if (okayToSpawnEnemy(enemy, x, y))
245 							{
246 								x = x << BRICKSHIFT;
247 								y = y << BRICKSHIFT;
248 								addEnemy(enemy, x, y, ENT_SPAWNED);
249 								addTeleportParticles(x, y, 25, SND_TELEPORT2);
250 							}
251 						}
252 					}
253 
254 					break;
255 
256 				case SPW_ITEM:
257 					if (game.missionOverReason != MIS_INPROGRESS)
258 						break;
259 
260 					x = (int)(player.x) >> BRICKSHIFT;
261 					y = (int)(player.y) >> BRICKSHIFT;
262 
263 					x += Math::rrand(-10, 10);
264 					y += Math::rrand(-10, 10);
265 
266 					if ((x >= 0) && (y >= 0))
267 					{
268 						if (map.data[x][y] == MAP_AIR)
269 						{
270 							x = x << BRICKSHIFT;
271 							y = y << BRICKSHIFT;
272 							dropHelperItems(x, y);
273 							addTeleportParticles(x, y, 5, SND_TELEPORT2);
274 						}
275 					}
276 
277 					break;
278 
279 				case SPW_BOSSBULLET:
280 
281 					if (game.missionOverReason != MIS_INPROGRESS)
282 						break;
283 
284 					if (map.boss[sp->spawnSubType]->health > 0)
285 					{
286 						map.boss[sp->spawnSubType]->active = true;
287 					}
288 
289 					break;
290 
291 				default:
292 					debug(("Spawn Type is unknown!\n"));
293 					break;
294 			}
295 
296 			sp->reset();
297 
298 			if (sp->spawnType == SPW_ENEMY)
299 			{
300 				if ((Math::prand() % (game.skill + 2)) > 0)
301 				{
302 					sp->requiredInterval = Math::rrand(1, 30);
303 				}
304 			}
305 		}
306 	}
307 }
308