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