diff options
author | Dave Chapman <dave@dchapman.com> | 2006-03-28 15:44:01 +0000 |
---|---|---|
committer | Dave Chapman <dave@dchapman.com> | 2006-03-28 15:44:01 +0000 |
commit | 47f4a458d636a889e955e68f896708f1276febc0 (patch) | |
tree | 99f770c02ef606f0abbdcd332ac39e69830d8007 /apps/plugins/doom/p_enemy.c | |
parent | fff7d6157d56f233cad5c2003475e47a5ff809a7 (diff) | |
download | rockbox-47f4a458d636a889e955e68f896708f1276febc0.tar.gz rockbox-47f4a458d636a889e955e68f896708f1276febc0.zip |
Patch #2969 - Doom! Currently only working on the H300.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9312 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/doom/p_enemy.c')
-rw-r--r-- | apps/plugins/doom/p_enemy.c | 2623 |
1 files changed, 2623 insertions, 0 deletions
diff --git a/apps/plugins/doom/p_enemy.c b/apps/plugins/doom/p_enemy.c new file mode 100644 index 0000000000..b1f40bc5b8 --- /dev/null +++ b/apps/plugins/doom/p_enemy.c | |||
@@ -0,0 +1,2623 @@ | |||
1 | /* Emacs style mode select -*- C++ -*- | ||
2 | *----------------------------------------------------------------------------- | ||
3 | * | ||
4 | * | ||
5 | * PrBoom a Doom port merged with LxDoom and LSDLDoom | ||
6 | * based on BOOM, a modified and improved DOOM engine | ||
7 | * Copyright (C) 1999 by | ||
8 | * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman | ||
9 | * Copyright (C) 1999-2000 by | ||
10 | * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||
25 | * 02111-1307, USA. | ||
26 | * | ||
27 | * DESCRIPTION: | ||
28 | * Enemy thinking, AI. | ||
29 | * Action Pointer Functions | ||
30 | * that are associated with states/frames. | ||
31 | * | ||
32 | *-----------------------------------------------------------------------------*/ | ||
33 | |||
34 | #include "doomstat.h" | ||
35 | #include "m_random.h" | ||
36 | #include "r_main.h" | ||
37 | #include "p_maputl.h" | ||
38 | #include "p_map.h" | ||
39 | #include "p_setup.h" | ||
40 | #include "p_spec.h" | ||
41 | #include "s_sound.h" | ||
42 | #include "sounds.h" | ||
43 | #include "p_inter.h" | ||
44 | #include "g_game.h" | ||
45 | #include "p_enemy.h" | ||
46 | #include "p_tick.h" | ||
47 | #include "m_bbox.h" | ||
48 | |||
49 | #include "rockmacros.h" | ||
50 | |||
51 | static mobj_t *current_actor; | ||
52 | |||
53 | typedef enum { | ||
54 | DI_EAST, | ||
55 | DI_NORTHEAST, | ||
56 | DI_NORTH, | ||
57 | DI_NORTHWEST, | ||
58 | DI_WEST, | ||
59 | DI_SOUTHWEST, | ||
60 | DI_SOUTH, | ||
61 | DI_SOUTHEAST, | ||
62 | DI_NODIR, | ||
63 | NUMDIRS | ||
64 | } dirtype_t; | ||
65 | |||
66 | void A_Fall(mobj_t *actor); | ||
67 | void A_FaceTarget(mobj_t *actor); | ||
68 | static void P_NewChaseDir(mobj_t *actor); | ||
69 | void P_ZBumpCheck(mobj_t *); // phares | ||
70 | |||
71 | // | ||
72 | // ENEMY THINKING | ||
73 | // Enemies are allways spawned | ||
74 | // with targetplayer = -1, threshold = 0 | ||
75 | // Most monsters are spawned unaware of all players, | ||
76 | // but some can be made preaware | ||
77 | // | ||
78 | |||
79 | // | ||
80 | // Called by P_NoiseAlert. | ||
81 | // Recursively traverse adjacent sectors, | ||
82 | // sound blocking lines cut off traversal. | ||
83 | // | ||
84 | // killough 5/5/98: reformatted, cleaned up | ||
85 | |||
86 | static void P_RecursiveSound(sector_t *sec, int soundblocks, | ||
87 | mobj_t *soundtarget) | ||
88 | { | ||
89 | int i; | ||
90 | |||
91 | // wake up all monsters in this sector | ||
92 | if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1) | ||
93 | return; // already flooded | ||
94 | |||
95 | sec->validcount = validcount; | ||
96 | sec->soundtraversed = soundblocks+1; | ||
97 | P_SetTarget(&sec->soundtarget, soundtarget); | ||
98 | |||
99 | for (i=0; i<sec->linecount; i++) | ||
100 | { | ||
101 | sector_t *other; | ||
102 | line_t *check = sec->lines[i]; | ||
103 | |||
104 | if (!(check->flags & ML_TWOSIDED)) | ||
105 | continue; | ||
106 | |||
107 | P_LineOpening(check); | ||
108 | |||
109 | if (openrange <= 0) | ||
110 | continue; // closed door | ||
111 | |||
112 | other=sides[check->sidenum[sides[check->sidenum[0]].sector==sec]].sector; | ||
113 | |||
114 | if (!(check->flags & ML_SOUNDBLOCK)) | ||
115 | P_RecursiveSound(other, soundblocks, soundtarget); | ||
116 | else | ||
117 | if (!soundblocks) | ||
118 | P_RecursiveSound(other, 1, soundtarget); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | // | ||
123 | // P_NoiseAlert | ||
124 | // If a monster yells at a player, | ||
125 | // it will alert other monsters to the player. | ||
126 | // | ||
127 | void P_NoiseAlert(mobj_t *target, mobj_t *emitter) | ||
128 | { | ||
129 | validcount++; | ||
130 | P_RecursiveSound(emitter->subsector->sector, 0, target); | ||
131 | } | ||
132 | |||
133 | // | ||
134 | // P_CheckMeleeRange | ||
135 | // | ||
136 | |||
137 | static boolean P_CheckMeleeRange(mobj_t *actor) | ||
138 | { | ||
139 | mobj_t *pl = actor->target; | ||
140 | |||
141 | return // killough 7/18/98: friendly monsters don't attack other friends | ||
142 | pl && !(actor->flags & pl->flags & MF_FRIEND) && | ||
143 | (P_AproxDistance(pl->x-actor->x, pl->y-actor->y) < | ||
144 | MELEERANGE - 20*FRACUNIT + pl->info->radius) && | ||
145 | P_CheckSight(actor, actor->target); | ||
146 | } | ||
147 | |||
148 | // | ||
149 | // P_HitFriend() | ||
150 | // | ||
151 | // killough 12/98 | ||
152 | // This function tries to prevent shooting at friends | ||
153 | |||
154 | static boolean P_HitFriend(mobj_t *actor) | ||
155 | { | ||
156 | return actor->flags & MF_FRIEND && actor->target && | ||
157 | (P_AimLineAttack(actor, | ||
158 | R_PointToAngle2(actor->x, actor->y, | ||
159 | actor->target->x, actor->target->y), | ||
160 | P_AproxDistance(actor->x-actor->target->x, | ||
161 | actor->y-actor->target->y), 0), | ||
162 | linetarget) && linetarget != actor->target && | ||
163 | !((linetarget->flags ^ actor->flags) & MF_FRIEND); | ||
164 | } | ||
165 | |||
166 | // | ||
167 | // P_CheckMissileRange | ||
168 | // | ||
169 | boolean P_CheckMissileRange(mobj_t *actor) | ||
170 | { | ||
171 | fixed_t dist; | ||
172 | |||
173 | if (!P_CheckSight(actor, actor->target)) | ||
174 | return false; | ||
175 | |||
176 | if (actor->flags & MF_JUSTHIT) | ||
177 | { // the target just hit the enemy, so fight back! | ||
178 | actor->flags &= ~MF_JUSTHIT; | ||
179 | |||
180 | /* killough 7/18/98: no friendly fire at corpses | ||
181 | * killough 11/98: prevent too much infighting among friends | ||
182 | * cph - yikes, talk about fitting everything on one line... */ | ||
183 | |||
184 | return | ||
185 | !(actor->flags & MF_FRIEND) || | ||
186 | (actor->target->health > 0 && | ||
187 | (!(actor->target->flags & MF_FRIEND) || | ||
188 | (actor->target->player ? | ||
189 | monster_infighting || P_Random(pr_defect) >128 : | ||
190 | !(actor->target->flags & MF_JUSTHIT) && P_Random(pr_defect) >128))); | ||
191 | } | ||
192 | |||
193 | /* killough 7/18/98: friendly monsters don't attack other friendly | ||
194 | * monsters or players (except when attacked, and then only once) | ||
195 | */ | ||
196 | if (actor->flags & actor->target->flags & MF_FRIEND) | ||
197 | return false; | ||
198 | |||
199 | if (actor->reactiontime) | ||
200 | return false; // do not attack yet | ||
201 | |||
202 | // OPTIMIZE: get this from a global checksight | ||
203 | dist = P_AproxDistance ( actor->x-actor->target->x, | ||
204 | actor->y-actor->target->y) - 64*FRACUNIT; | ||
205 | |||
206 | if (!actor->info->meleestate) | ||
207 | dist -= 128*FRACUNIT; // no melee attack, so fire more | ||
208 | |||
209 | dist >>= FRACBITS; | ||
210 | |||
211 | if (actor->type == MT_VILE) | ||
212 | if (dist > 14*64) | ||
213 | return false; // too far away | ||
214 | |||
215 | |||
216 | if (actor->type == MT_UNDEAD) | ||
217 | { | ||
218 | if (dist < 196) | ||
219 | return false; // close for fist attack | ||
220 | dist >>= 1; | ||
221 | } | ||
222 | |||
223 | if (actor->type == MT_CYBORG || | ||
224 | actor->type == MT_SPIDER || | ||
225 | actor->type == MT_SKULL) | ||
226 | dist >>= 1; | ||
227 | |||
228 | if (dist > 200) | ||
229 | dist = 200; | ||
230 | |||
231 | if (actor->type == MT_CYBORG && dist > 160) | ||
232 | dist = 160; | ||
233 | |||
234 | if (P_Random(pr_missrange) < dist) | ||
235 | return false; | ||
236 | |||
237 | if (P_HitFriend(actor)) | ||
238 | return false; | ||
239 | |||
240 | return true; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * P_IsOnLift | ||
245 | * | ||
246 | * killough 9/9/98: | ||
247 | * | ||
248 | * Returns true if the object is on a lift. Used for AI, | ||
249 | * since it may indicate the need for crowded conditions, | ||
250 | * or that a monster should stay on the lift for a while | ||
251 | * while it goes up or down. | ||
252 | */ | ||
253 | |||
254 | static boolean P_IsOnLift(const mobj_t *actor) | ||
255 | { | ||
256 | const sector_t *sec = actor->subsector->sector; | ||
257 | line_t line; | ||
258 | int l; | ||
259 | |||
260 | // Short-circuit: it's on a lift which is active. | ||
261 | if (sec->floordata && ((thinker_t *) sec->floordata)->function==T_PlatRaise) | ||
262 | return true; | ||
263 | |||
264 | // Check to see if it's in a sector which can be activated as a lift. | ||
265 | if ((line.tag = sec->tag)) | ||
266 | for (l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0;) | ||
267 | switch (lines[l].special) | ||
268 | { | ||
269 | case 10: case 14: case 15: case 20: case 21: case 22: | ||
270 | case 47: case 53: case 62: case 66: case 67: case 68: | ||
271 | case 87: case 88: case 95: case 120: case 121: case 122: | ||
272 | case 123: case 143: case 162: case 163: case 181: case 182: | ||
273 | case 144: case 148: case 149: case 211: case 227: case 228: | ||
274 | case 231: case 232: case 235: case 236: | ||
275 | return true; | ||
276 | } | ||
277 | |||
278 | return false; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * P_IsUnderDamage | ||
283 | * | ||
284 | * killough 9/9/98: | ||
285 | * | ||
286 | * Returns nonzero if the object is under damage based on | ||
287 | * their current position. Returns 1 if the damage is moderate, | ||
288 | * -1 if it is serious. Used for AI. | ||
289 | */ | ||
290 | |||
291 | static int P_IsUnderDamage(mobj_t *actor) | ||
292 | { | ||
293 | const struct msecnode_s *seclist; | ||
294 | const ceiling_t *cl; // Crushing ceiling | ||
295 | int dir = 0; | ||
296 | for (seclist=actor->touching_sectorlist; seclist; seclist=seclist->m_tnext) | ||
297 | if ((cl = seclist->m_sector->ceilingdata) && | ||
298 | cl->thinker.function == T_MoveCeiling) | ||
299 | dir |= cl->direction; | ||
300 | return dir; | ||
301 | } | ||
302 | |||
303 | // | ||
304 | // P_Move | ||
305 | // Move in the current direction, | ||
306 | // returns false if the move is blocked. | ||
307 | // | ||
308 | |||
309 | static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; | ||
310 | static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; | ||
311 | |||
312 | // 1/11/98 killough: Limit removed on special lines crossed | ||
313 | extern line_t **spechit; // New code -- killough | ||
314 | extern int numspechit; | ||
315 | |||
316 | static boolean P_Move(mobj_t *actor, boolean dropoff) /* killough 9/12/98 */ | ||
317 | { | ||
318 | fixed_t tryx, tryy, deltax, deltay, origx, origy; | ||
319 | boolean try_ok; | ||
320 | int movefactor = ORIG_FRICTION_FACTOR; // killough 10/98 | ||
321 | int friction = ORIG_FRICTION; | ||
322 | int speed; | ||
323 | |||
324 | if (actor->movedir == DI_NODIR) | ||
325 | return false; | ||
326 | |||
327 | #ifdef RANGECHECK | ||
328 | if ((unsigned)actor->movedir >= 8) | ||
329 | I_Error ("P_Move: Weird actor->movedir!"); | ||
330 | #endif | ||
331 | |||
332 | // killough 10/98: make monsters get affected by ice and sludge too: | ||
333 | |||
334 | if (monster_friction) | ||
335 | movefactor = P_GetMoveFactor(actor, &friction); | ||
336 | |||
337 | speed = actor->info->speed; | ||
338 | |||
339 | if (friction < ORIG_FRICTION && // sludge | ||
340 | !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2) | ||
341 | * speed) / ORIG_FRICTION_FACTOR)) | ||
342 | speed = 1; // always give the monster a little bit of speed | ||
343 | |||
344 | tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]); | ||
345 | tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]); | ||
346 | |||
347 | try_ok = P_TryMove(actor, tryx, tryy, dropoff); | ||
348 | |||
349 | // killough 10/98: | ||
350 | // Let normal momentum carry them, instead of steptoeing them across ice. | ||
351 | |||
352 | if (try_ok && friction > ORIG_FRICTION) | ||
353 | { | ||
354 | actor->x = origx; | ||
355 | actor->y = origy; | ||
356 | movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4; | ||
357 | actor->momx += FixedMul(deltax, movefactor); | ||
358 | actor->momy += FixedMul(deltay, movefactor); | ||
359 | } | ||
360 | |||
361 | if (!try_ok) | ||
362 | { // open any specials | ||
363 | int good; | ||
364 | |||
365 | if (actor->flags & MF_FLOAT && floatok) | ||
366 | { | ||
367 | if (actor->z < tmfloorz) // must adjust height | ||
368 | actor->z += FLOATSPEED; | ||
369 | else | ||
370 | actor->z -= FLOATSPEED; | ||
371 | |||
372 | actor->flags |= MF_INFLOAT; | ||
373 | |||
374 | return true; | ||
375 | } | ||
376 | |||
377 | if (!numspechit) | ||
378 | return false; | ||
379 | |||
380 | actor->movedir = DI_NODIR; | ||
381 | |||
382 | /* if the special is not a door that can be opened, return false | ||
383 | * | ||
384 | * killough 8/9/98: this is what caused monsters to get stuck in | ||
385 | * doortracks, because it thought that the monster freed itself | ||
386 | * by opening a door, even if it was moving towards the doortrack, | ||
387 | * and not the door itself. | ||
388 | * | ||
389 | * killough 9/9/98: If a line blocking the monster is activated, | ||
390 | * return true 90% of the time. If a line blocking the monster is | ||
391 | * not activated, but some other line is, return false 90% of the | ||
392 | * time. A bit of randomness is needed to ensure it's free from | ||
393 | * lockups, but for most cases, it returns the correct result. | ||
394 | * | ||
395 | * Do NOT simply return false 1/4th of the time (causes monsters to | ||
396 | * back out when they shouldn't, and creates secondary stickiness). | ||
397 | */ | ||
398 | |||
399 | for (good = false; numspechit--; ) | ||
400 | if (P_UseSpecialLine(actor, spechit[numspechit], 0)) | ||
401 | good |= spechit[numspechit] == blockline ? 1 : 2; | ||
402 | |||
403 | /* cph - compatibility maze here | ||
404 | * Boom v2.01 and orig. Doom return "good" | ||
405 | * Boom v2.02 and LxDoom return good && (P_Random(pr_trywalk)&3) | ||
406 | * MBF plays even more games | ||
407 | */ | ||
408 | if (!good || comp[comp_doorstuck]) return good; | ||
409 | if (!mbf_features) | ||
410 | return (P_Random(pr_trywalk)&3); /* jff 8/13/98 */ | ||
411 | else /* finally, MBF code */ | ||
412 | return ((P_Random(pr_opendoor) >= 230) ^ (good & 1)); | ||
413 | } | ||
414 | else | ||
415 | actor->flags &= ~MF_INFLOAT; | ||
416 | |||
417 | /* killough 11/98: fall more slowly, under gravity, if felldown==true */ | ||
418 | if (!(actor->flags & MF_FLOAT) && | ||
419 | (!felldown || !mbf_features)) | ||
420 | actor->z = actor->floorz; | ||
421 | |||
422 | return true; | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | * P_SmartMove | ||
427 | * | ||
428 | * killough 9/12/98: Same as P_Move, except smarter | ||
429 | */ | ||
430 | |||
431 | static boolean P_SmartMove(mobj_t *actor) | ||
432 | { | ||
433 | mobj_t *target = actor->target; | ||
434 | int on_lift, dropoff = false, under_damage; | ||
435 | |||
436 | /* killough 9/12/98: Stay on a lift if target is on one */ | ||
437 | on_lift = !comp[comp_staylift] | ||
438 | && target && target->health > 0 | ||
439 | && target->subsector->sector->tag==actor->subsector->sector->tag && | ||
440 | P_IsOnLift(actor); | ||
441 | |||
442 | under_damage = monster_avoid_hazards && P_IsUnderDamage(actor); | ||
443 | |||
444 | // killough 10/98: allow dogs to drop off of taller ledges sometimes. | ||
445 | // dropoff==1 means always allow it, dropoff==2 means only up to 128 high, | ||
446 | // and only if the target is immediately on the other side of the line. | ||
447 | |||
448 | #ifdef DOGS | ||
449 | if (actor->type == MT_DOGS && target && dog_jumping && | ||
450 | !((target->flags ^ actor->flags) & MF_FRIEND) && | ||
451 | P_AproxDistance(actor->x - target->x, | ||
452 | actor->y - target->y) < FRACUNIT*144 && | ||
453 | P_Random(pr_dropoff) < 235) | ||
454 | dropoff = 2; | ||
455 | #endif | ||
456 | |||
457 | if (!P_Move(actor, dropoff)) | ||
458 | return false; | ||
459 | |||
460 | // killough 9/9/98: avoid crushing ceilings or other damaging areas | ||
461 | if ( | ||
462 | (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift | ||
463 | !P_IsOnLift(actor)) | ||
464 | || | ||
465 | (monster_avoid_hazards && !under_damage && // Get away from damage | ||
466 | (under_damage = P_IsUnderDamage(actor)) && | ||
467 | (under_damage < 0 || P_Random(pr_avoidcrush) < 200)) | ||
468 | ) | ||
469 | actor->movedir = DI_NODIR; // avoid the area (most of the time anyway) | ||
470 | |||
471 | return true; | ||
472 | } | ||
473 | |||
474 | // | ||
475 | // TryWalk | ||
476 | // Attempts to move actor on | ||
477 | // in its current (ob->moveangle) direction. | ||
478 | // If blocked by either a wall or an actor | ||
479 | // returns FALSE | ||
480 | // If move is either clear or blocked only by a door, | ||
481 | // returns TRUE and sets... | ||
482 | // If a door is in the way, | ||
483 | // an OpenDoor call is made to start it opening. | ||
484 | // | ||
485 | |||
486 | boolean P_TryWalk(mobj_t *actor) | ||
487 | { | ||
488 | if (!P_SmartMove(actor)) | ||
489 | return false; | ||
490 | actor->movecount = P_Random(pr_trywalk)&15; | ||
491 | return true; | ||
492 | } | ||
493 | |||
494 | // | ||
495 | // P_DoNewChaseDir | ||
496 | // | ||
497 | // killough 9/8/98: | ||
498 | // | ||
499 | // Most of P_NewChaseDir(), except for what | ||
500 | // determines the new direction to take | ||
501 | // | ||
502 | |||
503 | static void P_DoNewChaseDir(mobj_t *actor, fixed_t deltax, fixed_t deltay) | ||
504 | { | ||
505 | dirtype_t xdir, ydir, tdir; | ||
506 | dirtype_t olddir = actor->movedir; | ||
507 | dirtype_t turnaround = olddir; | ||
508 | |||
509 | if (turnaround != DI_NODIR) // find reverse direction | ||
510 | turnaround ^= 4; | ||
511 | |||
512 | xdir = | ||
513 | deltax > 10*FRACUNIT ? DI_EAST : | ||
514 | deltax < -10*FRACUNIT ? DI_WEST : DI_NODIR; | ||
515 | |||
516 | ydir = | ||
517 | deltay < -10*FRACUNIT ? DI_SOUTH : | ||
518 | deltay > 10*FRACUNIT ? DI_NORTH : DI_NODIR; | ||
519 | |||
520 | // try direct route | ||
521 | if (xdir != DI_NODIR && ydir != DI_NODIR && turnaround != | ||
522 | (actor->movedir = deltay < 0 ? deltax > 0 ? DI_SOUTHEAST : DI_SOUTHWEST : | ||
523 | deltax > 0 ? DI_NORTHEAST : DI_NORTHWEST) && P_TryWalk(actor)) | ||
524 | return; | ||
525 | |||
526 | // try other directions | ||
527 | if (P_Random(pr_newchase) > 200 || abs(deltay)>abs(deltax)) | ||
528 | tdir = xdir, xdir = ydir, ydir = tdir; | ||
529 | |||
530 | if ((xdir == turnaround ? xdir = DI_NODIR : xdir) != DI_NODIR && | ||
531 | (actor->movedir = xdir, P_TryWalk(actor))) | ||
532 | return; // either moved forward or attacked | ||
533 | |||
534 | if ((ydir == turnaround ? ydir = DI_NODIR : ydir) != DI_NODIR && | ||
535 | (actor->movedir = ydir, P_TryWalk(actor))) | ||
536 | return; | ||
537 | |||
538 | // there is no direct path to the player, so pick another direction. | ||
539 | if (olddir != DI_NODIR && (actor->movedir = olddir, P_TryWalk(actor))) | ||
540 | return; | ||
541 | |||
542 | // randomly determine direction of search | ||
543 | if (P_Random(pr_newchasedir) & 1) | ||
544 | { | ||
545 | for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) | ||
546 | if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) | ||
547 | return; | ||
548 | } | ||
549 | else | ||
550 | for (tdir = DI_SOUTHEAST; tdir != (unsigned)(DI_EAST-1); tdir--) | ||
551 | if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) | ||
552 | return; | ||
553 | |||
554 | if ((actor->movedir = turnaround) != DI_NODIR && !P_TryWalk(actor)) | ||
555 | actor->movedir = DI_NODIR; | ||
556 | } | ||
557 | |||
558 | // | ||
559 | // killough 11/98: | ||
560 | // | ||
561 | // Monsters try to move away from tall dropoffs. | ||
562 | // | ||
563 | // In Doom, they were never allowed to hang over dropoffs, | ||
564 | // and would remain stuck if involuntarily forced over one. | ||
565 | // This logic, combined with p_map.c (P_TryMove), allows | ||
566 | // monsters to free themselves without making them tend to | ||
567 | // hang over dropoffs. | ||
568 | |||
569 | static fixed_t dropoff_deltax, dropoff_deltay, floorz; | ||
570 | |||
571 | static boolean PIT_AvoidDropoff(line_t *line) | ||
572 | { | ||
573 | if (line->backsector && // Ignore one-sided linedefs | ||
574 | tmbbox[BOXRIGHT] > line->bbox[BOXLEFT] && | ||
575 | tmbbox[BOXLEFT] < line->bbox[BOXRIGHT] && | ||
576 | tmbbox[BOXTOP] > line->bbox[BOXBOTTOM] && // Linedef must be contacted | ||
577 | tmbbox[BOXBOTTOM] < line->bbox[BOXTOP] && | ||
578 | P_BoxOnLineSide(tmbbox, line) == -1) | ||
579 | { | ||
580 | fixed_t front = line->frontsector->floorheight; | ||
581 | fixed_t back = line->backsector->floorheight; | ||
582 | angle_t angle; | ||
583 | |||
584 | // The monster must contact one of the two floors, | ||
585 | // and the other must be a tall dropoff (more than 24). | ||
586 | |||
587 | if (back == floorz && front < floorz - FRACUNIT*24) | ||
588 | angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff | ||
589 | else | ||
590 | if (front == floorz && back < floorz - FRACUNIT*24) | ||
591 | angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff | ||
592 | else | ||
593 | return true; | ||
594 | |||
595 | // Move away from dropoff at a standard speed. | ||
596 | // Multiple contacted linedefs are cumulative (e.g. hanging over corner) | ||
597 | dropoff_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32; | ||
598 | dropoff_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32; | ||
599 | } | ||
600 | return true; | ||
601 | } | ||
602 | |||
603 | // | ||
604 | // Driver for above | ||
605 | // | ||
606 | |||
607 | static fixed_t P_AvoidDropoff(mobj_t *actor) | ||
608 | { | ||
609 | int yh=((tmbbox[BOXTOP] = actor->y+actor->radius)-bmaporgy)>>MAPBLOCKSHIFT; | ||
610 | int yl=((tmbbox[BOXBOTTOM]= actor->y-actor->radius)-bmaporgy)>>MAPBLOCKSHIFT; | ||
611 | int xh=((tmbbox[BOXRIGHT] = actor->x+actor->radius)-bmaporgx)>>MAPBLOCKSHIFT; | ||
612 | int xl=((tmbbox[BOXLEFT] = actor->x-actor->radius)-bmaporgx)>>MAPBLOCKSHIFT; | ||
613 | int bx, by; | ||
614 | |||
615 | floorz = actor->z; // remember floor height | ||
616 | |||
617 | dropoff_deltax = dropoff_deltay = 0; | ||
618 | |||
619 | // check lines | ||
620 | |||
621 | validcount++; | ||
622 | for (bx=xl ; bx<=xh ; bx++) | ||
623 | for (by=yl ; by<=yh ; by++) | ||
624 | P_BlockLinesIterator(bx, by, PIT_AvoidDropoff); // all contacted lines | ||
625 | |||
626 | return dropoff_deltax | dropoff_deltay; // Non-zero if movement prescribed | ||
627 | } | ||
628 | |||
629 | // | ||
630 | // P_NewChaseDir | ||
631 | // | ||
632 | // killough 9/8/98: Split into two functions | ||
633 | // | ||
634 | |||
635 | static void P_NewChaseDir(mobj_t *actor) | ||
636 | { | ||
637 | mobj_t *target = actor->target; | ||
638 | fixed_t deltax = target->x - actor->x; | ||
639 | fixed_t deltay = target->y - actor->y; | ||
640 | |||
641 | // killough 8/8/98: sometimes move away from target, keeping distance | ||
642 | // | ||
643 | // 1) Stay a certain distance away from a friend, to avoid being in their way | ||
644 | // 2) Take advantage over an enemy without missiles, by keeping distance | ||
645 | |||
646 | actor->strafecount = 0; | ||
647 | |||
648 | if (mbf_features) { | ||
649 | if (actor->floorz - actor->dropoffz > FRACUNIT*24 && | ||
650 | actor->z <= actor->floorz && | ||
651 | !(actor->flags & (MF_DROPOFF|MF_FLOAT)) && | ||
652 | !comp[comp_dropoff] && | ||
653 | P_AvoidDropoff(actor)) /* Move away from dropoff */ | ||
654 | { | ||
655 | P_DoNewChaseDir(actor, dropoff_deltax, dropoff_deltay); | ||
656 | |||
657 | // If moving away from dropoff, set movecount to 1 so that | ||
658 | // small steps are taken to get monster away from dropoff. | ||
659 | |||
660 | actor->movecount = 1; | ||
661 | return; | ||
662 | } | ||
663 | else | ||
664 | { | ||
665 | fixed_t dist = P_AproxDistance(deltax, deltay); | ||
666 | |||
667 | // Move away from friends when too close, except | ||
668 | // in certain situations (e.g. a crowded lift) | ||
669 | |||
670 | if (actor->flags & target->flags & MF_FRIEND && | ||
671 | distfriend << FRACBITS > dist && | ||
672 | !P_IsOnLift(target) && !P_IsUnderDamage(actor)) | ||
673 | { | ||
674 | deltax = -deltax, deltay = -deltay; | ||
675 | } else | ||
676 | if (target->health > 0 && (actor->flags ^ target->flags) & MF_FRIEND) | ||
677 | { // Live enemy target | ||
678 | if (monster_backing && | ||
679 | actor->info->missilestate && actor->type != MT_SKULL && | ||
680 | ((!target->info->missilestate && dist < MELEERANGE*2) || | ||
681 | (target->player && dist < MELEERANGE*3 && | ||
682 | (target->player->readyweapon == wp_fist || | ||
683 | target->player->readyweapon == wp_chainsaw)))) | ||
684 | { // Back away from melee attacker | ||
685 | actor->strafecount = P_Random(pr_enemystrafe) & 15; | ||
686 | deltax = -deltax, deltay = -deltay; | ||
687 | } | ||
688 | } | ||
689 | } | ||
690 | } | ||
691 | |||
692 | P_DoNewChaseDir(actor, deltax, deltay); | ||
693 | |||
694 | // If strafing, set movecount to strafecount so that old Doom | ||
695 | // logic still works the same, except in the strafing part | ||
696 | |||
697 | if (actor->strafecount) | ||
698 | actor->movecount = actor->strafecount; | ||
699 | } | ||
700 | |||
701 | // | ||
702 | // P_IsVisible | ||
703 | // | ||
704 | // killough 9/9/98: whether a target is visible to a monster | ||
705 | // | ||
706 | |||
707 | static boolean P_IsVisible(mobj_t *actor, mobj_t *mo, boolean allaround) | ||
708 | { | ||
709 | if (!allaround) | ||
710 | { | ||
711 | angle_t an = R_PointToAngle2(actor->x, actor->y, | ||
712 | mo->x, mo->y) - actor->angle; | ||
713 | if (an > ANG90 && an < ANG270 && | ||
714 | P_AproxDistance(mo->x-actor->x, mo->y-actor->y) > MELEERANGE) | ||
715 | return false; | ||
716 | } | ||
717 | return P_CheckSight(actor, mo); | ||
718 | } | ||
719 | |||
720 | // | ||
721 | // PIT_FindTarget | ||
722 | // | ||
723 | // killough 9/5/98 | ||
724 | // | ||
725 | // Finds monster targets for other monsters | ||
726 | // | ||
727 | |||
728 | static int current_allaround; | ||
729 | |||
730 | static boolean PIT_FindTarget(mobj_t *mo) | ||
731 | { | ||
732 | mobj_t *actor = current_actor; | ||
733 | |||
734 | if (!((mo->flags ^ actor->flags) & MF_FRIEND && // Invalid target | ||
735 | mo->health > 0 && (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL))) | ||
736 | return true; | ||
737 | |||
738 | // If the monster is already engaged in a one-on-one attack | ||
739 | // with a healthy friend, don't attack around 60% the time | ||
740 | { | ||
741 | const mobj_t *targ = mo->target; | ||
742 | if (targ && targ->target == mo && | ||
743 | P_Random(pr_skiptarget) > 100 && | ||
744 | (targ->flags ^ mo->flags) & MF_FRIEND && | ||
745 | targ->health*2 >= targ->info->spawnhealth) | ||
746 | return true; | ||
747 | } | ||
748 | |||
749 | if (!P_IsVisible(actor, mo, current_allaround)) | ||
750 | return true; | ||
751 | |||
752 | P_SetTarget(&actor->lastenemy, actor->target); // Remember previous target | ||
753 | P_SetTarget(&actor->target, mo); // Found target | ||
754 | |||
755 | // Move the selected monster to the end of its associated | ||
756 | // list, so that it gets searched last next time. | ||
757 | |||
758 | { | ||
759 | thinker_t *cap = &thinkerclasscap[mo->flags & MF_FRIEND ? | ||
760 | th_friends : th_enemies]; | ||
761 | (mo->thinker.cprev->cnext = mo->thinker.cnext)->cprev = mo->thinker.cprev; | ||
762 | (mo->thinker.cprev = cap->cprev)->cnext = &mo->thinker; | ||
763 | (mo->thinker.cnext = cap)->cprev = &mo->thinker; | ||
764 | } | ||
765 | |||
766 | return false; | ||
767 | } | ||
768 | |||
769 | // | ||
770 | // P_LookForPlayers | ||
771 | // If allaround is false, only look 180 degrees in front. | ||
772 | // Returns true if a player is targeted. | ||
773 | // | ||
774 | |||
775 | static boolean P_LookForPlayers(mobj_t *actor, boolean allaround) | ||
776 | { | ||
777 | player_t *player; | ||
778 | int stop, stopc, c; | ||
779 | |||
780 | if (actor->flags & MF_FRIEND) | ||
781 | { // killough 9/9/98: friendly monsters go about players differently | ||
782 | int anyone; | ||
783 | |||
784 | #if 0 | ||
785 | if (!allaround) // If you want friendly monsters not to awaken unprovoked | ||
786 | return false; | ||
787 | #endif | ||
788 | |||
789 | // Go back to a player, no matter whether it's visible or not | ||
790 | for (anyone=0; anyone<=1; anyone++) | ||
791 | for (c=0; c<MAXPLAYERS; c++) | ||
792 | if (playeringame[c] && players[c].playerstate==PST_LIVE && | ||
793 | (anyone || P_IsVisible(actor, players[c].mo, allaround))) | ||
794 | { | ||
795 | P_SetTarget(&actor->target, players[c].mo); | ||
796 | |||
797 | // killough 12/98: | ||
798 | // get out of refiring loop, to avoid hitting player accidentally | ||
799 | |||
800 | if (actor->info->missilestate) | ||
801 | { | ||
802 | P_SetMobjState(actor, actor->info->seestate); | ||
803 | actor->flags &= ~MF_JUSTHIT; | ||
804 | } | ||
805 | |||
806 | return true; | ||
807 | } | ||
808 | |||
809 | return false; | ||
810 | } | ||
811 | |||
812 | // Change mask of 3 to (MAXPLAYERS-1) -- killough 2/15/98: | ||
813 | stop = (actor->lastlook-1)&(MAXPLAYERS-1); | ||
814 | |||
815 | c = 0; | ||
816 | |||
817 | stopc = !mbf_features && | ||
818 | !demo_compatibility && monsters_remember ? | ||
819 | MAXPLAYERS : 2; // killough 9/9/98 | ||
820 | |||
821 | for (;; actor->lastlook = (actor->lastlook+1)&(MAXPLAYERS-1)) | ||
822 | { | ||
823 | if (!playeringame[actor->lastlook]) | ||
824 | continue; | ||
825 | |||
826 | // killough 2/15/98, 9/9/98: | ||
827 | if (c++ == stopc || actor->lastlook == stop) // done looking | ||
828 | return false; | ||
829 | |||
830 | player = &players[actor->lastlook]; | ||
831 | |||
832 | if (player->health <= 0) | ||
833 | continue; // dead | ||
834 | |||
835 | if (!P_IsVisible(actor, player->mo, allaround)) | ||
836 | continue; | ||
837 | |||
838 | P_SetTarget(&actor->target, player->mo); | ||
839 | |||
840 | /* killough 9/9/98: give monsters a threshold towards getting players | ||
841 | * (we don't want it to be too easy for a player with dogs :) | ||
842 | */ | ||
843 | if (!comp[comp_pursuit]) | ||
844 | actor->threshold = 60; | ||
845 | |||
846 | return true; | ||
847 | } | ||
848 | } | ||
849 | |||
850 | // | ||
851 | // Friendly monsters, by Lee Killough 7/18/98 | ||
852 | // | ||
853 | // Friendly monsters go after other monsters first, but | ||
854 | // also return to owner if they cannot find any targets. | ||
855 | // A marine's best friend :) killough 7/18/98, 9/98 | ||
856 | // | ||
857 | |||
858 | static boolean P_LookForMonsters(mobj_t *actor, boolean allaround) | ||
859 | { | ||
860 | thinker_t *cap, *th; | ||
861 | |||
862 | if (demo_compatibility) | ||
863 | return false; | ||
864 | |||
865 | if (actor->lastenemy && actor->lastenemy->health > 0 && monsters_remember && | ||
866 | !(actor->lastenemy->flags & actor->flags & MF_FRIEND)) // not friends | ||
867 | { | ||
868 | P_SetTarget(&actor->target, actor->lastenemy); | ||
869 | P_SetTarget(&actor->lastenemy, NULL); | ||
870 | return true; | ||
871 | } | ||
872 | |||
873 | /* Old demos do not support monster-seeking bots */ | ||
874 | if (!mbf_features) | ||
875 | return false; | ||
876 | |||
877 | // Search the threaded list corresponding to this object's potential targets | ||
878 | cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_enemies : th_friends]; | ||
879 | |||
880 | // Search for new enemy | ||
881 | |||
882 | if (cap->cnext != cap) // Empty list? bail out early | ||
883 | { | ||
884 | int x = (actor->x - bmaporgx)>>MAPBLOCKSHIFT; | ||
885 | int y = (actor->y - bmaporgy)>>MAPBLOCKSHIFT; | ||
886 | int d; | ||
887 | |||
888 | current_actor = actor; | ||
889 | current_allaround = allaround; | ||
890 | |||
891 | // Search first in the immediate vicinity. | ||
892 | |||
893 | if (!P_BlockThingsIterator(x, y, PIT_FindTarget)) | ||
894 | return true; | ||
895 | |||
896 | for (d=1; d<5; d++) | ||
897 | { | ||
898 | int i = 1 - d; | ||
899 | do | ||
900 | if (!P_BlockThingsIterator(x+i, y-d, PIT_FindTarget) || | ||
901 | !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget)) | ||
902 | return true; | ||
903 | while (++i < d); | ||
904 | do | ||
905 | if (!P_BlockThingsIterator(x-d, y+i, PIT_FindTarget) || | ||
906 | !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget)) | ||
907 | return true; | ||
908 | while (--i + d >= 0); | ||
909 | } | ||
910 | |||
911 | { // Random number of monsters, to prevent patterns from forming | ||
912 | int n = (P_Random(pr_friends) & 31) + 15; | ||
913 | |||
914 | for (th = cap->cnext; th != cap; th = th->cnext) | ||
915 | if (--n < 0) | ||
916 | { | ||
917 | // Only a subset of the monsters were searched. Move all of | ||
918 | // the ones which were searched so far, to the end of the list. | ||
919 | |||
920 | (cap->cnext->cprev = cap->cprev)->cnext = cap->cnext; | ||
921 | (cap->cprev = th->cprev)->cnext = cap; | ||
922 | (th->cprev = cap)->cnext = th; | ||
923 | break; | ||
924 | } | ||
925 | else | ||
926 | if (!PIT_FindTarget((mobj_t *) th)) // If target sighted | ||
927 | return true; | ||
928 | } | ||
929 | } | ||
930 | |||
931 | return false; // No monster found | ||
932 | } | ||
933 | |||
934 | // | ||
935 | // P_LookForTargets | ||
936 | // | ||
937 | // killough 9/5/98: look for targets to go after, depending on kind of monster | ||
938 | // | ||
939 | |||
940 | static boolean P_LookForTargets(mobj_t *actor, int allaround) | ||
941 | { | ||
942 | return actor->flags & MF_FRIEND ? | ||
943 | P_LookForMonsters(actor, allaround) || P_LookForPlayers (actor, allaround): | ||
944 | P_LookForPlayers (actor, allaround) || P_LookForMonsters(actor, allaround); | ||
945 | } | ||
946 | |||
947 | // | ||
948 | // P_HelpFriend | ||
949 | // | ||
950 | // killough 9/8/98: Help friends in danger of dying | ||
951 | // | ||
952 | |||
953 | static boolean P_HelpFriend(mobj_t *actor) | ||
954 | { | ||
955 | thinker_t *cap, *th; | ||
956 | |||
957 | // If less than 33% health, self-preservation rules | ||
958 | if (actor->health*3 < actor->info->spawnhealth) | ||
959 | return false; | ||
960 | |||
961 | current_actor = actor; | ||
962 | current_allaround = true; | ||
963 | |||
964 | // Possibly help a friend under 50% health | ||
965 | cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_friends : th_enemies]; | ||
966 | |||
967 | for (th = cap->cnext; th != cap; th = th->cnext) | ||
968 | if (((mobj_t *) th)->health*2 >= ((mobj_t *) th)->info->spawnhealth) | ||
969 | { | ||
970 | if (P_Random(pr_helpfriend) < 180) | ||
971 | break; | ||
972 | } | ||
973 | else | ||
974 | if (((mobj_t *) th)->flags & MF_JUSTHIT && | ||
975 | ((mobj_t *) th)->target && | ||
976 | ((mobj_t *) th)->target != actor->target && | ||
977 | !PIT_FindTarget(((mobj_t *) th)->target)) | ||
978 | { | ||
979 | // Ignore any attacking monsters, while searching for friend | ||
980 | actor->threshold = BASETHRESHOLD; | ||
981 | return true; | ||
982 | } | ||
983 | |||
984 | return false; | ||
985 | } | ||
986 | |||
987 | // | ||
988 | // A_KeenDie | ||
989 | // DOOM II special, map 32. | ||
990 | // Uses special tag 666. | ||
991 | // | ||
992 | void A_KeenDie(mobj_t* mo) | ||
993 | { | ||
994 | thinker_t *th; | ||
995 | line_t junk; | ||
996 | |||
997 | A_Fall(mo); | ||
998 | |||
999 | // scan the remaining thinkers to see if all Keens are dead | ||
1000 | |||
1001 | for (th = thinkercap.next ; th != &thinkercap ; th=th->next) | ||
1002 | if (th->function == P_MobjThinker) | ||
1003 | { | ||
1004 | mobj_t *mo2 = (mobj_t *) th; | ||
1005 | if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) | ||
1006 | return; // other Keen not dead | ||
1007 | } | ||
1008 | |||
1009 | junk.tag = 666; | ||
1010 | EV_DoDoor(&junk,p_open); | ||
1011 | } | ||
1012 | |||
1013 | |||
1014 | // | ||
1015 | // ACTION ROUTINES | ||
1016 | // | ||
1017 | |||
1018 | // | ||
1019 | // A_Look | ||
1020 | // Stay in state until a player is sighted. | ||
1021 | // | ||
1022 | |||
1023 | void A_Look(mobj_t *actor) | ||
1024 | { | ||
1025 | mobj_t *targ = actor->subsector->sector->soundtarget; | ||
1026 | actor->threshold = 0; // any shot will wake up | ||
1027 | |||
1028 | /* killough 7/18/98: | ||
1029 | * Friendly monsters go after other monsters first, but | ||
1030 | * also return to player, without attacking them, if they | ||
1031 | * cannot find any targets. A marine's best friend :) | ||
1032 | */ | ||
1033 | actor->pursuecount = 0; | ||
1034 | |||
1035 | if (!(actor->flags & MF_FRIEND && P_LookForTargets(actor, false)) && | ||
1036 | !((targ = actor->subsector->sector->soundtarget) && | ||
1037 | targ->flags & MF_SHOOTABLE && | ||
1038 | (P_SetTarget(&actor->target, targ), | ||
1039 | !(actor->flags & MF_AMBUSH) || P_CheckSight(actor, targ))) && | ||
1040 | (actor->flags & MF_FRIEND || !P_LookForTargets(actor, false))) | ||
1041 | return; | ||
1042 | |||
1043 | // go into chase state | ||
1044 | |||
1045 | if (actor->info->seesound) | ||
1046 | { | ||
1047 | int sound; | ||
1048 | switch (actor->info->seesound) | ||
1049 | { | ||
1050 | case sfx_posit1: | ||
1051 | case sfx_posit2: | ||
1052 | case sfx_posit3: | ||
1053 | sound = sfx_posit1+P_Random(pr_see)%3; | ||
1054 | break; | ||
1055 | |||
1056 | case sfx_bgsit1: | ||
1057 | case sfx_bgsit2: | ||
1058 | sound = sfx_bgsit1+P_Random(pr_see)%2; | ||
1059 | break; | ||
1060 | |||
1061 | default: | ||
1062 | sound = actor->info->seesound; | ||
1063 | break; | ||
1064 | } | ||
1065 | if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) | ||
1066 | S_StartSound(NULL, sound); // full volume | ||
1067 | else | ||
1068 | S_StartSound(actor, sound); | ||
1069 | } | ||
1070 | P_SetMobjState(actor, actor->info->seestate); | ||
1071 | } | ||
1072 | |||
1073 | // | ||
1074 | // A_KeepChasing | ||
1075 | // | ||
1076 | // killough 10/98: | ||
1077 | // Allows monsters to continue movement while attacking | ||
1078 | // | ||
1079 | |||
1080 | void A_KeepChasing(mobj_t *actor) | ||
1081 | { | ||
1082 | if (actor->movecount) | ||
1083 | { | ||
1084 | actor->movecount--; | ||
1085 | if (actor->strafecount) | ||
1086 | actor->strafecount--; | ||
1087 | P_SmartMove(actor); | ||
1088 | } | ||
1089 | } | ||
1090 | |||
1091 | // | ||
1092 | // A_Chase | ||
1093 | // Actor has a melee attack, | ||
1094 | // so it tries to close as fast as possible | ||
1095 | // | ||
1096 | |||
1097 | void A_Chase(mobj_t *actor) | ||
1098 | { | ||
1099 | if (actor->reactiontime) | ||
1100 | actor->reactiontime--; | ||
1101 | |||
1102 | if (actor->threshold) { /* modify target threshold */ | ||
1103 | if (!actor->target || actor->target->health <= 0) | ||
1104 | actor->threshold = 0; | ||
1105 | else | ||
1106 | actor->threshold--; | ||
1107 | } | ||
1108 | |||
1109 | /* turn towards movement direction if not there yet | ||
1110 | * killough 9/7/98: keep facing towards target if strafing or backing out | ||
1111 | */ | ||
1112 | |||
1113 | if (actor->strafecount) | ||
1114 | A_FaceTarget(actor); | ||
1115 | else if (actor->movedir < 8) | ||
1116 | { | ||
1117 | int delta = (actor->angle &= (7<<29)) - (actor->movedir << 29); | ||
1118 | if (delta > 0) | ||
1119 | actor->angle -= ANG90/2; | ||
1120 | else | ||
1121 | if (delta < 0) | ||
1122 | actor->angle += ANG90/2; | ||
1123 | } | ||
1124 | |||
1125 | if (!actor->target || !(actor->target->flags&MF_SHOOTABLE)) | ||
1126 | { | ||
1127 | if (!P_LookForTargets(actor,true)) // look for a new target | ||
1128 | P_SetMobjState(actor, actor->info->spawnstate); // no new target | ||
1129 | return; | ||
1130 | } | ||
1131 | |||
1132 | // do not attack twice in a row | ||
1133 | if (actor->flags & MF_JUSTATTACKED) | ||
1134 | { | ||
1135 | actor->flags &= ~MF_JUSTATTACKED; | ||
1136 | if (gameskill != sk_nightmare && !fastparm) | ||
1137 | P_NewChaseDir(actor); | ||
1138 | return; | ||
1139 | } | ||
1140 | |||
1141 | // check for melee attack | ||
1142 | if (actor->info->meleestate && P_CheckMeleeRange(actor)) | ||
1143 | { | ||
1144 | if (actor->info->attacksound) | ||
1145 | S_StartSound(actor, actor->info->attacksound); | ||
1146 | P_SetMobjState(actor, actor->info->meleestate); | ||
1147 | /* killough 8/98: remember an attack | ||
1148 | * cph - DEMOSYNC? */ | ||
1149 | if (!actor->info->missilestate) | ||
1150 | actor->flags |= MF_JUSTHIT; | ||
1151 | return; | ||
1152 | } | ||
1153 | |||
1154 | // check for missile attack | ||
1155 | if (actor->info->missilestate) | ||
1156 | if (!(gameskill < sk_nightmare && !fastparm && actor->movecount)) | ||
1157 | if (P_CheckMissileRange(actor)) | ||
1158 | { | ||
1159 | P_SetMobjState(actor, actor->info->missilestate); | ||
1160 | actor->flags |= MF_JUSTATTACKED; | ||
1161 | return; | ||
1162 | } | ||
1163 | |||
1164 | if (!actor->threshold) { | ||
1165 | if (!mbf_features) | ||
1166 | { /* killough 9/9/98: for backward demo compatibility */ | ||
1167 | if (netgame && !P_CheckSight(actor, actor->target) && | ||
1168 | P_LookForPlayers(actor, true)) | ||
1169 | return; | ||
1170 | } | ||
1171 | /* killough 7/18/98, 9/9/98: new monster AI */ | ||
1172 | else if (help_friends && P_HelpFriend(actor)) | ||
1173 | return; /* killough 9/8/98: Help friends in need */ | ||
1174 | /* Look for new targets if current one is bad or is out of view */ | ||
1175 | else if (actor->pursuecount) | ||
1176 | actor->pursuecount--; | ||
1177 | else { | ||
1178 | /* Our pursuit time has expired. We're going to think about | ||
1179 | * changing targets */ | ||
1180 | actor->pursuecount = BASETHRESHOLD; | ||
1181 | |||
1182 | /* Unless (we have a live target | ||
1183 | * and it's not friendly | ||
1184 | * and we can see it) | ||
1185 | * try to find a new one; return if sucessful */ | ||
1186 | |||
1187 | if (!(actor->target && actor->target->health > 0 && | ||
1188 | ((comp[comp_pursuit] && !netgame) || | ||
1189 | (((actor->target->flags ^ actor->flags) & MF_FRIEND || | ||
1190 | (!(actor->flags & MF_FRIEND) && monster_infighting)) && | ||
1191 | P_CheckSight(actor, actor->target)))) | ||
1192 | && P_LookForTargets(actor, true)) | ||
1193 | return; | ||
1194 | |||
1195 | /* (Current target was good, or no new target was found.) | ||
1196 | * | ||
1197 | * If monster is a missile-less friend, give up pursuit and | ||
1198 | * return to player, if no attacks have occurred recently. | ||
1199 | */ | ||
1200 | |||
1201 | if (!actor->info->missilestate && actor->flags & MF_FRIEND) { | ||
1202 | if (actor->flags & MF_JUSTHIT) /* if recent action, */ | ||
1203 | actor->flags &= ~MF_JUSTHIT; /* keep fighting */ | ||
1204 | else if (P_LookForPlayers(actor, true)) /* else return to player */ | ||
1205 | return; | ||
1206 | } | ||
1207 | } | ||
1208 | } | ||
1209 | |||
1210 | if (actor->strafecount) | ||
1211 | actor->strafecount--; | ||
1212 | |||
1213 | // chase towards player | ||
1214 | if (--actor->movecount<0 || !P_SmartMove(actor)) | ||
1215 | P_NewChaseDir(actor); | ||
1216 | |||
1217 | // make active sound | ||
1218 | if (actor->info->activesound && P_Random(pr_see)<3) | ||
1219 | S_StartSound(actor, actor->info->activesound); | ||
1220 | } | ||
1221 | |||
1222 | // | ||
1223 | // A_FaceTarget | ||
1224 | // | ||
1225 | void A_FaceTarget(mobj_t *actor) | ||
1226 | { | ||
1227 | if (!actor->target) | ||
1228 | return; | ||
1229 | actor->flags &= ~MF_AMBUSH; | ||
1230 | actor->angle = R_PointToAngle2(actor->x, actor->y, | ||
1231 | actor->target->x, actor->target->y); | ||
1232 | if (actor->target->flags & MF_SHADOW) | ||
1233 | { // killough 5/5/98: remove dependence on order of evaluation: | ||
1234 | int t = P_Random(pr_facetarget); | ||
1235 | actor->angle += (t-P_Random(pr_facetarget))<<21; | ||
1236 | } | ||
1237 | } | ||
1238 | |||
1239 | // | ||
1240 | // A_PosAttack | ||
1241 | // | ||
1242 | |||
1243 | void A_PosAttack(mobj_t *actor) | ||
1244 | { | ||
1245 | int angle, damage, slope, t; | ||
1246 | |||
1247 | if (!actor->target) | ||
1248 | return; | ||
1249 | A_FaceTarget(actor); | ||
1250 | angle = actor->angle; | ||
1251 | slope = P_AimLineAttack(actor, angle, MISSILERANGE, 0); /* killough 8/2/98 */ | ||
1252 | S_StartSound(actor, sfx_pistol); | ||
1253 | |||
1254 | // killough 5/5/98: remove dependence on order of evaluation: | ||
1255 | t = P_Random(pr_posattack); | ||
1256 | angle += (t - P_Random(pr_posattack))<<20; | ||
1257 | damage = (P_Random(pr_posattack)%5 + 1)*3; | ||
1258 | P_LineAttack(actor, angle, MISSILERANGE, slope, damage); | ||
1259 | } | ||
1260 | |||
1261 | void A_SPosAttack(mobj_t* actor) | ||
1262 | { | ||
1263 | int i, bangle, slope; | ||
1264 | |||
1265 | if (!actor->target) | ||
1266 | return; | ||
1267 | S_StartSound(actor, sfx_shotgn); | ||
1268 | A_FaceTarget(actor); | ||
1269 | bangle = actor->angle; | ||
1270 | slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ | ||
1271 | for (i=0; i<3; i++) | ||
1272 | { // killough 5/5/98: remove dependence on order of evaluation: | ||
1273 | int t = P_Random(pr_sposattack); | ||
1274 | int angle = bangle + ((t - P_Random(pr_sposattack))<<20); | ||
1275 | int damage = ((P_Random(pr_sposattack)%5)+1)*3; | ||
1276 | P_LineAttack(actor, angle, MISSILERANGE, slope, damage); | ||
1277 | } | ||
1278 | } | ||
1279 | |||
1280 | void A_CPosAttack(mobj_t *actor) | ||
1281 | { | ||
1282 | int angle, bangle, damage, slope, t; | ||
1283 | |||
1284 | if (!actor->target) | ||
1285 | return; | ||
1286 | S_StartSound(actor, sfx_shotgn); | ||
1287 | A_FaceTarget(actor); | ||
1288 | bangle = actor->angle; | ||
1289 | slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ | ||
1290 | |||
1291 | // killough 5/5/98: remove dependence on order of evaluation: | ||
1292 | t = P_Random(pr_cposattack); | ||
1293 | angle = bangle + ((t - P_Random(pr_cposattack))<<20); | ||
1294 | damage = ((P_Random(pr_cposattack)%5)+1)*3; | ||
1295 | P_LineAttack(actor, angle, MISSILERANGE, slope, damage); | ||
1296 | } | ||
1297 | |||
1298 | void A_CPosRefire(mobj_t *actor) | ||
1299 | { | ||
1300 | // keep firing unless target got out of sight | ||
1301 | A_FaceTarget(actor); | ||
1302 | |||
1303 | /* killough 12/98: Stop firing if a friend has gotten in the way */ | ||
1304 | if (P_HitFriend(actor)) | ||
1305 | goto stop; | ||
1306 | |||
1307 | /* killough 11/98: prevent refiring on friends continuously */ | ||
1308 | if (P_Random(pr_cposrefire) < 40) { | ||
1309 | if (actor->target && actor->flags & actor->target->flags & MF_FRIEND) | ||
1310 | goto stop; | ||
1311 | else | ||
1312 | return; | ||
1313 | } | ||
1314 | |||
1315 | if (!actor->target || actor->target->health <= 0 | ||
1316 | || !P_CheckSight(actor, actor->target)) | ||
1317 | stop: P_SetMobjState(actor, actor->info->seestate); | ||
1318 | } | ||
1319 | |||
1320 | void A_SpidRefire(mobj_t* actor) | ||
1321 | { | ||
1322 | // keep firing unless target got out of sight | ||
1323 | A_FaceTarget(actor); | ||
1324 | |||
1325 | /* killough 12/98: Stop firing if a friend has gotten in the way */ | ||
1326 | if (P_HitFriend(actor)) | ||
1327 | goto stop; | ||
1328 | |||
1329 | if (P_Random(pr_spidrefire) < 10) | ||
1330 | return; | ||
1331 | |||
1332 | // killough 11/98: prevent refiring on friends continuously | ||
1333 | if (!actor->target || actor->target->health <= 0 | ||
1334 | || actor->flags & actor->target->flags & MF_FRIEND | ||
1335 | || !P_CheckSight(actor, actor->target)) | ||
1336 | stop: P_SetMobjState(actor, actor->info->seestate); | ||
1337 | } | ||
1338 | |||
1339 | void A_BspiAttack(mobj_t *actor) | ||
1340 | { | ||
1341 | if (!actor->target) | ||
1342 | return; | ||
1343 | A_FaceTarget(actor); | ||
1344 | P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); // launch a missile | ||
1345 | } | ||
1346 | |||
1347 | // | ||
1348 | // A_TroopAttack | ||
1349 | // | ||
1350 | |||
1351 | void A_TroopAttack(mobj_t *actor) | ||
1352 | { | ||
1353 | if (!actor->target) | ||
1354 | return; | ||
1355 | A_FaceTarget(actor); | ||
1356 | if (P_CheckMeleeRange(actor)) | ||
1357 | { | ||
1358 | int damage; | ||
1359 | S_StartSound(actor, sfx_claw); | ||
1360 | damage = (P_Random(pr_troopattack)%8+1)*3; | ||
1361 | P_DamageMobj(actor->target, actor, actor, damage); | ||
1362 | return; | ||
1363 | } | ||
1364 | P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); // launch a missile | ||
1365 | } | ||
1366 | |||
1367 | void A_SargAttack(mobj_t *actor) | ||
1368 | { | ||
1369 | if (!actor->target) | ||
1370 | return; | ||
1371 | A_FaceTarget(actor); | ||
1372 | if (P_CheckMeleeRange(actor)) | ||
1373 | { | ||
1374 | int damage = ((P_Random(pr_sargattack)%10)+1)*4; | ||
1375 | P_DamageMobj(actor->target, actor, actor, damage); | ||
1376 | } | ||
1377 | } | ||
1378 | |||
1379 | void A_HeadAttack(mobj_t *actor) | ||
1380 | { | ||
1381 | if (!actor->target) | ||
1382 | return; | ||
1383 | A_FaceTarget (actor); | ||
1384 | if (P_CheckMeleeRange(actor)) | ||
1385 | { | ||
1386 | int damage = (P_Random(pr_headattack)%6+1)*10; | ||
1387 | P_DamageMobj(actor->target, actor, actor, damage); | ||
1388 | return; | ||
1389 | } | ||
1390 | P_SpawnMissile(actor, actor->target, MT_HEADSHOT); // launch a missile | ||
1391 | } | ||
1392 | |||
1393 | void A_CyberAttack(mobj_t *actor) | ||
1394 | { | ||
1395 | if (!actor->target) | ||
1396 | return; | ||
1397 | A_FaceTarget(actor); | ||
1398 | P_SpawnMissile(actor, actor->target, MT_ROCKET); | ||
1399 | } | ||
1400 | |||
1401 | void A_BruisAttack(mobj_t *actor) | ||
1402 | { | ||
1403 | if (!actor->target) | ||
1404 | return; | ||
1405 | if (P_CheckMeleeRange(actor)) | ||
1406 | { | ||
1407 | int damage; | ||
1408 | S_StartSound(actor, sfx_claw); | ||
1409 | damage = (P_Random(pr_bruisattack)%8+1)*10; | ||
1410 | P_DamageMobj(actor->target, actor, actor, damage); | ||
1411 | return; | ||
1412 | } | ||
1413 | P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); // launch a missile | ||
1414 | } | ||
1415 | |||
1416 | // | ||
1417 | // A_SkelMissile | ||
1418 | // | ||
1419 | |||
1420 | void A_SkelMissile(mobj_t *actor) | ||
1421 | { | ||
1422 | mobj_t *mo; | ||
1423 | |||
1424 | if (!actor->target) | ||
1425 | return; | ||
1426 | |||
1427 | A_FaceTarget (actor); | ||
1428 | actor->z += 16*FRACUNIT; // so missile spawns higher | ||
1429 | mo = P_SpawnMissile (actor, actor->target, MT_TRACER); | ||
1430 | actor->z -= 16*FRACUNIT; // back to normal | ||
1431 | |||
1432 | mo->x += mo->momx; | ||
1433 | mo->y += mo->momy; | ||
1434 | P_SetTarget(&mo->tracer, actor->target); | ||
1435 | } | ||
1436 | |||
1437 | int TRACEANGLE = 0xc000000; | ||
1438 | |||
1439 | void A_Tracer(mobj_t *actor) | ||
1440 | { | ||
1441 | angle_t exact; | ||
1442 | fixed_t dist; | ||
1443 | fixed_t slope; | ||
1444 | mobj_t *dest; | ||
1445 | mobj_t *th; | ||
1446 | |||
1447 | /* killough 1/18/98: this is why some missiles do not have smoke | ||
1448 | * and some do. Also, internal demos start at random gametics, thus | ||
1449 | * the bug in which revenants cause internal demos to go out of sync. | ||
1450 | * | ||
1451 | * killough 3/6/98: fix revenant internal demo bug by subtracting | ||
1452 | * levelstarttic from gametic. | ||
1453 | * | ||
1454 | * killough 9/29/98: use new "basetic" so that demos stay in sync | ||
1455 | * during pauses and menu activations, while retaining old demo sync. | ||
1456 | * | ||
1457 | * leveltime would have been better to use to start with in Doom, but | ||
1458 | * since old demos were recorded using gametic, we must stick with it, | ||
1459 | * and improvise around it (using leveltime causes desync across levels). | ||
1460 | */ | ||
1461 | |||
1462 | if ((gametic-basetic) & 3) | ||
1463 | return; | ||
1464 | |||
1465 | // spawn a puff of smoke behind the rocket | ||
1466 | P_SpawnPuff(actor->x, actor->y, actor->z); | ||
1467 | |||
1468 | th = P_SpawnMobj (actor->x-actor->momx, | ||
1469 | actor->y-actor->momy, | ||
1470 | actor->z, MT_SMOKE); | ||
1471 | |||
1472 | th->momz = FRACUNIT; | ||
1473 | th->tics -= P_Random(pr_tracer) & 3; | ||
1474 | if (th->tics < 1) | ||
1475 | th->tics = 1; | ||
1476 | |||
1477 | // adjust direction | ||
1478 | dest = actor->tracer; | ||
1479 | |||
1480 | if (!dest || dest->health <= 0) | ||
1481 | return; | ||
1482 | |||
1483 | // change angle | ||
1484 | exact = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y); | ||
1485 | |||
1486 | if (exact != actor->angle) { | ||
1487 | if (exact - actor->angle > 0x80000000) | ||
1488 | { | ||
1489 | actor->angle -= TRACEANGLE; | ||
1490 | if (exact - actor->angle < 0x80000000) | ||
1491 | actor->angle = exact; | ||
1492 | } | ||
1493 | else | ||
1494 | { | ||
1495 | actor->angle += TRACEANGLE; | ||
1496 | if (exact - actor->angle > 0x80000000) | ||
1497 | actor->angle = exact; | ||
1498 | } | ||
1499 | } | ||
1500 | |||
1501 | exact = actor->angle>>ANGLETOFINESHIFT; | ||
1502 | actor->momx = FixedMul(actor->info->speed, finecosine[exact]); | ||
1503 | actor->momy = FixedMul(actor->info->speed, finesine[exact]); | ||
1504 | |||
1505 | // change slope | ||
1506 | dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); | ||
1507 | |||
1508 | dist = dist / actor->info->speed; | ||
1509 | |||
1510 | if (dist < 1) | ||
1511 | dist = 1; | ||
1512 | |||
1513 | slope = (dest->z+40*FRACUNIT - actor->z) / dist; | ||
1514 | |||
1515 | if (slope < actor->momz) | ||
1516 | actor->momz -= FRACUNIT/8; | ||
1517 | else | ||
1518 | actor->momz += FRACUNIT/8; | ||
1519 | } | ||
1520 | |||
1521 | void A_SkelWhoosh(mobj_t *actor) | ||
1522 | { | ||
1523 | if (!actor->target) | ||
1524 | return; | ||
1525 | A_FaceTarget(actor); | ||
1526 | S_StartSound(actor,sfx_skeswg); | ||
1527 | } | ||
1528 | |||
1529 | void A_SkelFist(mobj_t *actor) | ||
1530 | { | ||
1531 | if (!actor->target) | ||
1532 | return; | ||
1533 | A_FaceTarget(actor); | ||
1534 | if (P_CheckMeleeRange(actor)) | ||
1535 | { | ||
1536 | int damage = ((P_Random(pr_skelfist)%10)+1)*6; | ||
1537 | S_StartSound(actor, sfx_skepch); | ||
1538 | P_DamageMobj(actor->target, actor, actor, damage); | ||
1539 | } | ||
1540 | } | ||
1541 | |||
1542 | // | ||
1543 | // PIT_VileCheck | ||
1544 | // Detect a corpse that could be raised. | ||
1545 | // | ||
1546 | |||
1547 | mobj_t* corpsehit; | ||
1548 | mobj_t* vileobj; | ||
1549 | fixed_t viletryx; | ||
1550 | fixed_t viletryy; | ||
1551 | |||
1552 | boolean PIT_VileCheck(mobj_t *thing) | ||
1553 | { | ||
1554 | int maxdist; | ||
1555 | boolean check; | ||
1556 | |||
1557 | if (!(thing->flags & MF_CORPSE) ) | ||
1558 | return true; // not a monster | ||
1559 | |||
1560 | if (thing->tics != -1) | ||
1561 | return true; // not lying still yet | ||
1562 | |||
1563 | if (thing->info->raisestate == S_NULL) | ||
1564 | return true; // monster doesn't have a raise state | ||
1565 | |||
1566 | maxdist = thing->info->radius + mobjinfo[MT_VILE].radius; | ||
1567 | |||
1568 | if (D_abs(thing->x-viletryx) > maxdist || D_abs(thing->y-viletryy) > maxdist) | ||
1569 | return true; // not actually touching | ||
1570 | |||
1571 | // Check to see if the radius and height are zero. If they are // phares | ||
1572 | // then this is a crushed monster that has been turned into a // | | ||
1573 | // gib. One of the options may be to ignore this guy. // V | ||
1574 | |||
1575 | // Option 1: the original, buggy method, -> ghost (compatibility) | ||
1576 | // Option 2: ressurect the monster, but not as a ghost | ||
1577 | // Option 3: ignore the gib | ||
1578 | |||
1579 | // if (Option3) // ^ | ||
1580 | // if ((thing->height == 0) && (thing->radius == 0)) // | | ||
1581 | // return true; // phares | ||
1582 | |||
1583 | corpsehit = thing; | ||
1584 | corpsehit->momx = corpsehit->momy = 0; | ||
1585 | if (comp[comp_vile]) // phares | ||
1586 | { // | | ||
1587 | corpsehit->height <<= 2; // V | ||
1588 | check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); | ||
1589 | corpsehit->height >>= 2; | ||
1590 | } | ||
1591 | else | ||
1592 | { | ||
1593 | int height,radius; | ||
1594 | |||
1595 | height = corpsehit->height; // save temporarily | ||
1596 | radius = corpsehit->radius; // save temporarily | ||
1597 | corpsehit->height = corpsehit->info->height; | ||
1598 | corpsehit->radius = corpsehit->info->radius; | ||
1599 | corpsehit->flags |= MF_SOLID; | ||
1600 | check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); | ||
1601 | corpsehit->height = height; // restore | ||
1602 | corpsehit->radius = radius; // restore // ^ | ||
1603 | corpsehit->flags &= ~MF_SOLID; | ||
1604 | } // | | ||
1605 | // phares | ||
1606 | if (!check) | ||
1607 | return true; // doesn't fit here | ||
1608 | return false; // got one, so stop checking | ||
1609 | } | ||
1610 | |||
1611 | // | ||
1612 | // A_VileChase | ||
1613 | // Check for ressurecting a body | ||
1614 | // | ||
1615 | |||
1616 | void A_VileChase(mobj_t* actor) | ||
1617 | { | ||
1618 | int xl, xh; | ||
1619 | int yl, yh; | ||
1620 | int bx, by; | ||
1621 | |||
1622 | if (actor->movedir != DI_NODIR) | ||
1623 | { | ||
1624 | // check for corpses to raise | ||
1625 | viletryx = | ||
1626 | actor->x + actor->info->speed*xspeed[actor->movedir]; | ||
1627 | viletryy = | ||
1628 | actor->y + actor->info->speed*yspeed[actor->movedir]; | ||
1629 | |||
1630 | xl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT; | ||
1631 | xh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT; | ||
1632 | yl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT; | ||
1633 | yh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT; | ||
1634 | |||
1635 | vileobj = actor; | ||
1636 | for (bx=xl ; bx<=xh ; bx++) | ||
1637 | { | ||
1638 | for (by=yl ; by<=yh ; by++) | ||
1639 | { | ||
1640 | // Call PIT_VileCheck to check | ||
1641 | // whether object is a corpse | ||
1642 | // that canbe raised. | ||
1643 | if (!P_BlockThingsIterator(bx,by,PIT_VileCheck)) | ||
1644 | { | ||
1645 | mobjinfo_t *info; | ||
1646 | |||
1647 | // got one! | ||
1648 | mobj_t* temp = actor->target; | ||
1649 | actor->target = corpsehit; | ||
1650 | A_FaceTarget(actor); | ||
1651 | actor->target = temp; | ||
1652 | |||
1653 | P_SetMobjState(actor, S_VILE_HEAL1); | ||
1654 | S_StartSound(corpsehit, sfx_slop); | ||
1655 | info = corpsehit->info; | ||
1656 | |||
1657 | P_SetMobjState(corpsehit,info->raisestate); | ||
1658 | |||
1659 | if (comp[comp_vile]) // phares | ||
1660 | corpsehit->height <<= 2; // | | ||
1661 | else // V | ||
1662 | { | ||
1663 | corpsehit->height = info->height; // fix Ghost bug | ||
1664 | corpsehit->radius = info->radius; // fix Ghost bug | ||
1665 | } // phares | ||
1666 | |||
1667 | /* killough 7/18/98: | ||
1668 | * friendliness is transferred from AV to raised corpse | ||
1669 | */ | ||
1670 | corpsehit->flags = | ||
1671 | (info->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); | ||
1672 | |||
1673 | if (!((corpsehit->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) | ||
1674 | totallive++; | ||
1675 | |||
1676 | corpsehit->health = info->spawnhealth; | ||
1677 | P_SetTarget(&corpsehit->target, NULL); // killough 11/98 | ||
1678 | |||
1679 | if (mbf_features) | ||
1680 | { /* kilough 9/9/98 */ | ||
1681 | P_SetTarget(&corpsehit->lastenemy, NULL); | ||
1682 | corpsehit->flags &= ~MF_JUSTHIT; | ||
1683 | } | ||
1684 | |||
1685 | /* killough 8/29/98: add to appropriate thread */ | ||
1686 | P_UpdateThinker(&corpsehit->thinker); | ||
1687 | |||
1688 | return; | ||
1689 | } | ||
1690 | } | ||
1691 | } | ||
1692 | } | ||
1693 | A_Chase(actor); // Return to normal attack. | ||
1694 | } | ||
1695 | |||
1696 | // | ||
1697 | // A_VileStart | ||
1698 | // | ||
1699 | |||
1700 | void A_VileStart(mobj_t *actor) | ||
1701 | { | ||
1702 | S_StartSound(actor, sfx_vilatk); | ||
1703 | } | ||
1704 | |||
1705 | // | ||
1706 | // A_Fire | ||
1707 | // Keep fire in front of player unless out of sight | ||
1708 | // | ||
1709 | |||
1710 | void A_Fire(mobj_t *actor); | ||
1711 | |||
1712 | void A_StartFire(mobj_t *actor) | ||
1713 | { | ||
1714 | S_StartSound(actor,sfx_flamst); | ||
1715 | A_Fire(actor); | ||
1716 | } | ||
1717 | |||
1718 | void A_FireCrackle(mobj_t* actor) | ||
1719 | { | ||
1720 | S_StartSound(actor,sfx_flame); | ||
1721 | A_Fire(actor); | ||
1722 | } | ||
1723 | |||
1724 | void A_Fire(mobj_t *actor) | ||
1725 | { | ||
1726 | unsigned an; | ||
1727 | mobj_t *dest = actor->tracer; | ||
1728 | |||
1729 | if (!dest) | ||
1730 | return; | ||
1731 | |||
1732 | // don't move it if the vile lost sight | ||
1733 | if (!P_CheckSight(actor->target, dest) ) | ||
1734 | return; | ||
1735 | |||
1736 | an = dest->angle >> ANGLETOFINESHIFT; | ||
1737 | |||
1738 | P_UnsetThingPosition(actor); | ||
1739 | actor->x = dest->x + FixedMul(24*FRACUNIT, finecosine[an]); | ||
1740 | actor->y = dest->y + FixedMul(24*FRACUNIT, finesine[an]); | ||
1741 | actor->z = dest->z; | ||
1742 | P_SetThingPosition(actor); | ||
1743 | } | ||
1744 | |||
1745 | // | ||
1746 | // A_VileTarget | ||
1747 | // Spawn the hellfire | ||
1748 | // | ||
1749 | |||
1750 | void A_VileTarget(mobj_t *actor) | ||
1751 | { | ||
1752 | mobj_t *fog; | ||
1753 | |||
1754 | if (!actor->target) | ||
1755 | return; | ||
1756 | |||
1757 | A_FaceTarget(actor); | ||
1758 | |||
1759 | // killough 12/98: fix Vile fog coordinates // CPhipps - compatibility optioned | ||
1760 | fog = P_SpawnMobj(actor->target->x, | ||
1761 | (compatibility_level < lxdoom_1_compatibility) ? actor->target->x : actor->target->y, | ||
1762 | actor->target->z,MT_FIRE); | ||
1763 | |||
1764 | P_SetTarget(&actor->tracer, fog); | ||
1765 | P_SetTarget(&fog->target, actor); | ||
1766 | P_SetTarget(&fog->tracer, actor->target); | ||
1767 | A_Fire(fog); | ||
1768 | } | ||
1769 | |||
1770 | // | ||
1771 | // A_VileAttack | ||
1772 | // | ||
1773 | |||
1774 | void A_VileAttack(mobj_t *actor) | ||
1775 | { | ||
1776 | mobj_t *fire; | ||
1777 | int an; | ||
1778 | |||
1779 | if (!actor->target) | ||
1780 | return; | ||
1781 | |||
1782 | A_FaceTarget(actor); | ||
1783 | |||
1784 | if (!P_CheckSight(actor, actor->target)) | ||
1785 | return; | ||
1786 | |||
1787 | S_StartSound(actor, sfx_barexp); | ||
1788 | P_DamageMobj(actor->target, actor, actor, 20); | ||
1789 | actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; | ||
1790 | |||
1791 | an = actor->angle >> ANGLETOFINESHIFT; | ||
1792 | |||
1793 | fire = actor->tracer; | ||
1794 | |||
1795 | if (!fire) | ||
1796 | return; | ||
1797 | |||
1798 | // move the fire between the vile and the player | ||
1799 | fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); | ||
1800 | fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); | ||
1801 | P_RadiusAttack(fire, actor, 70); | ||
1802 | } | ||
1803 | |||
1804 | // | ||
1805 | // Mancubus attack, | ||
1806 | // firing three missiles (bruisers) | ||
1807 | // in three different directions? | ||
1808 | // Doesn't look like it. | ||
1809 | // | ||
1810 | |||
1811 | #define FATSPREAD (ANG90/8) | ||
1812 | |||
1813 | void A_FatRaise(mobj_t *actor) | ||
1814 | { | ||
1815 | A_FaceTarget(actor); | ||
1816 | S_StartSound(actor, sfx_manatk); | ||
1817 | } | ||
1818 | |||
1819 | void A_FatAttack1(mobj_t *actor) | ||
1820 | { | ||
1821 | mobj_t *mo; | ||
1822 | int an; | ||
1823 | |||
1824 | A_FaceTarget(actor); | ||
1825 | |||
1826 | // Change direction to ... | ||
1827 | actor->angle += FATSPREAD; | ||
1828 | |||
1829 | P_SpawnMissile(actor, actor->target, MT_FATSHOT); | ||
1830 | |||
1831 | mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT); | ||
1832 | mo->angle += FATSPREAD; | ||
1833 | an = mo->angle >> ANGLETOFINESHIFT; | ||
1834 | mo->momx = FixedMul(mo->info->speed, finecosine[an]); | ||
1835 | mo->momy = FixedMul(mo->info->speed, finesine[an]); | ||
1836 | } | ||
1837 | |||
1838 | void A_FatAttack2(mobj_t *actor) | ||
1839 | { | ||
1840 | mobj_t *mo; | ||
1841 | int an; | ||
1842 | |||
1843 | A_FaceTarget(actor); | ||
1844 | // Now here choose opposite deviation. | ||
1845 | actor->angle -= FATSPREAD; | ||
1846 | P_SpawnMissile(actor, actor->target, MT_FATSHOT); | ||
1847 | |||
1848 | mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); | ||
1849 | mo->angle -= FATSPREAD*2; | ||
1850 | an = mo->angle >> ANGLETOFINESHIFT; | ||
1851 | mo->momx = FixedMul(mo->info->speed, finecosine[an]); | ||
1852 | mo->momy = FixedMul(mo->info->speed, finesine[an]); | ||
1853 | } | ||
1854 | |||
1855 | void A_FatAttack3(mobj_t *actor) | ||
1856 | { | ||
1857 | mobj_t *mo; | ||
1858 | int an; | ||
1859 | |||
1860 | A_FaceTarget(actor); | ||
1861 | |||
1862 | mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); | ||
1863 | mo->angle -= FATSPREAD/2; | ||
1864 | an = mo->angle >> ANGLETOFINESHIFT; | ||
1865 | mo->momx = FixedMul(mo->info->speed, finecosine[an]); | ||
1866 | mo->momy = FixedMul(mo->info->speed, finesine[an]); | ||
1867 | |||
1868 | mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); | ||
1869 | mo->angle += FATSPREAD/2; | ||
1870 | an = mo->angle >> ANGLETOFINESHIFT; | ||
1871 | mo->momx = FixedMul(mo->info->speed, finecosine[an]); | ||
1872 | mo->momy = FixedMul(mo->info->speed, finesine[an]); | ||
1873 | } | ||
1874 | |||
1875 | |||
1876 | // | ||
1877 | // SkullAttack | ||
1878 | // Fly at the player like a missile. | ||
1879 | // | ||
1880 | #define SKULLSPEED (20*FRACUNIT) | ||
1881 | |||
1882 | void A_SkullAttack(mobj_t *actor) | ||
1883 | { | ||
1884 | mobj_t *dest; | ||
1885 | angle_t an; | ||
1886 | int dist; | ||
1887 | |||
1888 | if (!actor->target) | ||
1889 | return; | ||
1890 | |||
1891 | dest = actor->target; | ||
1892 | actor->flags |= MF_SKULLFLY; | ||
1893 | |||
1894 | S_StartSound(actor, actor->info->attacksound); | ||
1895 | A_FaceTarget(actor); | ||
1896 | an = actor->angle >> ANGLETOFINESHIFT; | ||
1897 | actor->momx = FixedMul(SKULLSPEED, finecosine[an]); | ||
1898 | actor->momy = FixedMul(SKULLSPEED, finesine[an]); | ||
1899 | dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); | ||
1900 | dist = dist / SKULLSPEED; | ||
1901 | |||
1902 | if (dist < 1) | ||
1903 | dist = 1; | ||
1904 | actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist; | ||
1905 | } | ||
1906 | |||
1907 | // | ||
1908 | // A_PainShootSkull | ||
1909 | // Spawn a lost soul and launch it at the target | ||
1910 | // | ||
1911 | |||
1912 | void A_PainShootSkull(mobj_t *actor, angle_t angle) | ||
1913 | { | ||
1914 | fixed_t x,y,z; | ||
1915 | mobj_t *newmobj; | ||
1916 | angle_t an; | ||
1917 | int prestep; | ||
1918 | |||
1919 | // The original code checked for 20 skulls on the level, // phares | ||
1920 | // and wouldn't spit another one if there were. If not in // phares | ||
1921 | // compatibility mode, we remove the limit. // phares | ||
1922 | // phares | ||
1923 | if (comp[comp_pain]) /* killough 10/98: compatibility-optioned */ | ||
1924 | { | ||
1925 | // count total number of skulls currently on the level | ||
1926 | int count = 0; | ||
1927 | thinker_t *currentthinker; | ||
1928 | for (currentthinker = thinkercap.next; | ||
1929 | currentthinker != &thinkercap; | ||
1930 | currentthinker = currentthinker->next) | ||
1931 | if ((currentthinker->function == P_MobjThinker) | ||
1932 | && ((mobj_t *)currentthinker)->type == MT_SKULL) | ||
1933 | count++; | ||
1934 | if (count > 20) // phares | ||
1935 | return; // phares | ||
1936 | } | ||
1937 | |||
1938 | // okay, there's room for another one | ||
1939 | |||
1940 | an = angle >> ANGLETOFINESHIFT; | ||
1941 | |||
1942 | prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2; | ||
1943 | |||
1944 | x = actor->x + FixedMul(prestep, finecosine[an]); | ||
1945 | y = actor->y + FixedMul(prestep, finesine[an]); | ||
1946 | z = actor->z + 8*FRACUNIT; | ||
1947 | |||
1948 | if (comp[comp_skull]) /* killough 10/98: compatibility-optioned */ | ||
1949 | newmobj = P_SpawnMobj(x, y, z, MT_SKULL); // phares | ||
1950 | else // V | ||
1951 | { | ||
1952 | // Check whether the Lost Soul is being fired through a 1-sided | ||
1953 | // wall or an impassible line, or a "monsters can't cross" line. | ||
1954 | // If it is, then we don't allow the spawn. This is a bug fix, but | ||
1955 | // it should be considered an enhancement, since it may disturb | ||
1956 | // existing demos, so don't do it in compatibility mode. | ||
1957 | |||
1958 | if (Check_Sides(actor,x,y)) | ||
1959 | return; | ||
1960 | |||
1961 | newmobj = P_SpawnMobj(x, y, z, MT_SKULL); | ||
1962 | |||
1963 | // Check to see if the new Lost Soul's z value is above the | ||
1964 | // ceiling of its new sector, or below the floor. If so, kill it. | ||
1965 | |||
1966 | if ((newmobj->z > | ||
1967 | (newmobj->subsector->sector->ceilingheight - newmobj->height)) || | ||
1968 | (newmobj->z < newmobj->subsector->sector->floorheight)) | ||
1969 | { | ||
1970 | // kill it immediately | ||
1971 | P_DamageMobj(newmobj,actor,actor,10000); | ||
1972 | return; // ^ | ||
1973 | } // | | ||
1974 | } // phares | ||
1975 | |||
1976 | /* killough 7/20/98: PEs shoot lost souls with the same friendliness */ | ||
1977 | newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); | ||
1978 | |||
1979 | /* killough 8/29/98: add to appropriate thread */ | ||
1980 | P_UpdateThinker(&newmobj->thinker); | ||
1981 | |||
1982 | // Check for movements. | ||
1983 | // killough 3/15/98: don't jump over dropoffs: | ||
1984 | |||
1985 | if (!P_TryMove(newmobj, newmobj->x, newmobj->y, false)) | ||
1986 | { | ||
1987 | // kill it immediately | ||
1988 | P_DamageMobj(newmobj, actor, actor, 10000); | ||
1989 | return; | ||
1990 | } | ||
1991 | |||
1992 | P_SetTarget(&newmobj->target, actor->target); | ||
1993 | A_SkullAttack(newmobj); | ||
1994 | } | ||
1995 | |||
1996 | // | ||
1997 | // A_PainAttack | ||
1998 | // Spawn a lost soul and launch it at the target | ||
1999 | // | ||
2000 | |||
2001 | void A_PainAttack(mobj_t *actor) | ||
2002 | { | ||
2003 | if (!actor->target) | ||
2004 | return; | ||
2005 | A_FaceTarget(actor); | ||
2006 | A_PainShootSkull(actor, actor->angle); | ||
2007 | } | ||
2008 | |||
2009 | void A_PainDie(mobj_t *actor) | ||
2010 | { | ||
2011 | A_Fall(actor); | ||
2012 | A_PainShootSkull(actor, actor->angle+ANG90); | ||
2013 | A_PainShootSkull(actor, actor->angle+ANG180); | ||
2014 | A_PainShootSkull(actor, actor->angle+ANG270); | ||
2015 | } | ||
2016 | |||
2017 | void A_Scream(mobj_t *actor) | ||
2018 | { | ||
2019 | int sound; | ||
2020 | |||
2021 | switch (actor->info->deathsound) | ||
2022 | { | ||
2023 | case 0: | ||
2024 | return; | ||
2025 | |||
2026 | case sfx_podth1: | ||
2027 | case sfx_podth2: | ||
2028 | case sfx_podth3: | ||
2029 | sound = sfx_podth1 + P_Random(pr_scream)%3; | ||
2030 | break; | ||
2031 | |||
2032 | case sfx_bgdth1: | ||
2033 | case sfx_bgdth2: | ||
2034 | sound = sfx_bgdth1 + P_Random(pr_scream)%2; | ||
2035 | break; | ||
2036 | |||
2037 | default: | ||
2038 | sound = actor->info->deathsound; | ||
2039 | break; | ||
2040 | } | ||
2041 | |||
2042 | // Check for bosses. | ||
2043 | if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) | ||
2044 | S_StartSound(NULL, sound); // full volume | ||
2045 | else | ||
2046 | S_StartSound(actor, sound); | ||
2047 | } | ||
2048 | |||
2049 | void A_XScream(mobj_t *actor) | ||
2050 | { | ||
2051 | S_StartSound(actor, sfx_slop); | ||
2052 | } | ||
2053 | |||
2054 | void A_Pain(mobj_t *actor) | ||
2055 | { | ||
2056 | if (actor->info->painsound) | ||
2057 | S_StartSound(actor, actor->info->painsound); | ||
2058 | } | ||
2059 | |||
2060 | void A_Fall(mobj_t *actor) | ||
2061 | { | ||
2062 | // actor is on ground, it can be walked over | ||
2063 | actor->flags &= ~MF_SOLID; | ||
2064 | } | ||
2065 | |||
2066 | // | ||
2067 | // A_Explode | ||
2068 | // | ||
2069 | void A_Explode(mobj_t *thingy) | ||
2070 | { | ||
2071 | P_RadiusAttack( thingy, thingy->target, 128 ); | ||
2072 | } | ||
2073 | |||
2074 | // | ||
2075 | // A_BossDeath | ||
2076 | // Possibly trigger special effects | ||
2077 | // if on first boss level | ||
2078 | // | ||
2079 | |||
2080 | void A_BossDeath(mobj_t *mo) | ||
2081 | { | ||
2082 | thinker_t *th; | ||
2083 | line_t junk; | ||
2084 | int i; | ||
2085 | |||
2086 | if (gamemode == commercial) | ||
2087 | { | ||
2088 | if (gamemap != 7) | ||
2089 | return; | ||
2090 | |||
2091 | if ((mo->type != MT_FATSO) | ||
2092 | && (mo->type != MT_BABY)) | ||
2093 | return; | ||
2094 | } | ||
2095 | else | ||
2096 | { | ||
2097 | switch(gameepisode) | ||
2098 | { | ||
2099 | case 1: | ||
2100 | if (gamemap != 8) | ||
2101 | return; | ||
2102 | |||
2103 | if (mo->type != MT_BRUISER) | ||
2104 | return; | ||
2105 | break; | ||
2106 | |||
2107 | case 2: | ||
2108 | if (gamemap != 8) | ||
2109 | return; | ||
2110 | |||
2111 | if (mo->type != MT_CYBORG) | ||
2112 | return; | ||
2113 | break; | ||
2114 | |||
2115 | case 3: | ||
2116 | if (gamemap != 8) | ||
2117 | return; | ||
2118 | |||
2119 | if (mo->type != MT_SPIDER) | ||
2120 | return; | ||
2121 | |||
2122 | break; | ||
2123 | |||
2124 | case 4: | ||
2125 | switch(gamemap) | ||
2126 | { | ||
2127 | case 6: | ||
2128 | if (mo->type != MT_CYBORG) | ||
2129 | return; | ||
2130 | break; | ||
2131 | |||
2132 | case 8: | ||
2133 | if (mo->type != MT_SPIDER) | ||
2134 | return; | ||
2135 | break; | ||
2136 | |||
2137 | default: | ||
2138 | return; | ||
2139 | break; | ||
2140 | } | ||
2141 | break; | ||
2142 | |||
2143 | default: | ||
2144 | if (gamemap != 8) | ||
2145 | return; | ||
2146 | break; | ||
2147 | } | ||
2148 | |||
2149 | } | ||
2150 | |||
2151 | // make sure there is a player alive for victory | ||
2152 | for (i=0; i<MAXPLAYERS; i++) | ||
2153 | if (playeringame[i] && players[i].health > 0) | ||
2154 | break; | ||
2155 | |||
2156 | if (i==MAXPLAYERS) | ||
2157 | return; // no one left alive, so do not end game | ||
2158 | |||
2159 | // scan the remaining thinkers to see | ||
2160 | // if all bosses are dead | ||
2161 | for (th = thinkercap.next ; th != &thinkercap ; th=th->next) | ||
2162 | if (th->function == P_MobjThinker) | ||
2163 | { | ||
2164 | mobj_t *mo2 = (mobj_t *) th; | ||
2165 | if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) | ||
2166 | return; // other boss not dead | ||
2167 | } | ||
2168 | |||
2169 | // victory! | ||
2170 | if ( gamemode == commercial) | ||
2171 | { | ||
2172 | if (gamemap == 7) | ||
2173 | { | ||
2174 | if (mo->type == MT_FATSO) | ||
2175 | { | ||
2176 | junk.tag = 666; | ||
2177 | EV_DoFloor(&junk,lowerFloorToLowest); | ||
2178 | return; | ||
2179 | } | ||
2180 | |||
2181 | if (mo->type == MT_BABY) | ||
2182 | { | ||
2183 | junk.tag = 667; | ||
2184 | EV_DoFloor(&junk,raiseToTexture); | ||
2185 | return; | ||
2186 | } | ||
2187 | } | ||
2188 | } | ||
2189 | else | ||
2190 | { | ||
2191 | switch(gameepisode) | ||
2192 | { | ||
2193 | case 1: | ||
2194 | junk.tag = 666; | ||
2195 | EV_DoFloor(&junk, lowerFloorToLowest); | ||
2196 | return; | ||
2197 | break; | ||
2198 | |||
2199 | case 4: | ||
2200 | switch(gamemap) | ||
2201 | { | ||
2202 | case 6: | ||
2203 | junk.tag = 666; | ||
2204 | EV_DoDoor(&junk, blazeOpen); | ||
2205 | return; | ||
2206 | break; | ||
2207 | |||
2208 | case 8: | ||
2209 | junk.tag = 666; | ||
2210 | EV_DoFloor(&junk, lowerFloorToLowest); | ||
2211 | return; | ||
2212 | break; | ||
2213 | } | ||
2214 | } | ||
2215 | } | ||
2216 | G_ExitLevel(); | ||
2217 | } | ||
2218 | |||
2219 | |||
2220 | void A_Hoof (mobj_t* mo) | ||
2221 | { | ||
2222 | S_StartSound(mo, sfx_hoof); | ||
2223 | A_Chase(mo); | ||
2224 | } | ||
2225 | |||
2226 | void A_Metal(mobj_t *mo) | ||
2227 | { | ||
2228 | S_StartSound(mo, sfx_metal); | ||
2229 | A_Chase(mo); | ||
2230 | } | ||
2231 | |||
2232 | void A_BabyMetal(mobj_t *mo) | ||
2233 | { | ||
2234 | S_StartSound(mo, sfx_bspwlk); | ||
2235 | A_Chase(mo); | ||
2236 | } | ||
2237 | |||
2238 | void A_OpenShotgun2(player_t *player, pspdef_t *psp) | ||
2239 | { | ||
2240 | (void)psp; | ||
2241 | S_StartSound(player->mo, sfx_dbopn); | ||
2242 | } | ||
2243 | |||
2244 | void A_LoadShotgun2(player_t *player, pspdef_t *psp) | ||
2245 | { | ||
2246 | (void)psp; | ||
2247 | S_StartSound(player->mo, sfx_dbload); | ||
2248 | } | ||
2249 | |||
2250 | void A_ReFire(player_t *player, pspdef_t *psp); | ||
2251 | |||
2252 | void A_CloseShotgun2(player_t *player, pspdef_t *psp) | ||
2253 | { | ||
2254 | S_StartSound(player->mo, sfx_dbcls); | ||
2255 | A_ReFire(player,psp); | ||
2256 | } | ||
2257 | |||
2258 | // killough 2/7/98: Remove limit on icon landings: | ||
2259 | mobj_t **braintargets; | ||
2260 | int numbraintargets_alloc; | ||
2261 | int numbraintargets; | ||
2262 | |||
2263 | struct brain_s brain; // killough 3/26/98: global state of boss brain | ||
2264 | |||
2265 | // killough 3/26/98: initialize icon landings at level startup, | ||
2266 | // rather than at boss wakeup, to prevent savegame-related crashes | ||
2267 | |||
2268 | void P_SpawnBrainTargets(void) // killough 3/26/98: renamed old function | ||
2269 | { | ||
2270 | thinker_t *thinker; | ||
2271 | |||
2272 | // find all the target spots | ||
2273 | numbraintargets = 0; | ||
2274 | brain.targeton = 0; | ||
2275 | brain.easy = 0; // killough 3/26/98: always init easy to 0 | ||
2276 | |||
2277 | for (thinker = thinkercap.next ; | ||
2278 | thinker != &thinkercap ; | ||
2279 | thinker = thinker->next) | ||
2280 | if (thinker->function == P_MobjThinker) | ||
2281 | { | ||
2282 | mobj_t *m = (mobj_t *) thinker; | ||
2283 | |||
2284 | if (m->type == MT_BOSSTARGET ) | ||
2285 | { // killough 2/7/98: remove limit on icon landings: | ||
2286 | if (numbraintargets >= numbraintargets_alloc) | ||
2287 | braintargets = realloc(braintargets, | ||
2288 | (numbraintargets_alloc = numbraintargets_alloc ? | ||
2289 | numbraintargets_alloc*2 : 32) *sizeof *braintargets); | ||
2290 | braintargets[numbraintargets++] = m; | ||
2291 | } | ||
2292 | } | ||
2293 | } | ||
2294 | |||
2295 | void A_BrainAwake(mobj_t *mo) | ||
2296 | { | ||
2297 | (void)mo; | ||
2298 | S_StartSound(NULL,sfx_bossit); // killough 3/26/98: only generates sound now | ||
2299 | } | ||
2300 | |||
2301 | void A_BrainPain(mobj_t *mo) | ||
2302 | { | ||
2303 | (void)mo; | ||
2304 | S_StartSound(NULL,sfx_bospn); | ||
2305 | } | ||
2306 | |||
2307 | void A_BrainScream(mobj_t *mo) | ||
2308 | { | ||
2309 | int x; | ||
2310 | for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8) | ||
2311 | { | ||
2312 | int y = mo->y - 320*FRACUNIT; | ||
2313 | int z = 128 + P_Random(pr_brainscream)*2*FRACUNIT; | ||
2314 | mobj_t *th = P_SpawnMobj (x,y,z, MT_ROCKET); | ||
2315 | th->momz = P_Random(pr_brainscream)*512; | ||
2316 | P_SetMobjState(th, S_BRAINEXPLODE1); | ||
2317 | th->tics -= P_Random(pr_brainscream)&7; | ||
2318 | if (th->tics < 1) | ||
2319 | th->tics = 1; | ||
2320 | } | ||
2321 | S_StartSound(NULL,sfx_bosdth); | ||
2322 | } | ||
2323 | |||
2324 | void A_BrainExplode(mobj_t *mo) | ||
2325 | { // killough 5/5/98: remove dependence on order of evaluation: | ||
2326 | (void)mo; | ||
2327 | int t = P_Random(pr_brainexp); | ||
2328 | int x = mo->x + (t - P_Random(pr_brainexp))*2048; | ||
2329 | int y = mo->y; | ||
2330 | int z = 128 + P_Random(pr_brainexp)*2*FRACUNIT; | ||
2331 | mobj_t *th = P_SpawnMobj(x,y,z, MT_ROCKET); | ||
2332 | th->momz = P_Random(pr_brainexp)*512; | ||
2333 | P_SetMobjState(th, S_BRAINEXPLODE1); | ||
2334 | th->tics -= P_Random(pr_brainexp)&7; | ||
2335 | if (th->tics < 1) | ||
2336 | th->tics = 1; | ||
2337 | } | ||
2338 | |||
2339 | void A_BrainDie(mobj_t *mo) | ||
2340 | { | ||
2341 | (void)mo; | ||
2342 | G_ExitLevel(); | ||
2343 | } | ||
2344 | |||
2345 | void A_BrainSpit(mobj_t *mo) | ||
2346 | { | ||
2347 | mobj_t *targ, *newmobj; | ||
2348 | |||
2349 | if (!numbraintargets) // killough 4/1/98: ignore if no targets | ||
2350 | return; | ||
2351 | |||
2352 | brain.easy ^= 1; // killough 3/26/98: use brain struct | ||
2353 | if (gameskill <= sk_easy && !brain.easy) | ||
2354 | return; | ||
2355 | |||
2356 | // shoot a cube at current target | ||
2357 | targ = braintargets[brain.targeton++]; // killough 3/26/98: | ||
2358 | brain.targeton %= numbraintargets; // Use brain struct for targets | ||
2359 | |||
2360 | // spawn brain missile | ||
2361 | newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT); | ||
2362 | P_SetTarget(&newmobj->target, targ); | ||
2363 | newmobj->reactiontime = (short)(((targ->y-mo->y)/newmobj->momy)/newmobj->state->tics); | ||
2364 | |||
2365 | // killough 7/18/98: brain friendliness is transferred | ||
2366 | newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); | ||
2367 | |||
2368 | // killough 8/29/98: add to appropriate thread | ||
2369 | P_UpdateThinker(&newmobj->thinker); | ||
2370 | |||
2371 | S_StartSound(NULL, sfx_bospit); | ||
2372 | } | ||
2373 | |||
2374 | void A_SpawnFly(mobj_t *mo); | ||
2375 | |||
2376 | // travelling cube sound | ||
2377 | void A_SpawnSound(mobj_t *mo) | ||
2378 | { | ||
2379 | S_StartSound(mo,sfx_boscub); | ||
2380 | A_SpawnFly(mo); | ||
2381 | } | ||
2382 | |||
2383 | void A_SpawnFly(mobj_t *mo) | ||
2384 | { | ||
2385 | mobj_t *newmobj; | ||
2386 | mobj_t *fog; | ||
2387 | mobj_t *targ; | ||
2388 | int r; | ||
2389 | mobjtype_t type; | ||
2390 | |||
2391 | if (--mo->reactiontime) | ||
2392 | return; // still flying | ||
2393 | |||
2394 | targ = mo->target; | ||
2395 | |||
2396 | // First spawn teleport fog. | ||
2397 | fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); | ||
2398 | S_StartSound(fog, sfx_telept); | ||
2399 | |||
2400 | // Randomly select monster to spawn. | ||
2401 | r = P_Random(pr_spawnfly); | ||
2402 | |||
2403 | // Probability distribution (kind of :), decreasing likelihood. | ||
2404 | if ( r<50 ) | ||
2405 | type = MT_TROOP; | ||
2406 | else if (r<90) | ||
2407 | type = MT_SERGEANT; | ||
2408 | else if (r<120) | ||
2409 | type = MT_SHADOWS; | ||
2410 | else if (r<130) | ||
2411 | type = MT_PAIN; | ||
2412 | else if (r<160) | ||
2413 | type = MT_HEAD; | ||
2414 | else if (r<162) | ||
2415 | type = MT_VILE; | ||
2416 | else if (r<172) | ||
2417 | type = MT_UNDEAD; | ||
2418 | else if (r<192) | ||
2419 | type = MT_BABY; | ||
2420 | else if (r<222) | ||
2421 | type = MT_FATSO; | ||
2422 | else if (r<246) | ||
2423 | type = MT_KNIGHT; | ||
2424 | else | ||
2425 | type = MT_BRUISER; | ||
2426 | |||
2427 | newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type); | ||
2428 | |||
2429 | /* killough 7/18/98: brain friendliness is transferred */ | ||
2430 | newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); | ||
2431 | |||
2432 | /* killough 8/29/98: add to appropriate thread */ | ||
2433 | P_UpdateThinker(&newmobj->thinker); | ||
2434 | |||
2435 | if (P_LookForTargets(newmobj,true)) /* killough 9/4/98 */ | ||
2436 | P_SetMobjState(newmobj, newmobj->info->seestate); | ||
2437 | |||
2438 | // telefrag anything in this spot | ||
2439 | P_TeleportMove(newmobj, newmobj->x, newmobj->y, true); /* killough 8/9/98 */ | ||
2440 | |||
2441 | // remove self (i.e., cube). | ||
2442 | P_RemoveMobj(mo); | ||
2443 | } | ||
2444 | |||
2445 | void A_PlayerScream(mobj_t *mo) | ||
2446 | { | ||
2447 | int sound = sfx_pldeth; // Default death sound. | ||
2448 | if (gamemode != shareware && mo->health < -50) | ||
2449 | sound = sfx_pdiehi; // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING | ||
2450 | S_StartSound(mo, sound); | ||
2451 | } | ||
2452 | |||
2453 | /* cph - MBF-added codepointer functions */ | ||
2454 | |||
2455 | // killough 11/98: kill an object | ||
2456 | void A_Die(mobj_t *actor) | ||
2457 | { | ||
2458 | P_DamageMobj(actor, NULL, NULL, actor->health); | ||
2459 | } | ||
2460 | |||
2461 | // | ||
2462 | // A_Detonate | ||
2463 | // killough 8/9/98: same as A_Explode, except that the damage is variable | ||
2464 | // | ||
2465 | |||
2466 | void A_Detonate(mobj_t *mo) | ||
2467 | { | ||
2468 | P_RadiusAttack(mo, mo->target, mo->info->damage); | ||
2469 | } | ||
2470 | |||
2471 | // | ||
2472 | // killough 9/98: a mushroom explosion effect, sorta :) | ||
2473 | // Original idea: Linguica | ||
2474 | // | ||
2475 | |||
2476 | void A_Mushroom(mobj_t *actor) | ||
2477 | { | ||
2478 | int i, j, n = actor->info->damage; | ||
2479 | |||
2480 | A_Explode(actor); // First make normal explosion | ||
2481 | |||
2482 | // Now launch mushroom cloud | ||
2483 | for (i = -n; i <= n; i += 8) | ||
2484 | for (j = -n; j <= n; j += 8) | ||
2485 | { | ||
2486 | mobj_t target = *actor, *mo; | ||
2487 | target.x += i << FRACBITS; // Aim in many directions from source | ||
2488 | target.y += j << FRACBITS; | ||
2489 | target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high | ||
2490 | mo = P_SpawnMissile(actor, &target, MT_FATSHOT); // Launch fireball | ||
2491 | mo->momx >>= 1; | ||
2492 | mo->momy >>= 1; // Slow it down a bit | ||
2493 | mo->momz >>= 1; | ||
2494 | mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity | ||
2495 | } | ||
2496 | } | ||
2497 | |||
2498 | // | ||
2499 | // killough 11/98 | ||
2500 | // | ||
2501 | // The following were inspired by Len Pitre | ||
2502 | // | ||
2503 | // A small set of highly-sought-after code pointers | ||
2504 | // | ||
2505 | |||
2506 | void A_Spawn(mobj_t *mo) | ||
2507 | { | ||
2508 | if (mo->state->misc1) | ||
2509 | { | ||
2510 | /* mobj_t *newmobj = */ | ||
2511 | P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z, | ||
2512 | mo->state->misc1 - 1); | ||
2513 | /* CPhipps - no friendlyness (yet) | ||
2514 | newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); | ||
2515 | */ | ||
2516 | } | ||
2517 | } | ||
2518 | |||
2519 | void A_Turn(mobj_t *mo) | ||
2520 | { | ||
2521 | mo->angle += (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); | ||
2522 | } | ||
2523 | |||
2524 | void A_Face(mobj_t *mo) | ||
2525 | { | ||
2526 | mo->angle = (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); | ||
2527 | } | ||
2528 | |||
2529 | void A_Scratch(mobj_t *mo) | ||
2530 | { | ||
2531 | mo->target && (A_FaceTarget(mo), P_CheckMeleeRange(mo)) ? | ||
2532 | mo->state->misc2 ? S_StartSound(mo, mo->state->misc2) : (void) 0, | ||
2533 | P_DamageMobj(mo->target, mo, mo, mo->state->misc1) : (void) 0; | ||
2534 | } | ||
2535 | |||
2536 | void A_PlaySound(mobj_t *mo) | ||
2537 | { | ||
2538 | S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1); | ||
2539 | } | ||
2540 | |||
2541 | void A_RandomJump(mobj_t *mo) | ||
2542 | { | ||
2543 | if (P_Random(pr_randomjump) < mo->state->misc2) | ||
2544 | P_SetMobjState(mo, mo->state->misc1); | ||
2545 | } | ||
2546 | |||
2547 | // | ||
2548 | // This allows linedef effects to be activated inside deh frames. | ||
2549 | // | ||
2550 | |||
2551 | void A_LineEffect(mobj_t *mo) | ||
2552 | { | ||
2553 | static line_t junk; | ||
2554 | player_t player; | ||
2555 | player_t *oldplayer; | ||
2556 | junk = *lines; | ||
2557 | oldplayer = mo->player; | ||
2558 | mo->player = &player; | ||
2559 | player.health = 100; | ||
2560 | junk.special = (short)mo->state->misc1; | ||
2561 | if (!junk.special) | ||
2562 | return; | ||
2563 | junk.tag = (short)mo->state->misc2; | ||
2564 | if (!P_UseSpecialLine(mo, &junk, 0)) | ||
2565 | P_CrossSpecialLine(&junk, 0, mo); | ||
2566 | mo->state->misc1 = junk.special; | ||
2567 | mo->player = oldplayer; | ||
2568 | } | ||
2569 | |||
2570 | /***** Start of new functions for Andy Baker's stealth monsters ******/ | ||
2571 | |||
2572 | void P_BecomeVisible(mobj_t* actor) | ||
2573 | { | ||
2574 | actor->invisible = false; | ||
2575 | actor->flags &= ~MF_TRANSLUCBITS; | ||
2576 | }; | ||
2577 | |||
2578 | void P_IncreaseVisibility(mobj_t* actor) | ||
2579 | { | ||
2580 | if (actor->invisible) { | ||
2581 | actor->invisible = false; | ||
2582 | actor->flags |= MF_TRANSLUC25; | ||
2583 | } else switch (actor->flags & MF_TRANSLUCBITS) { | ||
2584 | case MF_TRANSLUC25: | ||
2585 | actor->flags &= ~MF_TRANSLUCBITS; | ||
2586 | actor->flags |= MF_TRANSLUC50; | ||
2587 | break; | ||
2588 | case MF_TRANSLUC50: | ||
2589 | actor->flags &= ~MF_TRANSLUCBITS; | ||
2590 | actor->flags |= MF_TRANSLUC25; | ||
2591 | actor->flags |= MF_TRANSLUC50; | ||
2592 | break; | ||
2593 | case MF_TRANSLUC75: | ||
2594 | actor->flags &= ~MF_TRANSLUCBITS; | ||
2595 | break; | ||
2596 | } | ||
2597 | } | ||
2598 | |||
2599 | void P_DecreaseVisibility(mobj_t* actor) | ||
2600 | { | ||
2601 | if (actor->invisible) | ||
2602 | return; // already invisible | ||
2603 | |||
2604 | switch (actor->flags & MF_TRANSLUCBITS) { | ||
2605 | case 0: | ||
2606 | actor->flags &= ~MF_TRANSLUCBITS; | ||
2607 | actor->flags |= MF_TRANSLUC75; | ||
2608 | break; | ||
2609 | case MF_TRANSLUC75: | ||
2610 | actor->flags &= ~MF_TRANSLUCBITS; | ||
2611 | actor->flags |= MF_TRANSLUC50; | ||
2612 | break; | ||
2613 | case MF_TRANSLUC50: | ||
2614 | actor->flags &= ~MF_TRANSLUCBITS; | ||
2615 | actor->flags |= MF_TRANSLUC25; | ||
2616 | break; | ||
2617 | case MF_TRANSLUC25: | ||
2618 | actor->flags &= ~MF_TRANSLUCBITS; | ||
2619 | actor->invisible = true; | ||
2620 | } | ||
2621 | } | ||
2622 | /***** End of new functions for Andy Baker's stealth monsters ******/ | ||
2623 | |||