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