1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file p_enemy.c
12 /// \brief Enemy thinking, AI
13 /// Action Pointer Functions that are associated with states/frames
14
15 #include "doomdef.h"
16 #include "g_game.h"
17 #include "p_local.h"
18 #include "p_setup.h"
19 #include "r_main.h"
20 #include "r_state.h"
21 #include "s_sound.h"
22 #include "m_random.h"
23 #include "m_misc.h"
24 #include "r_skins.h"
25 #include "i_video.h"
26 #include "z_zone.h"
27 #include "lua_hook.h"
28
29 #ifdef HW3SOUND
30 #include "hardware/hw3sound.h"
31 #endif
32
33 boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor);
34
35 player_t *stplyr;
36 INT32 var1;
37 INT32 var2;
38 INT32 modulothing;
39
40 //
41 // P_NewChaseDir related LUT.
42 //
43 static dirtype_t opposite[] =
44 {
45 DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,
46 DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR
47 };
48
49 static dirtype_t diags[] =
50 {
51 DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST
52 };
53
54 //Real Prototypes to A_*
55 void A_Fall(mobj_t *actor);
56 void A_Look(mobj_t *actor);
57 void A_Chase(mobj_t *actor);
58 void A_FaceStabChase(mobj_t *actor);
59 void A_FaceStabRev(mobj_t *actor);
60 void A_FaceStabHurl(mobj_t *actor);
61 void A_FaceStabMiss(mobj_t *actor);
62 void A_StatueBurst(mobj_t *actor);
63 void A_JetJawRoam(mobj_t *actor);
64 void A_JetJawChomp(mobj_t *actor);
65 void A_PointyThink(mobj_t *actor);
66 void A_CheckBuddy(mobj_t *actor);
67 void A_HoodFire(mobj_t *actor);
68 void A_HoodThink(mobj_t *actor);
69 void A_HoodFall(mobj_t *actor);
70 void A_ArrowBonks(mobj_t *actor);
71 void A_SnailerThink(mobj_t *actor);
72 void A_SharpChase(mobj_t *actor);
73 void A_SharpSpin(mobj_t *actor);
74 void A_SharpDecel(mobj_t *actor);
75 void A_CrushstaceanWalk(mobj_t *actor);
76 void A_CrushstaceanPunch(mobj_t *actor);
77 void A_CrushclawAim(mobj_t *actor);
78 void A_CrushclawLaunch(mobj_t *actor);
79 void A_VultureVtol(mobj_t *actor);
80 void A_VultureCheck(mobj_t *actor);
81 void A_VultureHover(mobj_t *actor);
82 void A_VultureBlast(mobj_t *actor);
83 void A_VultureFly(mobj_t *actor);
84 void A_SkimChase(mobj_t *actor);
85 void A_FaceTarget(mobj_t *actor);
86 void A_FaceTracer(mobj_t *actor);
87 void A_LobShot(mobj_t *actor);
88 void A_FireShot(mobj_t *actor);
89 void A_SuperFireShot(mobj_t *actor);
90 void A_BossFireShot(mobj_t *actor);
91 void A_Boss7FireMissiles(mobj_t *actor);
92 void A_Boss1Laser(mobj_t *actor);
93 void A_FocusTarget(mobj_t *actor);
94 void A_Boss4Reverse(mobj_t *actor);
95 void A_Boss4SpeedUp(mobj_t *actor);
96 void A_Boss4Raise(mobj_t *actor);
97 void A_SkullAttack(mobj_t *actor);
98 void A_BossZoom(mobj_t *actor);
99 void A_BossScream(mobj_t *actor);
100 void A_Scream(mobj_t *actor);
101 void A_Pain(mobj_t *actor);
102 void A_1upThinker(mobj_t *actor);
103 void A_MonitorPop(mobj_t *actor);
104 void A_GoldMonitorPop(mobj_t *actor);
105 void A_GoldMonitorRestore(mobj_t *actor);
106 void A_GoldMonitorSparkle(mobj_t *actor);
107 void A_Explode(mobj_t *actor);
108 void A_BossDeath(mobj_t *actor);
109 void A_CustomPower(mobj_t *actor);
110 void A_GiveWeapon(mobj_t *actor);
111 void A_RingBox(mobj_t *actor);
112 void A_Invincibility(mobj_t *actor);
113 void A_SuperSneakers(mobj_t *actor);
114 void A_AwardScore(mobj_t *actor);
115 void A_ExtraLife(mobj_t *actor);
116 void A_GiveShield(mobj_t *actor);
117 void A_GravityBox(mobj_t *actor);
118 void A_ScoreRise(mobj_t *actor);
119 void A_BunnyHop(mobj_t *actor);
120 void A_BubbleSpawn(mobj_t *actor);
121 void A_FanBubbleSpawn(mobj_t *actor);
122 void A_BubbleRise(mobj_t *actor);
123 void A_BubbleCheck(mobj_t *actor);
124 void A_AttractChase(mobj_t *actor);
125 void A_DropMine(mobj_t *actor);
126 void A_FishJump(mobj_t *actor);
127 void A_ThrownRing(mobj_t *actor);
128 void A_SetSolidSteam(mobj_t *actor);
129 void A_UnsetSolidSteam(mobj_t *actor);
130 void A_SignSpin(mobj_t *actor);
131 void A_SignPlayer(mobj_t *actor);
132 void A_OverlayThink(mobj_t *actor);
133 void A_JetChase(mobj_t *actor);
134 void A_JetbThink(mobj_t *actor);
135 void A_JetgShoot(mobj_t *actor);
136 void A_JetgThink(mobj_t *actor);
137 void A_ShootBullet(mobj_t *actor);
138 void A_MinusDigging(mobj_t *actor);
139 void A_MinusPopup(mobj_t *actor);
140 void A_MinusCheck(mobj_t *actor);
141 void A_ChickenCheck(mobj_t *actor);
142 void A_MouseThink(mobj_t *actor);
143 void A_DetonChase(mobj_t *actor);
144 void A_CapeChase(mobj_t *actor);
145 void A_RotateSpikeBall(mobj_t *actor);
146 void A_SlingAppear(mobj_t *actor);
147 void A_UnidusBall(mobj_t *actor);
148 void A_RockSpawn(mobj_t *actor);
149 void A_SetFuse(mobj_t *actor);
150 void A_CrawlaCommanderThink(mobj_t *actor);
151 void A_RingExplode(mobj_t *actor);
152 void A_OldRingExplode(mobj_t *actor);
153 void A_MixUp(mobj_t *actor);
154 void A_RecyclePowers(mobj_t *actor);
155 void A_Boss2TakeDamage(mobj_t *actor);
156 void A_Boss7Chase(mobj_t *actor);
157 void A_GoopSplat(mobj_t *actor);
158 void A_Boss2PogoSFX(mobj_t *actor);
159 void A_Boss2PogoTarget(mobj_t *actor);
160 void A_EggmanBox(mobj_t *actor);
161 void A_TurretFire(mobj_t *actor);
162 void A_SuperTurretFire(mobj_t *actor);
163 void A_TurretStop(mobj_t *actor);
164 void A_SparkFollow(mobj_t *actor);
165 void A_BuzzFly(mobj_t *actor);
166 void A_GuardChase(mobj_t *actor);
167 void A_EggShield(mobj_t *actor);
168 void A_SetReactionTime(mobj_t *actor);
169 void A_Boss1Spikeballs(mobj_t *actor);
170 void A_Boss3TakeDamage(mobj_t *actor);
171 void A_Boss3Path(mobj_t *actor);
172 void A_Boss3ShockThink(mobj_t *actor);
173 void A_LinedefExecute(mobj_t *actor);
174 void A_PlaySeeSound(mobj_t *actor);
175 void A_PlayAttackSound(mobj_t *actor);
176 void A_PlayActiveSound(mobj_t *actor);
177 void A_SmokeTrailer(mobj_t *actor);
178 void A_SpawnObjectAbsolute(mobj_t *actor);
179 void A_SpawnObjectRelative(mobj_t *actor);
180 void A_ChangeAngleRelative(mobj_t *actor);
181 void A_ChangeAngleAbsolute(mobj_t *actor);
182 void A_RollAngle(mobj_t *actor);
183 void A_ChangeRollAngleRelative(mobj_t *actor);
184 void A_ChangeRollAngleAbsolute(mobj_t *actor);
185 void A_PlaySound(mobj_t *actor);
186 void A_FindTarget(mobj_t *actor);
187 void A_FindTracer(mobj_t *actor);
188 void A_SetTics(mobj_t *actor);
189 void A_SetRandomTics(mobj_t *actor);
190 void A_ChangeColorRelative(mobj_t *actor);
191 void A_ChangeColorAbsolute(mobj_t *actor);
192 void A_Dye(mobj_t *actor);
193 void A_MoveRelative(mobj_t *actor);
194 void A_MoveAbsolute(mobj_t *actor);
195 void A_Thrust(mobj_t *actor);
196 void A_ZThrust(mobj_t *actor);
197 void A_SetTargetsTarget(mobj_t *actor);
198 void A_SetObjectFlags(mobj_t *actor);
199 void A_SetObjectFlags2(mobj_t *actor);
200 void A_RandomState(mobj_t *actor);
201 void A_RandomStateRange(mobj_t *actor);
202 void A_DualAction(mobj_t *actor);
203 void A_RemoteAction(mobj_t *actor);
204 void A_ToggleFlameJet(mobj_t *actor);
205 void A_OrbitNights(mobj_t *actor);
206 void A_GhostMe(mobj_t *actor);
207 void A_SetObjectState(mobj_t *actor);
208 void A_SetObjectTypeState(mobj_t *actor);
209 void A_KnockBack(mobj_t *actor);
210 void A_PushAway(mobj_t *actor);
211 void A_RingDrain(mobj_t *actor);
212 void A_SplitShot(mobj_t *actor);
213 void A_MissileSplit(mobj_t *actor);
214 void A_MultiShot(mobj_t *actor);
215 void A_InstaLoop(mobj_t *actor);
216 void A_Custom3DRotate(mobj_t *actor);
217 void A_SearchForPlayers(mobj_t *actor);
218 void A_CheckRandom(mobj_t *actor);
219 void A_CheckTargetRings(mobj_t *actor);
220 void A_CheckRings(mobj_t *actor);
221 void A_CheckTotalRings(mobj_t *actor);
222 void A_CheckHealth(mobj_t *actor);
223 void A_CheckRange(mobj_t *actor);
224 void A_CheckHeight(mobj_t *actor);
225 void A_CheckTrueRange(mobj_t *actor);
226 void A_CheckThingCount(mobj_t *actor);
227 void A_CheckAmbush(mobj_t *actor);
228 void A_CheckCustomValue(mobj_t *actor);
229 void A_CheckCusValMemo(mobj_t *actor);
230 void A_SetCustomValue(mobj_t *actor);
231 void A_UseCusValMemo(mobj_t *actor);
232 void A_RelayCustomValue(mobj_t *actor);
233 void A_CusValAction(mobj_t *actor);
234 void A_ForceStop(mobj_t *actor);
235 void A_ForceWin(mobj_t *actor);
236 void A_SpikeRetract(mobj_t *actor);
237 void A_InfoState(mobj_t *actor);
238 void A_Repeat(mobj_t *actor);
239 void A_SetScale(mobj_t *actor);
240 void A_RemoteDamage(mobj_t *actor);
241 void A_HomingChase(mobj_t *actor);
242 void A_TrapShot(mobj_t *actor);
243 void A_Boss1Chase(mobj_t *actor);
244 void A_Boss2Chase(mobj_t *actor);
245 void A_Boss2Pogo(mobj_t *actor);
246 void A_BossJetFume(mobj_t *actor);
247 void A_VileTarget(mobj_t *actor);
248 void A_VileAttack(mobj_t *actor);
249 void A_VileFire(mobj_t *actor);
250 void A_BrakChase(mobj_t *actor);
251 void A_BrakFireShot(mobj_t *actor);
252 void A_BrakLobShot(mobj_t *actor);
253 void A_NapalmScatter(mobj_t *actor);
254 void A_SpawnFreshCopy(mobj_t *actor);
255 void A_FlickySpawn(mobj_t *actor);
256 void A_FlickyCenter(mobj_t *actor);
257 void A_FlickyAim(mobj_t *actor);
258 void A_FlickyFly(mobj_t *actor);
259 void A_FlickySoar(mobj_t *actor);
260 void A_FlickyCoast(mobj_t *actor);
261 void A_FlickyHop(mobj_t *actor);
262 void A_FlickyFlounder(mobj_t *actor);
263 void A_FlickyCheck(mobj_t *actor);
264 void A_FlickyHeightCheck(mobj_t *actor);
265 void A_FlickyFlutter(mobj_t *actor);
266 void A_FlameParticle(mobj_t *actor);
267 void A_FadeOverlay(mobj_t *actor);
268 void A_Boss5Jump(mobj_t *actor);
269 void A_LightBeamReset(mobj_t *actor);
270 void A_MineExplode(mobj_t *actor);
271 void A_MineRange(mobj_t *actor);
272 void A_ConnectToGround(mobj_t *actor);
273 void A_SpawnParticleRelative(mobj_t *actor);
274 void A_MultiShotDist(mobj_t *actor);
275 void A_WhoCaresIfYourSonIsABee(mobj_t *actor);
276 void A_ParentTriesToSleep(mobj_t *actor);
277 void A_CryingToMomma(mobj_t *actor);
278 void A_CheckFlags2(mobj_t *actor);
279 void A_Boss5FindWaypoint(mobj_t *actor);
280 void A_DoNPCSkid(mobj_t *actor);
281 void A_DoNPCPain(mobj_t *actor);
282 void A_PrepareRepeat(mobj_t *actor);
283 void A_Boss5ExtraRepeat(mobj_t *actor);
284 void A_Boss5Calm(mobj_t *actor);
285 void A_Boss5CheckOnGround(mobj_t *actor);
286 void A_Boss5CheckFalling(mobj_t *actor);
287 void A_Boss5PinchShot(mobj_t *actor);
288 void A_Boss5MakeItRain(mobj_t *actor);
289 void A_Boss5MakeJunk(mobj_t *actor);
290 void A_LookForBetter(mobj_t *actor);
291 void A_Boss5BombExplode(mobj_t *actor);
292 void A_DustDevilThink(mobj_t *actor);
293 void A_TNTExplode(mobj_t *actor);
294 void A_DebrisRandom(mobj_t *actor);
295 void A_TrainCameo(mobj_t *actor);
296 void A_TrainCameo2(mobj_t *actor);
297 void A_CanarivoreGas(mobj_t *actor);
298 void A_KillSegments(mobj_t *actor);
299 void A_SnapperSpawn(mobj_t *actor);
300 void A_SnapperThinker(mobj_t *actor);
301 void A_SaloonDoorSpawn(mobj_t *actor);
302 void A_MinecartSparkThink(mobj_t *actor);
303 void A_ModuloToState(mobj_t *actor);
304 void A_LavafallRocks(mobj_t *actor);
305 void A_LavafallLava(mobj_t *actor);
306 void A_FallingLavaCheck(mobj_t *actor);
307 void A_FireShrink(mobj_t *actor);
308 void A_SpawnPterabytes(mobj_t *actor);
309 void A_PterabyteHover(mobj_t *actor);
310 void A_RolloutSpawn(mobj_t *actor);
311 void A_RolloutRock(mobj_t *actor);
312 void A_DragonbomberSpawn(mobj_t *actor);
313 void A_DragonWing(mobj_t *actor);
314 void A_DragonSegment(mobj_t *actor);
315 void A_ChangeHeight(mobj_t *actor);
316
317 //for p_enemy.c
318
319 //
320 // ENEMY THINKING
321 // Enemies are always spawned with targetplayer = -1, threshold = 0
322 // Most monsters are spawned unaware of all players, but some can be made preaware.
323 //
324
325 //
326 // P_CheckMeleeRange
327 //
P_CheckMeleeRange(mobj_t * actor)328 boolean P_CheckMeleeRange(mobj_t *actor)
329 {
330 mobj_t *pl;
331 fixed_t dist;
332
333 if (!actor->target)
334 return false;
335
336 pl = actor->target;
337 dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
338
339 if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
340 return false;
341
342 // check height now, so that damn crawlas cant attack
343 // you if you stand on a higher ledge.
344 if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
345 return false;
346
347 if (!P_CheckSight(actor, actor->target))
348 return false;
349
350 return true;
351 }
352
353 // P_CheckMeleeRange for Jettysyn Bomber.
P_JetbCheckMeleeRange(mobj_t * actor)354 boolean P_JetbCheckMeleeRange(mobj_t *actor)
355 {
356 mobj_t *pl;
357 fixed_t dist;
358
359 if (!actor->target)
360 return false;
361
362 pl = actor->target;
363 dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
364
365 if (dist >= (actor->radius + pl->radius)*2)
366 return false;
367
368 if (actor->eflags & MFE_VERTICALFLIP)
369 {
370 if (pl->z < actor->z + actor->height + FixedMul(40<<FRACBITS, actor->scale))
371 return false;
372 }
373 else
374 {
375 if (pl->z + pl->height > actor->z - FixedMul(40<<FRACBITS, actor->scale))
376 return false;
377 }
378
379 return true;
380 }
381
382 // P_CheckMeleeRange for CastleBot FaceStabber.
P_FaceStabCheckMeleeRange(mobj_t * actor)383 boolean P_FaceStabCheckMeleeRange(mobj_t *actor)
384 {
385 mobj_t *pl;
386 fixed_t dist;
387
388 if (!actor->target)
389 return false;
390
391 pl = actor->target;
392 dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
393
394 if (dist >= (actor->radius + pl->radius)*4)
395 return false;
396
397 if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
398 return false;
399
400 if (!P_CheckSight(actor, actor->target))
401 return false;
402
403 return true;
404 }
405
406 // P_CheckMeleeRange for Skim.
P_SkimCheckMeleeRange(mobj_t * actor)407 boolean P_SkimCheckMeleeRange(mobj_t *actor)
408 {
409 mobj_t *pl;
410 fixed_t dist;
411
412 if (!actor->target)
413 return false;
414
415 pl = actor->target;
416 dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
417
418 if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
419 return false;
420
421 if (actor->eflags & MFE_VERTICALFLIP)
422 {
423 if (pl->z < actor->z + actor->height + FixedMul(24<<FRACBITS, actor->scale))
424 return false;
425 }
426 else
427 {
428 if (pl->z + pl->height > actor->z - FixedMul(24<<FRACBITS, actor->scale))
429 return false;
430 }
431
432 return true;
433 }
434
435 //
436 // P_CheckMissileRange
437 //
P_CheckMissileRange(mobj_t * actor)438 boolean P_CheckMissileRange(mobj_t *actor)
439 {
440 fixed_t dist;
441
442 if (!actor->target)
443 return false;
444
445 if (actor->reactiontime)
446 return false; // do not attack yet
447
448 if (!P_CheckSight(actor, actor->target))
449 return false;
450
451 // OPTIMIZE: get this from a global checksight
452 dist = P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - FixedMul(64*FRACUNIT, actor->scale);
453
454 if (!actor->info->meleestate)
455 dist -= FixedMul(128*FRACUNIT, actor->scale); // no melee attack, so fire more
456
457 dist >>= FRACBITS;
458
459 if (actor->type == MT_EGGMOBILE)
460 dist >>= 1;
461
462 if (dist > 200)
463 dist = 200;
464
465 if (actor->type == MT_EGGMOBILE && dist > 160)
466 dist = 160;
467
468 if (P_RandomByte() < dist)
469 return false;
470
471 return true;
472 }
473
474 /** Checks for water in a sector.
475 * Used by Skim movements.
476 *
477 * \param x X coordinate on the map.
478 * \param y Y coordinate on the map.
479 * \return True if there's water at this location, false if not.
480 * \sa ::MT_SKIM
481 */
P_WaterInSector(mobj_t * mobj,fixed_t x,fixed_t y)482 static boolean P_WaterInSector(mobj_t *mobj, fixed_t x, fixed_t y)
483 {
484 sector_t *sector;
485
486 sector = R_PointInSubsector(x, y)->sector;
487
488 if (sector->ffloors)
489 {
490 ffloor_t *rover;
491
492 for (rover = sector->ffloors; rover; rover = rover->next)
493 {
494 if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE))
495 continue;
496
497 if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z)
498 return true; // we found water!!
499 }
500 }
501
502 return false;
503 }
504
505 static const fixed_t xspeed[NUMDIRS] = {FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS)), 0, 46341>>(16-FRACBITS)};
506 static const fixed_t yspeed[NUMDIRS] = {0, 46341>>(16-FRACBITS), FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS))};
507
508 /** Moves an actor in its current direction.
509 *
510 * \param actor Actor object to move.
511 * \return False if the move is blocked, otherwise true.
512 */
P_Move(mobj_t * actor,fixed_t speed)513 boolean P_Move(mobj_t *actor, fixed_t speed)
514 {
515 fixed_t tryx, tryy;
516 dirtype_t movedir = actor->movedir;
517
518 if (movedir == DI_NODIR || !actor->health)
519 return false;
520
521 I_Assert(movedir < NUMDIRS);
522
523 tryx = actor->x + FixedMul(speed*xspeed[movedir], actor->scale);
524 if (twodlevel || actor->flags2 & MF2_TWOD)
525 tryy = actor->y;
526 else
527 tryy = actor->y + FixedMul(speed*yspeed[movedir], actor->scale);
528
529 if (actor->type == MT_SKIM && !P_WaterInSector(actor, tryx, tryy)) // bail out if sector lacks water
530 return false;
531
532 if (!P_TryMove(actor, tryx, tryy, false))
533 {
534 if (actor->flags & MF_FLOAT && floatok)
535 {
536 // must adjust height
537 if (actor->z < tmfloorz)
538 actor->z += FixedMul(FLOATSPEED, actor->scale);
539 else
540 actor->z -= FixedMul(FLOATSPEED, actor->scale);
541
542 if (actor->type == MT_JETJAW && actor->z + actor->height > actor->watertop)
543 actor->z = actor->watertop - actor->height;
544
545 actor->flags2 |= MF2_INFLOAT;
546 return true;
547 }
548
549 return false;
550 }
551 else
552 actor->flags2 &= ~MF2_INFLOAT;
553
554 return true;
555 }
556
557 /** Attempts to move an actor on in its current direction.
558 * If the move succeeds, the actor's move count is reset
559 * randomly to a value from 0 to 15.
560 *
561 * \param actor Actor to move.
562 * \return True if the move succeeds, false if the move is blocked.
563 */
P_TryWalk(mobj_t * actor)564 static boolean P_TryWalk(mobj_t *actor)
565 {
566 if (!P_Move(actor, actor->info->speed))
567 return false;
568 actor->movecount = P_RandomByte() & 15;
569 return true;
570 }
571
P_NewChaseDir(mobj_t * actor)572 void P_NewChaseDir(mobj_t *actor)
573 {
574 fixed_t deltax, deltay;
575 dirtype_t d[3];
576 dirtype_t tdir = DI_NODIR, olddir, turnaround;
577
578 I_Assert(actor->target != NULL);
579 I_Assert(!P_MobjWasRemoved(actor->target));
580
581 olddir = actor->movedir;
582
583 if (olddir >= NUMDIRS)
584 olddir = DI_NODIR;
585
586 if (olddir != DI_NODIR)
587 turnaround = opposite[olddir];
588 else
589 turnaround = olddir;
590
591 deltax = actor->target->x - actor->x;
592 deltay = actor->target->y - actor->y;
593
594 if (deltax > FixedMul(10*FRACUNIT, actor->scale))
595 d[1] = DI_EAST;
596 else if (deltax < -FixedMul(10*FRACUNIT, actor->scale))
597 d[1] = DI_WEST;
598 else
599 d[1] = DI_NODIR;
600
601 if (twodlevel || actor->flags2 & MF2_TWOD)
602 d[2] = DI_NODIR;
603 if (deltay < -FixedMul(10*FRACUNIT, actor->scale))
604 d[2] = DI_SOUTH;
605 else if (deltay > FixedMul(10*FRACUNIT, actor->scale))
606 d[2] = DI_NORTH;
607 else
608 d[2] = DI_NODIR;
609
610 // try direct route
611 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
612 {
613 dirtype_t newdir = diags[((deltay < 0)<<1) + (deltax > 0)];
614
615 actor->movedir = newdir;
616 if ((newdir != turnaround) && P_TryWalk(actor))
617 return;
618 }
619
620 // try other directions
621 if (P_RandomChance(25*FRACUNIT/32) || abs(deltay) > abs(deltax))
622 {
623 tdir = d[1];
624 d[1] = d[2];
625 d[2] = tdir;
626 }
627
628 if (d[1] == turnaround)
629 d[1] = DI_NODIR;
630 if (d[2] == turnaround)
631 d[2] = DI_NODIR;
632
633 if (d[1] != DI_NODIR)
634 {
635 actor->movedir = d[1];
636
637 if (P_TryWalk(actor))
638 return; // either moved forward or attacked
639 }
640
641 if (d[2] != DI_NODIR)
642 {
643 actor->movedir = d[2];
644
645 if (P_TryWalk(actor))
646 return;
647 }
648
649 // there is no direct path to the player, so pick another direction.
650 if (olddir != DI_NODIR)
651 {
652 actor->movedir =olddir;
653
654 if (P_TryWalk(actor))
655 return;
656 }
657
658 // randomly determine direction of search
659 if (P_RandomChance(FRACUNIT/2))
660 {
661 for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
662 {
663 if (tdir != turnaround)
664 {
665 actor->movedir = tdir;
666
667 if (P_TryWalk(actor))
668 return;
669 }
670 }
671 }
672 else
673 {
674 for (tdir = DI_SOUTHEAST; tdir >= DI_EAST; tdir--)
675 {
676 if (tdir != turnaround)
677 {
678 actor->movedir = tdir;
679
680 if (P_TryWalk(actor))
681 return;
682 }
683 }
684 }
685
686 if (turnaround != DI_NODIR)
687 {
688 actor->movedir = turnaround;
689
690 if (P_TryWalk(actor))
691 return;
692 }
693
694 actor->movedir = (angle_t)DI_NODIR; // cannot move
695 }
696
697 /** Looks for players to chase after, aim at, or whatever.
698 *
699 * \param actor The object looking for flesh.
700 * \param allaround Look all around? If false, only players in a 180-degree
701 * range in front will be spotted.
702 * \param dist If > 0, checks distance
703 * \return True if a player is found, otherwise false.
704 * \sa P_SupermanLook4Players
705 */
P_LookForPlayers(mobj_t * actor,boolean allaround,boolean tracer,fixed_t dist)706 boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist)
707 {
708 INT32 c = 0, stop;
709 player_t *player;
710 angle_t an;
711
712 // BP: first time init, this allow minimum lastlook changes
713 if (actor->lastlook < 0)
714 actor->lastlook = P_RandomByte();
715
716 actor->lastlook %= MAXPLAYERS;
717
718 stop = (actor->lastlook - 1) & PLAYERSMASK;
719
720 for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
721 {
722 // done looking
723 if (actor->lastlook == stop)
724 return false;
725
726 if (!playeringame[actor->lastlook])
727 continue;
728
729 if (c++ == 2)
730 return false;
731
732 player = &players[actor->lastlook];
733
734 if ((netgame || multiplayer) && player->spectator)
735 continue;
736
737 if (player->pflags & PF_INVIS)
738 continue; // ignore notarget
739
740 if (!player->mo || P_MobjWasRemoved(player->mo))
741 continue;
742
743 if (player->mo->health <= 0)
744 continue; // dead
745
746 if (player->bot)
747 continue; // ignore bots
748
749 if (player->quittime)
750 continue; // Ignore uncontrolled bodies
751
752 if (dist > 0
753 && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist)
754 continue; // Too far away
755
756 if (!allaround)
757 {
758 an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
759 if (an > ANGLE_90 && an < ANGLE_270)
760 {
761 dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y);
762 // if real close, react anyway
763 if (dist > FixedMul(MELEERANGE, actor->scale))
764 continue; // behind back
765 }
766 }
767
768 if (!P_CheckSight(actor, player->mo))
769 continue; // out of sight
770
771 if (tracer)
772 P_SetTarget(&actor->tracer, player->mo);
773 else
774 P_SetTarget(&actor->target, player->mo);
775 return true;
776 }
777
778 //return false;
779 }
780
781 /** Looks for a player with a ring shield.
782 * Used by rings.
783 *
784 * \param actor Ring looking for a shield to be attracted to.
785 * \return True if a player with ring shield is found, otherwise false.
786 * \sa A_AttractChase
787 */
P_LookForShield(mobj_t * actor)788 static boolean P_LookForShield(mobj_t *actor)
789 {
790 INT32 c = 0, stop;
791 player_t *player;
792
793 // BP: first time init, this allow minimum lastlook changes
794 if (actor->lastlook < 0)
795 actor->lastlook = P_RandomByte();
796
797 actor->lastlook %= MAXPLAYERS;
798
799 stop = (actor->lastlook - 1) & PLAYERSMASK;
800
801 for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK))
802 {
803 // done looking
804 if (actor->lastlook == stop)
805 return false;
806
807 if (!playeringame[actor->lastlook])
808 continue;
809
810 if (c++ == 2)
811 return false;
812
813 player = &players[actor->lastlook];
814
815 if (!player->mo || player->mo->health <= 0)
816 continue; // dead
817
818 //When in CTF, don't pull rings that you cannot pick up.
819 if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) ||
820 (actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
821 continue;
822
823 if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
824 && (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
825 {
826 P_SetTarget(&actor->tracer, player->mo);
827
828 if (actor->hnext)
829 P_SetTarget(&actor->hnext->hprev, actor->hprev);
830 if (actor->hprev)
831 P_SetTarget(&actor->hprev->hnext, actor->hnext);
832
833 return true;
834 }
835 }
836
837 //return false;
838 }
839
840 #ifdef WEIGHTEDRECYCLER
841 // Compares players to see who currently has the "best" items, etc.
P_RecycleCompare(const void * p1,const void * p2)842 static int P_RecycleCompare(const void *p1, const void *p2)
843 {
844 player_t *player1 = &players[*(const UINT8 *)p1];
845 player_t *player2 = &players[*(const UINT8 *)p2];
846
847 // Non-shooting gametypes
848 if (!G_PlatformGametype())
849 {
850 // Invincibility.
851 if (player1->powers[pw_invulnerability] > player2->powers[pw_invulnerability]) return -1;
852 else if (player2->powers[pw_invulnerability] > player1->powers[pw_invulnerability]) return 1;
853
854 // One has a shield, the other doesn't.
855 if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
856 else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
857
858 // Sneakers.
859 if (player1->powers[pw_sneakers] > player2->powers[pw_sneakers]) return -1;
860 else if (player2->powers[pw_sneakers] > player1->powers[pw_sneakers]) return 1;
861 }
862 else // Match, Team Match, CTF, Tag, Etc.
863 {
864 UINT8 player1_em = M_CountBits((UINT32)player1->powers[pw_emeralds], 7);
865 UINT8 player2_em = M_CountBits((UINT32)player2->powers[pw_emeralds], 7);
866
867 UINT8 player1_rw = M_CountBits((UINT32)player1->ringweapons, NUM_WEAPONS-1);
868 UINT8 player2_rw = M_CountBits((UINT32)player2->ringweapons, NUM_WEAPONS-1);
869
870 UINT16 player1_am = player1->powers[pw_infinityring] // max 800
871 + player1->powers[pw_automaticring] // max 300
872 + (player1->powers[pw_bouncering] * 3) // max 100
873 + (player1->powers[pw_explosionring] * 6) // max 50
874 + (player1->powers[pw_scatterring] * 3) // max 100
875 + (player1->powers[pw_grenadering] * 6) // max 50
876 + (player1->powers[pw_railring] * 6); // max 50
877 UINT16 player2_am = player2->powers[pw_infinityring] // max 800
878 + player2->powers[pw_automaticring] // max 300
879 + (player2->powers[pw_bouncering] * 3) // max 100
880 + (player2->powers[pw_explosionring] * 6) // max 50
881 + (player2->powers[pw_scatterring] * 3) // max 100
882 + (player2->powers[pw_grenadering] * 6) // max 50
883 + (player2->powers[pw_railring] * 6); // max 50
884
885 // Super trumps everything.
886 if (player1->powers[pw_super] && !player2->powers[pw_super]) return -1;
887 else if (player2->powers[pw_super] && !player1->powers[pw_super]) return 1;
888
889 // Emerald count if neither player is Super.
890 if (player1_em > player2_em) return -1;
891 else if (player1_em < player2_em) return 1;
892
893 // One has a shield, the other doesn't.
894 // (the likelihood of a shielded player being worse off than one without one is low.)
895 if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
896 else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
897
898 // Ring weapons count
899 if (player1_rw > player2_rw) return -1;
900 else if (player1_rw < player2_rw) return 1;
901
902 // Ring ammo if they have the same number of weapons
903 if (player1_am > player2_am) return -1;
904 else if (player1_am < player2_am) return 1;
905 }
906
907 // Identical for our purposes
908 return 0;
909 }
910 #endif
911
912 // Handles random monitor weights via console.
P_DoRandomBoxChances(void)913 static mobjtype_t P_DoRandomBoxChances(void)
914 {
915 mobjtype_t spawnchance[256];
916 INT32 numchoices = 0, i = 0;
917
918 if (!(netgame || multiplayer))
919 {
920 switch (P_RandomKey(10))
921 {
922 case 0:
923 return MT_RING_ICON;
924 case 1:
925 return MT_SNEAKERS_ICON;
926 case 2:
927 return MT_INVULN_ICON;
928 case 3:
929 return MT_WHIRLWIND_ICON;
930 case 4:
931 return MT_ELEMENTAL_ICON;
932 case 5:
933 return MT_ATTRACT_ICON;
934 case 6:
935 return MT_FORCE_ICON;
936 case 7:
937 return MT_ARMAGEDDON_ICON;
938 case 8:
939 return MT_1UP_ICON;
940 case 9:
941 return MT_EGGMAN_ICON;
942 }
943 return MT_NULL;
944 }
945
946 #define QUESTIONBOXCHANCES(type, cvar) \
947 for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
948 QUESTIONBOXCHANCES(MT_RING_ICON, cv_superring);
949 QUESTIONBOXCHANCES(MT_SNEAKERS_ICON, cv_supersneakers);
950 QUESTIONBOXCHANCES(MT_INVULN_ICON, cv_invincibility);
951 QUESTIONBOXCHANCES(MT_WHIRLWIND_ICON, cv_jumpshield);
952 QUESTIONBOXCHANCES(MT_ELEMENTAL_ICON, cv_watershield);
953 QUESTIONBOXCHANCES(MT_ATTRACT_ICON, cv_ringshield);
954 QUESTIONBOXCHANCES(MT_FORCE_ICON, cv_forceshield);
955 QUESTIONBOXCHANCES(MT_ARMAGEDDON_ICON, cv_bombshield);
956 QUESTIONBOXCHANCES(MT_1UP_ICON, cv_1up);
957 QUESTIONBOXCHANCES(MT_EGGMAN_ICON, cv_eggmanbox);
958 QUESTIONBOXCHANCES(MT_MIXUP_ICON, cv_teleporters);
959 QUESTIONBOXCHANCES(MT_RECYCLER_ICON, cv_recycler);
960 #undef QUESTIONBOXCHANCES
961
962 if (numchoices == 0) return MT_NULL;
963 return spawnchance[P_RandomKey(numchoices)];
964 }
965
966 //
967 // ACTION ROUTINES
968 //
969
970 // Function: A_Look
971 //
972 // Description: Look for a player and set your target to them.
973 //
974 // var1:
975 // lower 16 bits = look all around
976 // upper 16 bits = distance limit
977 // var2 = If 1, only change to seestate. If 2, only play seesound. If 0, do both.
978 //
A_Look(mobj_t * actor)979 void A_Look(mobj_t *actor)
980 {
981 INT32 locvar1 = var1;
982 INT32 locvar2 = var2;
983
984 if (LUA_CallAction(A_LOOK, actor))
985 return;
986
987 if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale)))
988 return;
989
990 // go into chase state
991 if (!locvar2)
992 {
993 P_SetMobjState(actor, actor->info->seestate);
994 A_PlaySeeSound(actor);
995 }
996 else if (locvar2 == 1) // Only go into seestate
997 P_SetMobjState(actor, actor->info->seestate);
998 else if (locvar2 == 2) // Only play seesound
999 A_PlaySeeSound(actor);
1000 }
1001
1002 // Function: A_Chase
1003 //
1004 // Description: Chase after your target.
1005 //
1006 // var1:
1007 // 1 = don't check meleestate
1008 // 2 = don't check missilestate
1009 // 3 = don't check meleestate and missilestate
1010 // var2 = unused
1011 //
A_Chase(mobj_t * actor)1012 void A_Chase(mobj_t *actor)
1013 {
1014 INT32 delta;
1015 INT32 locvar1 = var1;
1016
1017 if (LUA_CallAction(A_CHASE, actor))
1018 return;
1019
1020 I_Assert(actor != NULL);
1021 I_Assert(!P_MobjWasRemoved(actor));
1022
1023 if (actor->reactiontime)
1024 actor->reactiontime--;
1025
1026 // modify target threshold
1027 if (actor->threshold)
1028 {
1029 if (!actor->target || actor->target->health <= 0)
1030 actor->threshold = 0;
1031 else
1032 actor->threshold--;
1033 }
1034
1035 // turn towards movement direction if not there yet
1036 if (actor->movedir < NUMDIRS)
1037 {
1038 actor->angle &= (7<<29);
1039 delta = actor->angle - (actor->movedir << 29);
1040
1041 if (delta > 0)
1042 actor->angle -= ANGLE_45;
1043 else if (delta < 0)
1044 actor->angle += ANGLE_45;
1045 }
1046
1047 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
1048 {
1049 // look for a new target
1050 if (P_LookForPlayers(actor, true, false, 0))
1051 return; // got a new target
1052
1053 P_SetMobjStateNF(actor, actor->info->spawnstate);
1054 return;
1055 }
1056
1057 // do not attack twice in a row
1058 if (actor->flags2 & MF2_JUSTATTACKED)
1059 {
1060 actor->flags2 &= ~MF2_JUSTATTACKED;
1061 P_NewChaseDir(actor);
1062 return;
1063 }
1064
1065 // check for melee attack
1066 if (!(locvar1 & 1) && actor->info->meleestate && P_CheckMeleeRange(actor))
1067 {
1068 if (actor->info->attacksound)
1069 S_StartAttackSound(actor, actor->info->attacksound);
1070
1071 P_SetMobjState(actor, actor->info->meleestate);
1072 return;
1073 }
1074
1075 // check for missile attack
1076 if (!(locvar1 & 2) && actor->info->missilestate)
1077 {
1078 if (actor->movecount || !P_CheckMissileRange(actor))
1079 goto nomissile;
1080
1081 P_SetMobjState(actor, actor->info->missilestate);
1082 actor->flags2 |= MF2_JUSTATTACKED;
1083 return;
1084 }
1085
1086 nomissile:
1087 // possibly choose another target
1088 if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
1089 && P_LookForPlayers(actor, true, false, 0))
1090 return; // got a new target
1091
1092 // chase towards player
1093 if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
1094 P_NewChaseDir(actor);
1095 }
1096
1097 // Function: A_FaceStabChase
1098 //
1099 // Description: Unused variant of A_Chase for Castlebot Facestabber.
1100 //
1101 // var1 = unused
1102 // var2 = unused
1103 //
A_FaceStabChase(mobj_t * actor)1104 void A_FaceStabChase(mobj_t *actor)
1105 {
1106 INT32 delta;
1107
1108 if (LUA_CallAction(A_FACESTABCHASE, actor))
1109 return;
1110
1111 if (actor->reactiontime)
1112 actor->reactiontime--;
1113
1114 // modify target threshold
1115 if (actor->threshold)
1116 {
1117 if (!actor->target || actor->target->health <= 0)
1118 actor->threshold = 0;
1119 else
1120 actor->threshold--;
1121 }
1122
1123 // turn towards movement direction if not there yet
1124 if (actor->movedir < NUMDIRS)
1125 {
1126 actor->angle &= (7<<29);
1127 delta = actor->angle - (actor->movedir << 29);
1128
1129 if (delta > 0)
1130 actor->angle -= ANGLE_45;
1131 else if (delta < 0)
1132 actor->angle += ANGLE_45;
1133 }
1134
1135 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
1136 {
1137 // look for a new target
1138 if (P_LookForPlayers(actor, true, false, 0))
1139 return; // got a new target
1140
1141 P_SetMobjStateNF(actor, actor->info->spawnstate);
1142 return;
1143 }
1144
1145 // do not attack twice in a row
1146 if (actor->flags2 & MF2_JUSTATTACKED)
1147 {
1148 actor->flags2 &= ~MF2_JUSTATTACKED;
1149 P_NewChaseDir(actor);
1150 return;
1151 }
1152
1153 // check for melee attack
1154 if (actor->info->meleestate && P_FaceStabCheckMeleeRange(actor))
1155 {
1156 if (actor->info->attacksound)
1157 S_StartAttackSound(actor, actor->info->attacksound);
1158
1159 P_SetMobjState(actor, actor->info->meleestate);
1160 return;
1161 }
1162
1163 // check for missile attack
1164 if (actor->info->missilestate)
1165 {
1166 if (actor->movecount || !P_CheckMissileRange(actor))
1167 goto nomissile;
1168
1169 P_SetMobjState(actor, actor->info->missilestate);
1170 actor->flags2 |= MF2_JUSTATTACKED;
1171 return;
1172 }
1173
1174 nomissile:
1175 // possibly choose another target
1176 if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
1177 && P_LookForPlayers(actor, true, false, 0))
1178 return; // got a new target
1179
1180 // chase towards player
1181 if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
1182 P_NewChaseDir(actor);
1183 }
1184
P_SharpDust(mobj_t * actor,mobjtype_t type,angle_t ang)1185 static void P_SharpDust(mobj_t *actor, mobjtype_t type, angle_t ang)
1186 {
1187 mobj_t *dust;
1188
1189 if (!type || !P_IsObjectOnGround(actor))
1190 return;
1191
1192 dust = P_SpawnMobjFromMobj(actor,
1193 -P_ReturnThrustX(actor, ang, 16<<FRACBITS),
1194 -P_ReturnThrustY(actor, ang, 16<<FRACBITS),
1195 0, type);
1196 P_SetObjectMomZ(dust, P_RandomRange(1, 4)<<FRACBITS, false);
1197 }
1198
P_FaceStabFlume(mobj_t * actor)1199 static void P_FaceStabFlume(mobj_t *actor)
1200 {
1201 mobj_t *flume;
1202 if (leveltime & 1)
1203 return;
1204
1205 flume = P_SpawnMobjFromMobj(actor,
1206 -P_ReturnThrustX(actor, actor->angle, actor->radius),
1207 -P_ReturnThrustY(actor, actor->angle, actor->radius),
1208 actor->height/3,
1209 MT_PARTICLE);
1210 flume->destscale = actor->scale*3;
1211 P_SetScale(flume, flume->destscale);
1212 P_SetTarget(&flume->target, actor);
1213 flume->sprite = SPR_JETF;
1214 flume->frame = FF_FULLBRIGHT;
1215 flume->tics = 2;
1216 }
1217
1218 // Function: A_FaceStabRev
1219 //
1220 // Description: Facestabber rev action
1221 //
1222 // var1 = effective duration
1223 // var2 = effective nextstate
1224 //
A_FaceStabRev(mobj_t * actor)1225 void A_FaceStabRev(mobj_t *actor)
1226 {
1227 INT32 locvar1 = var1;
1228 INT32 locvar2 = var2;
1229
1230 if (LUA_CallAction(A_FACESTABREV, actor))
1231 return;
1232
1233 if (!actor->target)
1234 {
1235 P_SetMobjState(actor, actor->info->spawnstate);
1236 return;
1237 }
1238
1239 actor->extravalue1 = 0;
1240
1241 if (!actor->reactiontime)
1242 {
1243 actor->reactiontime = locvar1;
1244 S_StartSound(actor, actor->info->activesound);
1245 }
1246 else
1247 {
1248 if ((--actor->reactiontime) == 0)
1249 {
1250 S_StartSound(actor, actor->info->attacksound);
1251 P_SetMobjState(actor, locvar2);
1252 }
1253 else
1254 {
1255 P_TryMove(actor, actor->x - P_ReturnThrustX(actor, actor->angle, 2<<FRACBITS), actor->y - P_ReturnThrustY(actor, actor->angle, 2<<FRACBITS), false);
1256 P_FaceStabFlume(actor);
1257 }
1258 }
1259 }
1260
1261 // Function: A_FaceStabHurl
1262 //
1263 // Description: Facestabber hurl action
1264 //
1265 // var1 = homing strength (recommended strength between 0-8)
1266 // var2 = effective nextstate
1267 //
A_FaceStabHurl(mobj_t * actor)1268 void A_FaceStabHurl(mobj_t *actor)
1269 {
1270 INT32 locvar1 = var1;
1271 INT32 locvar2 = var2;
1272
1273 if (LUA_CallAction(A_FACESTABHURL, actor))
1274 return;
1275
1276 if (actor->target)
1277 {
1278 angle_t visang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
1279 // Calculate new direction.
1280 angle_t dirang = actor->angle;
1281 angle_t diffang = visang - dirang;
1282
1283 if (locvar1) // Allow homing?
1284 {
1285 if (diffang > ANGLE_180)
1286 {
1287 angle_t workang = locvar1*(InvAngle(diffang)>>5);
1288 diffang += InvAngle(workang);
1289 }
1290 else
1291 diffang += (locvar1*(diffang>>5));
1292 }
1293 diffang += ANGLE_45;
1294
1295 // Check the sight cone.
1296 if (diffang < ANGLE_90)
1297 {
1298 actor->angle = dirang;
1299 if (++actor->extravalue2 < 4)
1300 actor->extravalue2 = 4;
1301 else if (actor->extravalue2 > 26)
1302 actor->extravalue2 = 26;
1303
1304 if (P_TryMove(actor,
1305 actor->x + P_ReturnThrustX(actor, dirang, actor->extravalue2<<FRACBITS),
1306 actor->y + P_ReturnThrustY(actor, dirang, actor->extravalue2<<FRACBITS),
1307 false))
1308 {
1309 // Do the spear damage.
1310 #define NUMSTEPS 3
1311 #define NUMGRADS 5
1312 #define MAXVAL (NUMSTEPS*NUMGRADS)
1313 SINT8 step = (++actor->extravalue1);
1314 fixed_t basesize = FRACUNIT/MAXVAL;
1315 mobj_t *hwork = actor;
1316 INT32 dist = 113;
1317 fixed_t xo = P_ReturnThrustX(actor, actor->angle, dist*basesize);
1318 fixed_t yo = P_ReturnThrustY(actor, actor->angle, dist*basesize);
1319
1320 while (step > 0)
1321 {
1322 if (!hwork->hnext)
1323 P_SetTarget(&hwork->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_FACESTABBERSPEAR));
1324 hwork = hwork->hnext;
1325 hwork->angle = actor->angle + ANGLE_90;
1326 hwork->destscale = FixedSqrt(step*basesize);
1327 P_SetScale(hwork, hwork->destscale);
1328 hwork->fuse = 2;
1329 P_TeleportMove(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<<FRACBITS)));
1330 step -= NUMGRADS;
1331 }
1332
1333 if (actor->extravalue1 >= MAXVAL)
1334 actor->extravalue1 -= NUMGRADS;
1335
1336 if ((step % 5) == 0)
1337 P_SharpDust(actor, MT_SPINDUST, actor->angle);
1338
1339 P_FaceStabFlume(actor);
1340 return;
1341 #undef MAXVAL
1342 #undef NUMGRADS
1343 #undef NUMSTEPS
1344 }
1345 }
1346 }
1347
1348 P_SetMobjState(actor, locvar2);
1349 actor->reactiontime = actor->info->reactiontime;
1350 }
1351
1352 // Function: A_FaceStabMiss
1353 //
1354 // Description: Facestabber miss action
1355 //
1356 // var1 = unused
1357 // var2 = effective nextstate
1358 //
A_FaceStabMiss(mobj_t * actor)1359 void A_FaceStabMiss(mobj_t *actor)
1360 {
1361 INT32 locvar2 = var2;
1362
1363 if (LUA_CallAction(A_FACESTABMISS, actor))
1364 return;
1365
1366 if (++actor->extravalue1 >= 3)
1367 {
1368 actor->extravalue2 -= 2;
1369 actor->extravalue1 = 0;
1370 S_StartSound(actor, sfx_s3k47);
1371 P_SharpDust(actor, MT_SPINDUST, actor->angle);
1372 }
1373
1374 if (actor->extravalue2 <= 0 || !P_TryMove(actor,
1375 actor->x + P_ReturnThrustX(actor, actor->angle, actor->extravalue2<<FRACBITS),
1376 actor->y + P_ReturnThrustY(actor, actor->angle, actor->extravalue2<<FRACBITS),
1377 false))
1378 {
1379 actor->extravalue2 = 0;
1380 P_SetMobjState(actor, locvar2);
1381 }
1382 }
1383
1384 // Function: A_StatueBurst
1385 //
1386 // Description: For suspicious statues only...
1387 //
1388 // var1 = object to create
1389 // var2 = effective nextstate for created object
1390 //
A_StatueBurst(mobj_t * actor)1391 void A_StatueBurst(mobj_t *actor)
1392 {
1393 INT32 locvar1 = var1;
1394 INT32 locvar2 = var2;
1395 mobjtype_t chunktype = (mobjtype_t)actor->info->raisestate;
1396 mobj_t *new;
1397
1398 if (LUA_CallAction(A_STATUEBURST, actor))
1399 return;
1400
1401 if (!locvar1 || !(new = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1)))
1402 return;
1403
1404 new->angle = actor->angle;
1405 P_SetTarget(&new->target, actor->target);
1406 if (locvar2)
1407 P_SetMobjState(new, (statenum_t)locvar2);
1408 S_StartSound(new, new->info->attacksound);
1409 S_StopSound(actor);
1410 S_StartSound(actor, sfx_s3k96);
1411
1412 {
1413 fixed_t a, b;
1414 fixed_t c = (actor->height>>2) - FixedMul(actor->scale, mobjinfo[chunktype].height>>1);
1415 fixed_t v = 4<<FRACBITS;
1416 const fixed_t r = (actor->radius>>1);
1417 mobj_t *spawned;
1418 UINT8 i;
1419 for (i = 0; i < 8; i++)
1420 {
1421 a = ((i & 1) ? r : (-r));
1422 b = ((i & 2) ? r : (-r));
1423 if (i == 4)
1424 {
1425 c += (actor->height>>1);
1426 v = 8<<FRACBITS;
1427 }
1428
1429 spawned = P_SpawnMobjFromMobj(actor, a, b, c, chunktype);
1430
1431 P_InstaThrust(spawned, R_PointToAngle2(0, 0, a, b), 8<<FRACBITS);
1432 P_SetObjectMomZ(spawned, v, false);
1433
1434 spawned->fuse = 3*TICRATE;
1435 }
1436 }
1437 }
1438
1439 // Function: A_JetJawRoam
1440 //
1441 // Description: Roaming routine for JetJaw
1442 //
1443 // var1 = unused
1444 // var2 = unused
1445 //
A_JetJawRoam(mobj_t * actor)1446 void A_JetJawRoam(mobj_t *actor)
1447 {
1448 if (LUA_CallAction(A_JETJAWROAM, actor))
1449 return;
1450
1451 if (actor->reactiontime)
1452 {
1453 actor->reactiontime--;
1454 P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed*FRACUNIT/4, actor->scale));
1455 }
1456 else
1457 {
1458 actor->reactiontime = actor->info->reactiontime;
1459 actor->angle += ANGLE_180;
1460 }
1461
1462 if (P_LookForPlayers(actor, false, false, actor->radius * 16))
1463 P_SetMobjState(actor, actor->info->seestate);
1464 }
1465
1466 // Function: A_JetJawChomp
1467 //
1468 // Description: Chase and chomp at the target, as long as it is in view
1469 //
1470 // var1 = unused
1471 // var2 = unused
1472 //
A_JetJawChomp(mobj_t * actor)1473 void A_JetJawChomp(mobj_t *actor)
1474 {
1475 INT32 delta;
1476
1477 if (LUA_CallAction(A_JETJAWCHOMP, actor))
1478 return;
1479
1480 // turn towards movement direction if not there yet
1481 if (actor->movedir < NUMDIRS)
1482 {
1483 actor->angle &= (7<<29);
1484 delta = actor->angle - (actor->movedir << 29);
1485
1486 if (delta > 0)
1487 actor->angle -= ANGLE_45;
1488 else if (delta < 0)
1489 actor->angle += ANGLE_45;
1490 }
1491
1492 // Stop chomping if target's dead or you can't see it
1493 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)
1494 || actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
1495 {
1496 P_SetMobjStateNF(actor, actor->info->spawnstate);
1497 return;
1498 }
1499
1500 // chase towards player
1501 if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
1502 P_NewChaseDir(actor);
1503 }
1504
1505 // Function: A_PointyThink
1506 //
1507 // Description: Thinker function for Pointy
1508 //
1509 // var1 = unused
1510 // var2 = unused
1511 //
A_PointyThink(mobj_t * actor)1512 void A_PointyThink(mobj_t *actor)
1513 {
1514 INT32 i;
1515 player_t *player = NULL;
1516 mobj_t *ball;
1517 TVector v;
1518 TVector *res;
1519 angle_t fa;
1520 fixed_t radius = FixedMul(actor->info->radius*actor->info->reactiontime, actor->scale);
1521 boolean firsttime = true;
1522 INT32 sign;
1523
1524 if (LUA_CallAction(A_POINTYTHINK, actor))
1525 return;
1526
1527 actor->momx = actor->momy = actor->momz = 0;
1528
1529 // Find nearest player
1530 for (i = 0; i < MAXPLAYERS; i++)
1531 {
1532 if (!playeringame[i] || players[i].spectator)
1533 continue;
1534
1535 if (!players[i].mo)
1536 continue;
1537
1538 if (!players[i].mo->health)
1539 continue;
1540
1541 if (!P_CheckSight(actor, players[i].mo))
1542 continue;
1543
1544 if (firsttime)
1545 {
1546 firsttime = false;
1547 player = &players[i];
1548 }
1549 else
1550 {
1551 if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) <
1552 P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y))
1553 player = &players[i];
1554 }
1555 }
1556
1557 if (!player)
1558 return;
1559
1560 // Okay, we found the closest player. Let's move based on his movement.
1561 P_SetTarget(&actor->target, player->mo);
1562 A_FaceTarget(actor);
1563
1564 if (P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) < P_AproxDistance(player->mo->x + player->mo->momx - actor->x, player->mo->y + player->mo->momy - actor->y))
1565 sign = -1; // Player is moving away
1566 else
1567 sign = 1; // Player is moving closer
1568
1569 if (player->mo->momx || player->mo->momy)
1570 {
1571 P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y), FixedMul(actor->info->speed*sign, actor->scale));
1572
1573 // Rotate our spike balls
1574 actor->lastlook += actor->info->damage;
1575 actor->lastlook %= FINEANGLES/4;
1576 }
1577
1578 if (!actor->tracer) // For some reason we do not have spike balls...
1579 return;
1580
1581 // Position spike balls relative to the value of 'lastlook'.
1582 ball = actor->tracer;
1583
1584 i = 0;
1585 while (ball)
1586 {
1587 fa = actor->lastlook+i;
1588 v[0] = FixedMul(FINECOSINE(fa),radius);
1589 v[1] = 0;
1590 v[2] = FixedMul(FINESINE(fa),radius);
1591 v[3] = FRACUNIT;
1592
1593 res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(actor->lastlook+i)));
1594 M_Memcpy(&v, res, sizeof (v));
1595 res = VectorMatrixMultiply(v, *RotateZMatrix(actor->angle+ANGLE_180));
1596 M_Memcpy(&v, res, sizeof (v));
1597
1598 P_UnsetThingPosition(ball);
1599 ball->x = actor->x + v[0];
1600 ball->y = actor->y + v[1];
1601 ball->z = actor->z + (actor->height>>1) + v[2];
1602 P_SetThingPosition(ball);
1603
1604 ball = ball->tracer;
1605 i += ANGLE_90 >> ANGLETOFINESHIFT;
1606 }
1607 }
1608
1609 // Function: A_CheckBuddy
1610 //
1611 // Description: Checks if target/tracer exists/has health. If not, the object removes itself.
1612 //
1613 // var1:
1614 // 0 = target
1615 // 1 = tracer
1616 // var2 = unused
1617 //
A_CheckBuddy(mobj_t * actor)1618 void A_CheckBuddy(mobj_t *actor)
1619 {
1620 INT32 locvar1 = var1;
1621
1622 if (LUA_CallAction(A_CHECKBUDDY, actor))
1623 return;
1624
1625 if (locvar1 && (!actor->tracer || actor->tracer->health <= 0))
1626 P_RemoveMobj(actor);
1627 else if (!locvar1 && (!actor->target || actor->target->health <= 0))
1628 P_RemoveMobj(actor);
1629 }
1630
1631 // Helper function for the Robo Hood.
1632 // Don't ask me how it works. Nev3r made it with dark majyks.
P_ParabolicMove(mobj_t * actor,fixed_t x,fixed_t y,fixed_t z,fixed_t speed)1633 static void P_ParabolicMove(mobj_t *actor, fixed_t x, fixed_t y, fixed_t z, fixed_t speed)
1634 {
1635 fixed_t dh;
1636
1637 x -= actor->x;
1638 y -= actor->y;
1639 z -= actor->z;
1640
1641 dh = P_AproxDistance(x, y);
1642
1643 actor->momx = FixedMul(FixedDiv(x, dh), speed);
1644 actor->momy = FixedMul(FixedDiv(y, dh), speed);
1645
1646 if (!gravity)
1647 return;
1648
1649 dh = FixedDiv(FixedMul(dh, gravity), speed);
1650 actor->momz = (dh>>1) + FixedDiv(z, dh<<1);
1651 }
1652
1653 // Function: A_HoodFire
1654 //
1655 // Description: Firing Robo-Hood
1656 //
1657 // var1 = object type to fire
1658 // var2 = unused
1659 //
A_HoodFire(mobj_t * actor)1660 void A_HoodFire(mobj_t *actor)
1661 {
1662 mobj_t *arrow;
1663 INT32 locvar1 = var1;
1664
1665 if (LUA_CallAction(A_HOODFIRE, actor))
1666 return;
1667
1668 // Check target first.
1669 if (!actor->target)
1670 {
1671 actor->reactiontime = actor->info->reactiontime;
1672 P_SetMobjState(actor, actor->info->spawnstate);
1673 return;
1674 }
1675
1676 A_FaceTarget(actor);
1677
1678 if (!(arrow = P_SpawnMissile(actor, actor->target, (mobjtype_t)locvar1)))
1679 return;
1680
1681 // Set a parabolic trajectory for the arrow.
1682 P_ParabolicMove(arrow, actor->target->x, actor->target->y, actor->target->z, arrow->info->speed);
1683 }
1684
1685 // Function: A_HoodThink
1686 //
1687 // Description: Thinker for Robo-Hood
1688 //
1689 // var1 = unused
1690 // var2 = unused
1691 //
A_HoodThink(mobj_t * actor)1692 void A_HoodThink(mobj_t *actor)
1693 {
1694 fixed_t dx, dy, dz, dm;
1695 boolean checksight;
1696
1697 if (LUA_CallAction(A_HOODTHINK, actor))
1698 return;
1699
1700 // Check target first.
1701 if (!actor->target)
1702 {
1703 actor->reactiontime = actor->info->reactiontime;
1704 P_SetMobjState(actor, actor->info->spawnstate);
1705 return;
1706 }
1707
1708 dx = (actor->target->x - actor->x), dy = (actor->target->y - actor->y), dz = (actor->target->z - actor->z);
1709 dm = P_AproxDistance(dx, dy);
1710 // Target dangerously close to robohood, retreat then.
1711 if ((dm < 256<<FRACBITS) && (abs(dz) < 128<<FRACBITS))
1712 {
1713 S_StartSound(actor, actor->info->attacksound);
1714 P_SetMobjState(actor, actor->info->raisestate);
1715 return;
1716 }
1717
1718 // If target on sight, look at it.
1719 if ((checksight = P_CheckSight(actor, actor->target)))
1720 {
1721 angle_t dang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
1722 if (actor->angle >= ANGLE_180)
1723 {
1724 actor->angle = InvAngle(actor->angle)>>1;
1725 actor->angle = InvAngle(actor->angle);
1726 }
1727 else
1728 actor->angle >>= 1;
1729
1730 if (dang >= ANGLE_180)
1731 {
1732 dang = InvAngle(dang)>>1;
1733 dang = InvAngle(dang);
1734 }
1735 else
1736 dang >>= 1;
1737
1738 actor->angle += dang;
1739 }
1740
1741 // Check whether to do anything.
1742 if ((--actor->reactiontime) <= 0)
1743 {
1744 actor->reactiontime = actor->info->reactiontime;
1745
1746 // If way too far, don't shoot.
1747 if ((dm < (3072<<FRACBITS)) && checksight)
1748 {
1749 P_SetMobjState(actor, actor->info->missilestate);
1750 return;
1751 }
1752 }
1753 }
1754
1755 // Function: A_HoodFall
1756 //
1757 // Description: Falling Robo-Hood
1758 //
1759 // var1 = unused
1760 // var2 = unused
1761 //
A_HoodFall(mobj_t * actor)1762 void A_HoodFall(mobj_t *actor)
1763 {
1764 if (LUA_CallAction(A_HOODFALL, actor))
1765 return;
1766
1767 if (!P_IsObjectOnGround(actor))
1768 return;
1769
1770 actor->momx = actor->momy = 0;
1771 actor->reactiontime = actor->info->reactiontime;
1772 P_SetMobjState(actor, actor->info->seestate);
1773 }
1774
1775 // Function: A_ArrowBonks
1776 //
1777 // Description: Arrow momentum setting on collision
1778 //
1779 // var1 = unused
1780 // var2 = unused
1781 //
A_ArrowBonks(mobj_t * actor)1782 void A_ArrowBonks(mobj_t *actor)
1783 {
1784 if (LUA_CallAction(A_ARROWBONKS, actor))
1785 return;
1786
1787 if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
1788 || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
1789 actor->angle += ANGLE_180;
1790
1791 P_SetObjectMomZ(actor, 8*actor->scale, false);
1792 P_InstaThrust(actor, actor->angle, -6*actor->scale);
1793
1794 actor->flags = (actor->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY;
1795 actor->z += P_MobjFlip(actor);
1796 }
1797
1798 // Function: A_SnailerThink
1799 //
1800 // Description: Thinker function for Snailer
1801 //
1802 // var1 = unused
1803 // var2 = unused
1804 //
A_SnailerThink(mobj_t * actor)1805 void A_SnailerThink(mobj_t *actor)
1806 {
1807 if (LUA_CallAction(A_SNAILERTHINK, actor))
1808 return;
1809
1810 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
1811 {
1812 // look for a new target
1813 if (!P_LookForPlayers(actor, true, false, 0))
1814 return;
1815 }
1816
1817 // We now have a target. Oh bliss, rapture, and contentment!
1818
1819 if (actor->target->z + actor->target->height > actor->z - FixedMul(32*FRACUNIT, actor->scale)
1820 && actor->target->z < actor->z + actor->height + FixedMul(32*FRACUNIT, actor->scale)
1821 && !(leveltime % (TICRATE*2)))
1822 {
1823 angle_t an;
1824 fixed_t z;
1825
1826 // Actor shouldn't face target, so we'll do things a bit differently here
1827
1828 an = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
1829
1830 z = actor->z + actor->height/2;
1831
1832 if (an > ANGLE_45 && an < ANGLE_315) // fire as close as you can to the target, even if too sharp an angle from your front
1833 {
1834 fixed_t dist;
1835 fixed_t dx, dy;
1836
1837 dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
1838
1839 if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left
1840 {
1841 dx = actor->x + P_ReturnThrustX(actor, actor->angle + ANGLE_45, dist);
1842 dy = actor->y + P_ReturnThrustY(actor, actor->angle + ANGLE_45, dist);
1843 }
1844 else if (an >= ANGLE_270 && an < ANGLE_315) // fire at 45 degrees to the right
1845 {
1846 dx = actor->x + P_ReturnThrustX(actor, actor->angle - ANGLE_45, dist);
1847 dy = actor->y + P_ReturnThrustY(actor, actor->angle - ANGLE_45, dist);
1848 }
1849 else // fire straight ahead
1850 {
1851 dx = actor->x + P_ReturnThrustX(actor, actor->angle, dist);
1852 dy = actor->y + P_ReturnThrustY(actor, actor->angle, dist);
1853 }
1854
1855 P_SpawnPointMissile(actor, dx, dy, actor->target->z, MT_ROCKET, actor->x, actor->y, z);
1856 }
1857 else
1858 P_SpawnXYZMissile(actor, actor->target, MT_ROCKET, actor->x, actor->y, z);
1859 }
1860
1861 if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z > actor->z)
1862 || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) > (actor->z + actor->height)))
1863 actor->momz += FixedMul(actor->info->speed, actor->scale);
1864 else if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z < actor->z)
1865 || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) < (actor->z + actor->height)))
1866 actor->momz -= FixedMul(actor->info->speed, actor->scale);
1867
1868 actor->momz /= 2;
1869 }
1870
1871 // Function: A_SharpChase
1872 //
1873 // Description: Thinker/Chase routine for Spincushions
1874 //
1875 // var1 = unused
1876 // var2 = unused
1877 //
A_SharpChase(mobj_t * actor)1878 void A_SharpChase(mobj_t *actor)
1879 {
1880 if (LUA_CallAction(A_SHARPCHASE, actor))
1881 return;
1882
1883 if (actor->reactiontime)
1884 {
1885 INT32 delta;
1886
1887 actor->reactiontime--;
1888
1889 // turn towards movement direction if not there yet
1890 if (actor->movedir < NUMDIRS)
1891 {
1892 actor->angle &= (7<<29);
1893 delta = actor->angle - (actor->movedir << 29);
1894
1895 if (delta > 0)
1896 actor->angle -= ANGLE_45;
1897 else if (delta < 0)
1898 actor->angle += ANGLE_45;
1899 }
1900
1901 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
1902 {
1903 // look for a new target
1904 if (P_LookForPlayers(actor, true, false, 0))
1905 return; // got a new target
1906
1907 P_SetMobjState(actor, actor->info->spawnstate);
1908 return;
1909 }
1910
1911 // chase towards player
1912 if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
1913 P_NewChaseDir(actor);
1914 }
1915 else
1916 {
1917 actor->threshold = actor->info->painchance;
1918 P_SetMobjState(actor, actor->info->missilestate);
1919 S_StartSound(actor, actor->info->attacksound);
1920 }
1921 }
1922
1923 // Function: A_SharpSpin
1924 //
1925 // Description: Spin chase routine for Spincushions
1926 //
1927 // var1 = object # to spawn as dust (if not provided not done)
1928 // var2 = if nonzero, do the old-style spinning using this as the angle difference
1929 //
A_SharpSpin(mobj_t * actor)1930 void A_SharpSpin(mobj_t *actor)
1931 {
1932 INT32 locvar1 = var1;
1933 INT32 locvar2 = var2;
1934 angle_t oldang = actor->angle;
1935
1936 if (LUA_CallAction(A_SHARPSPIN, actor))
1937 return;
1938
1939 if (actor->threshold && actor->target)
1940 {
1941 angle_t ang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
1942 P_Thrust(actor, ang, actor->info->speed*actor->scale);
1943 if (locvar2)
1944 actor->angle += locvar2; // ANGLE_22h;
1945 else
1946 actor->angle = ang;
1947 actor->threshold--;
1948 if (leveltime & 1)
1949 S_StartSound(actor, actor->info->painsound);
1950 }
1951 else
1952 {
1953 actor->reactiontime = actor->info->reactiontime;
1954 P_SetMobjState(actor, actor->info->meleestate);
1955 }
1956
1957 P_SharpDust(actor, locvar1, oldang);
1958 }
1959
1960 // Function: A_SharpDecel
1961 //
1962 // Description: Slow down the Spincushion
1963 //
1964 // var1 = unused
1965 // var2 = unused
1966 //
A_SharpDecel(mobj_t * actor)1967 void A_SharpDecel(mobj_t *actor)
1968 {
1969 if (LUA_CallAction(A_SHARPDECEL, actor))
1970 return;
1971
1972 if (actor->momx > 2 || actor->momy > 2)
1973 {
1974 actor->momx >>= 1;
1975 actor->momy >>= 1;
1976 }
1977 else
1978 P_SetMobjState(actor, actor->info->xdeathstate);
1979 }
1980
1981 // Function: A_CrushstaceanWalk
1982 //
1983 // Description: Crushstacean movement
1984 //
1985 // var1 = speed (actor info's speed if 0)
1986 // var2 = state to switch to when blocked (spawnstate if 0)
1987 //
A_CrushstaceanWalk(mobj_t * actor)1988 void A_CrushstaceanWalk(mobj_t *actor)
1989 {
1990 INT32 locvar1 = (var1 ? var1 : (INT32)actor->info->speed);
1991 INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate);
1992 angle_t ang = actor->angle + ((actor->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
1993
1994 if (LUA_CallAction(A_CRUSHSTACEANWALK, actor))
1995 return;
1996
1997 actor->reactiontime--;
1998
1999 if (!P_TryMove(actor,
2000 actor->x + P_ReturnThrustX(actor, ang, locvar1*actor->scale),
2001 actor->y + P_ReturnThrustY(actor, ang, locvar1*actor->scale),
2002 false)
2003 || (actor->reactiontime-- <= 0))
2004 {
2005 actor->flags2 ^= MF2_AMBUSH;
2006 P_SetTarget(&actor->target, NULL);
2007 P_SetMobjState(actor, locvar2);
2008 actor->reactiontime = actor->info->reactiontime;
2009 }
2010 }
2011
2012 // Function: A_CrushstaceanPunch
2013 //
2014 // Description: Crushstacean attack
2015 //
2016 // var1 = unused
2017 // var2 = state to go to if unsuccessful (spawnstate if 0)
2018 //
A_CrushstaceanPunch(mobj_t * actor)2019 void A_CrushstaceanPunch(mobj_t *actor)
2020 {
2021 INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate);
2022
2023 if (LUA_CallAction(A_CRUSHSTACEANPUNCH, actor))
2024 return;
2025
2026 if (!actor->tracer)
2027 return;
2028
2029 if (!actor->target)
2030 {
2031 P_SetMobjState(actor, locvar2);
2032 return;
2033 }
2034
2035 actor->tracer->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
2036 P_SetMobjState(actor->tracer, actor->tracer->info->missilestate);
2037 actor->tracer->extravalue1 = actor->tracer->extravalue2 = 0;
2038 S_StartSound(actor, actor->info->attacksound);
2039 }
2040
2041 // Function: A_CrushclawAim
2042 //
2043 // Description: Crushstacean claw aiming
2044 //
2045 // var1 = sideways offset
2046 // var2 = vertical offset
2047 //
A_CrushclawAim(mobj_t * actor)2048 void A_CrushclawAim(mobj_t *actor)
2049 {
2050 INT32 locvar1 = var1;
2051 INT32 locvar2 = var2;
2052 mobj_t *crab = actor->tracer;
2053 angle_t ang;
2054
2055 if (LUA_CallAction(A_CRUSHCLAWAIM, actor))
2056 return;
2057
2058 if (!crab)
2059 {
2060 P_RemoveMobj(actor);
2061 return; // there is only one step and it is crab
2062 }
2063
2064 if (crab->target || P_LookForPlayers(crab, true, false, actor->info->speed*crab->scale))
2065 ang = R_PointToAngle2(crab->x, crab->y, crab->target->x, crab->target->y);
2066 else
2067 ang = crab->angle + ((crab->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
2068 ang -= actor->angle;
2069
2070 #define anglimit ANGLE_22h
2071 #define angfactor 5
2072 if (ang < ANGLE_180)
2073 {
2074 if (ang > anglimit)
2075 ang = anglimit;
2076 ang /= angfactor;
2077 }
2078 else
2079 {
2080 ang = InvAngle(ang);
2081 if (ang > anglimit)
2082 ang = anglimit;
2083 ang = InvAngle(ang/angfactor);
2084 }
2085 actor->angle += ang;
2086 #undef anglimit
2087 #undef angfactor
2088
2089 P_TeleportMove(actor,
2090 crab->x + P_ReturnThrustX(actor, actor->angle, locvar1*crab->scale),
2091 crab->y + P_ReturnThrustY(actor, actor->angle, locvar1*crab->scale),
2092 crab->z + locvar2*crab->scale);
2093
2094 if (!crab->target || !crab->info->missilestate || (statenum_t)(crab->state-states) == crab->info->missilestate)
2095 return;
2096
2097 if (((ang + ANG1) < ANG2) || P_AproxDistance(crab->x - crab->target->x, crab->y - crab->target->y) < 333*crab->scale)
2098 P_SetMobjState(crab, crab->info->missilestate);
2099 }
2100
2101 // Function: A_CrushclawLaunch
2102 //
2103 // Description: Crushstacean claw launching
2104 //
2105 // var1:
2106 // 0 - forwards
2107 // anything else - backwards
2108 // var2 = state to change to when done
2109 //
A_CrushclawLaunch(mobj_t * actor)2110 void A_CrushclawLaunch(mobj_t *actor)
2111 {
2112 INT32 locvar1 = var1;
2113 INT32 locvar2 = var2;
2114 mobj_t *crab = actor->tracer;
2115
2116 if (LUA_CallAction(A_CRUSHCLAWLAUNCH, actor))
2117 return;
2118
2119 if (!crab)
2120 {
2121 mobj_t *chainnext;
2122 while (actor)
2123 {
2124 chainnext = actor->target;
2125 P_RemoveMobj(actor);
2126 actor = chainnext;
2127 }
2128 return; // there is only one step and it is crab
2129 }
2130
2131 if (!actor->extravalue1)
2132 {
2133 S_StartSound(actor, actor->info->activesound);
2134 actor->extravalue1 = ((locvar1) ? -1 : 32);
2135 }
2136 else if (actor->extravalue1 != 1)
2137 actor->extravalue1 -= 1;
2138
2139 #define CSEGS 5
2140 if (!actor->target)
2141 {
2142 mobj_t *prevchain = actor;
2143 UINT8 i = 0;
2144 for (i = 0; (i < CSEGS); i++)
2145 {
2146 mobj_t *newchain = P_SpawnMobjFromMobj(actor, 0, 0, 0, (mobjtype_t)actor->info->raisestate);
2147 P_SetTarget(&prevchain->target, newchain);
2148 prevchain = newchain;
2149 }
2150 actor->target->angle = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y);
2151 }
2152
2153 if ((!locvar1) && crab->target)
2154 {
2155 #define anglimit ANGLE_22h
2156 #define angfactor 7
2157 angle_t ang = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y) - actor->target->angle;
2158 if (ang < ANGLE_180)
2159 {
2160 if (ang > anglimit)
2161 ang = anglimit;
2162 ang /= angfactor;
2163 }
2164 else
2165 {
2166 ang = InvAngle(ang);
2167 if (ang > anglimit)
2168 ang = anglimit;
2169 ang /= angfactor;
2170 ang = InvAngle(ang);
2171 }
2172 actor->target->angle += ang;
2173 actor->angle = actor->target->angle;
2174 }
2175
2176 actor->extravalue2 += actor->extravalue1;
2177
2178 if (!P_TryMove(actor,
2179 actor->target->x + P_ReturnThrustX(actor, actor->target->angle, actor->extravalue2*actor->scale),
2180 actor->target->y + P_ReturnThrustY(actor, actor->target->angle, actor->extravalue2*actor->scale),
2181 true)
2182 && !locvar1)
2183 {
2184 actor->extravalue1 = 0;
2185 actor->extravalue2 = FixedHypot(actor->x - actor->target->x, actor->y - actor->target->y)>>FRACBITS;
2186 P_SetMobjState(actor, locvar2);
2187 S_StopSound(actor);
2188 S_StartSound(actor, sfx_s3k49);
2189 }
2190 else
2191 {
2192 actor->z = actor->target->z;
2193 if ((!locvar1 && (actor->extravalue2 > 256)) || (locvar1 && (actor->extravalue2 < 16)))
2194 {
2195 if (locvar1) // In case of retracting, resume crab and remove the chain.
2196 {
2197 mobj_t *chain = actor->target, *chainnext;
2198 while (chain)
2199 {
2200 chainnext = chain->target;
2201 P_RemoveMobj(chain);
2202 chain = chainnext;
2203 }
2204 actor->extravalue2 = 0;
2205 actor->angle = R_PointToAngle2(crab->x, crab->y, actor->x, actor->y);
2206 P_SetTarget(&actor->target, NULL);
2207 P_SetTarget(&crab->target, NULL);
2208 P_SetMobjState(crab, crab->state->nextstate);
2209 }
2210 actor->extravalue1 = 0;
2211 P_SetMobjState(actor, locvar2);
2212 S_StopSound(actor);
2213 if (!locvar1)
2214 S_StartSound(actor, sfx_s3k64);
2215 }
2216 }
2217
2218 if (!actor->target)
2219 return;
2220
2221 {
2222 mobj_t *chain = actor->target->target;
2223 fixed_t dx = (actor->x - actor->target->x)/CSEGS, dy = (actor->y - actor->target->y)/CSEGS, dz = (actor->z - actor->target->z)/CSEGS;
2224 fixed_t idx = dx, idy = dy, idz = dz;
2225 while (chain)
2226 {
2227 P_TeleportMove(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz);
2228 chain->movefactor = chain->z;
2229 idx += dx;
2230 idy += dy;
2231 idz += dz;
2232 chain = chain->target;
2233 }
2234 }
2235 #undef CSEGS
2236 }
2237
2238 // Function: A_VultureVtol
2239 //
2240 // Description: Vulture rising up to match target's height
2241 //
2242 // var1 = unused
2243 // var2 = unused
2244 //
A_VultureVtol(mobj_t * actor)2245 void A_VultureVtol(mobj_t *actor)
2246 {
2247 if (LUA_CallAction(A_VULTUREVTOL, actor))
2248 return;
2249
2250 if (!actor->target)
2251 return;
2252
2253 actor->flags |= MF_NOGRAVITY;
2254 actor->flags |= MF_FLOAT;
2255
2256 A_FaceTarget(actor);
2257
2258 S_StopSound(actor);
2259
2260 if (actor->z < actor->target->z+(actor->target->height/4) && actor->z + actor->height < actor->ceilingz)
2261 actor->momz = FixedMul(2*FRACUNIT, actor->scale);
2262 else if (actor->z > (actor->target->z+(actor->target->height/4)*3) && actor->z > actor->floorz)
2263 actor->momz = FixedMul(-2*FRACUNIT, actor->scale);
2264 else
2265 {
2266 // Attack!
2267 actor->momz = 0;
2268 P_SetMobjState(actor, actor->info->missilestate);
2269 S_StartSound(actor, actor->info->activesound);
2270 }
2271 }
2272
2273 // Function: A_VultureCheck
2274 //
2275 // Description: If the vulture is stopped, look for a new target
2276 //
2277 // var1 = unused
2278 // var2 = unused
2279 //
A_VultureCheck(mobj_t * actor)2280 void A_VultureCheck(mobj_t *actor)
2281 {
2282 if (LUA_CallAction(A_VULTURECHECK, actor))
2283 return;
2284
2285 if (actor->momx || actor->momy)
2286 return;
2287
2288 actor->flags &= ~MF_NOGRAVITY; // Fall down
2289
2290 if (actor->z <= actor->floorz)
2291 {
2292 actor->angle -= ANGLE_180; // turn around
2293 P_SetMobjState(actor, actor->info->spawnstate);
2294 }
2295 }
2296
P_VultureHoverParticle(mobj_t * actor)2297 static void P_VultureHoverParticle(mobj_t *actor)
2298 {
2299 fixed_t fdist = actor->z - P_FloorzAtPos(actor->x, actor->y, actor->z, actor->height);
2300
2301 if (fdist < 128*FRACUNIT)
2302 {
2303 mobj_t *dust;
2304 UINT8 i;
2305 angle_t angle = (leveltime % 2)*ANGLE_45/2;
2306
2307 for (i = 0; i <= 7; i++)
2308 {
2309 angle_t fa = (angle >> ANGLETOFINESHIFT) & FINEMASK;
2310 fixed_t px = actor->x + FixedMul(fdist + 64*FRACUNIT, FINECOSINE(fa));
2311 fixed_t py = actor->y + FixedMul(fdist + 64*FRACUNIT, FINESINE(fa));
2312 fixed_t pz = P_FloorzAtPos(px, py, actor->z, actor->height);
2313
2314 dust = P_SpawnMobj(px, py, pz, MT_ARIDDUST);
2315 P_SetMobjState(dust, (statenum_t)(dust->state - states + P_RandomRange(0, 2)));
2316 P_Thrust(dust, angle, FixedDiv(12*FRACUNIT, max(FRACUNIT, fdist/2)));
2317 dust->momx += actor->momx;
2318 dust->momy += actor->momy;
2319 angle += ANGLE_45;
2320 }
2321 }
2322 }
2323
2324 // Function: A_VultureHover
2325 //
2326 // Description: Vulture hovering and checking whether to attack.
2327 //
2328 // var1 = unused
2329 // var2 = unused
2330 //
A_VultureHover(mobj_t * actor)2331 void A_VultureHover(mobj_t *actor)
2332 {
2333 fixed_t targetz;
2334 fixed_t distdif;
2335 fixed_t memz = actor->z;
2336 SINT8 i;
2337
2338 if (LUA_CallAction(A_VULTUREHOVER, actor))
2339 return;
2340
2341 if (!actor->target || P_MobjWasRemoved(actor->target))
2342 {
2343 P_SetMobjState(actor, actor->info->spawnstate);
2344 return;
2345 }
2346
2347 actor->flags |= MF_NOGRAVITY;
2348
2349 actor->momx -= actor->momx/24;
2350 actor->momy -= actor->momy/24;
2351
2352 P_VultureHoverParticle(actor);
2353
2354 A_FaceTarget(actor);
2355 targetz = actor->target->z + actor->target->height / 2;
2356 for (i = -1; i <= 1; i++)
2357 {
2358 actor->z = targetz - i * 128 * FRACUNIT;
2359 if (P_CheckSight(actor, actor->target))
2360 {
2361 targetz -= i * 128 * FRACUNIT;
2362 break;
2363 }
2364 }
2365 actor->z = memz;
2366
2367 distdif = (actor->z + actor->height/2) - targetz;
2368
2369 if (abs(actor->momz*16) > abs(distdif))
2370 actor->momz -= actor->momz/16;
2371 else if (distdif < 0)
2372 actor->momz = min(actor->momz+FRACUNIT/8, actor->info->speed*FRACUNIT);
2373 else
2374 actor->momz = max(actor->momz-FRACUNIT/8, -actor->info->speed*FRACUNIT);
2375
2376 if (abs(distdif) < 128*FRACUNIT && abs(actor->momz) < FRACUNIT && P_CheckSight(actor, actor->target))
2377 {
2378 P_SetMobjState(actor, actor->info->missilestate);
2379 actor->momx = 0;
2380 actor->momy = 0;
2381 actor->momz = 0;
2382 actor->extravalue1 = 0;
2383 }
2384 }
2385
2386 // Function: A_VultureBlast
2387 //
2388 // Description: Vulture spawning a dust cloud.
2389 //
2390 // var1 = unused
2391 // var2 = unused
2392 //
A_VultureBlast(mobj_t * actor)2393 void A_VultureBlast(mobj_t *actor)
2394 {
2395 mobj_t *dust;
2396 UINT8 i;
2397 angle_t faa;
2398 fixed_t faacos, faasin;
2399
2400 if (LUA_CallAction(A_VULTUREBLAST, actor))
2401 return;
2402
2403 S_StartSound(actor, actor->info->attacksound);
2404
2405 faa = (actor->angle >> ANGLETOFINESHIFT) & FINEMASK;
2406 faacos = FINECOSINE(faa);
2407 faasin = FINESINE(faa);
2408
2409 for (i = 0; i <= 7; i++)
2410 {
2411 angle_t fa = ((i*(angle_t)ANGLE_45) >> ANGLETOFINESHIFT) & FINEMASK;
2412 dust = P_SpawnMobj(actor->x + 48*FixedMul(FINECOSINE(fa), -faasin), actor->y + 48*FixedMul(FINECOSINE(fa), faacos), actor->z + actor->height/2 + 48*FINESINE(fa), MT_PARTICLE);
2413
2414 P_SetScale(dust, 4*FRACUNIT);
2415 dust->destscale = FRACUNIT;
2416 dust->scalespeed = 4*FRACUNIT/TICRATE;
2417 dust->fuse = TICRATE;
2418 dust->momx = FixedMul(FINECOSINE(fa), -faasin)*3;
2419 dust->momy = FixedMul(FINECOSINE(fa), faacos)*3;
2420 dust->momz = FINESINE(fa)*6;
2421 }
2422 }
2423
2424 // Function: A_VultureFly
2425 //
2426 // Description: Vulture charging towards target.
2427 //
2428 // var1 = unused
2429 // var2 = unused
2430 //
A_VultureFly(mobj_t * actor)2431 void A_VultureFly(mobj_t *actor)
2432 {
2433 fixed_t speedmax = 18*FRACUNIT;
2434 angle_t angledif;
2435 fixed_t dx, dy, dz, dxy, dm;
2436 mobj_t *dust;
2437 fixed_t momm;
2438
2439 if (LUA_CallAction(A_VULTUREFLY, actor))
2440 return;
2441
2442 if (!actor->target || P_MobjWasRemoved(actor->target))
2443 {
2444 P_SetMobjState(actor, actor->info->spawnstate);
2445 return;
2446 }
2447
2448 angledif = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
2449 dx = actor->target->x - actor->x;
2450 dy = actor->target->y - actor->y;
2451 dz = actor->target->z - actor->z;
2452 dxy = FixedHypot(dx, dy);
2453
2454 if (leveltime % 4 == 0)
2455 S_StartSound(actor, actor->info->activesound);
2456
2457 if (angledif > ANGLE_180)
2458 angledif = InvAngle(angledif);
2459
2460 // Tweak the target height according to the position.
2461 if (angledif < ANGLE_45) // Centered?
2462 {
2463 actor->reactiontime = actor->info->reactiontime;
2464 if (dxy > 768*FRACUNIT)
2465 dz = max(P_FloorzAtPos(actor->target->x, actor->target->y, actor->target->z, 0) - actor->z + min(dxy/8, 128*FRACUNIT), dz);
2466 }
2467 else
2468 {
2469 actor->reactiontime--;
2470
2471 if (angledif < ANGLE_90)
2472 dz = max(P_FloorzAtPos(actor->target->x, actor->target->y, actor->target->z, 0) - actor->z + min(dxy/2, 192*FRACUNIT), dz);
2473 else
2474 dz = max(P_FloorzAtPos(actor->target->x, actor->target->y, actor->target->z, 0) - actor->z + 232*FRACUNIT, dz);
2475 }
2476
2477 dm = FixedHypot(dz, dxy);
2478
2479 P_VultureHoverParticle(actor);
2480
2481 dust = P_SpawnMobj(actor->x + P_RandomFixed() - FRACUNIT/2, actor->y + P_RandomFixed() - FRACUNIT/2, actor->z + actor->height/2 + P_RandomFixed() - FRACUNIT/2, MT_PARTICLE);
2482 P_SetScale(dust, 2*FRACUNIT);
2483 dust->destscale = FRACUNIT/3;
2484 dust->scalespeed = FRACUNIT/40;
2485 dust->fuse = TICRATE*2;
2486
2487 actor->momx += FixedDiv(dx, dm)*2;
2488 actor->momy += FixedDiv(dy, dm)*2;
2489 actor->momz += FixedDiv(dz, dm)*2;
2490
2491 momm = FixedHypot(actor->momz, FixedHypot(actor->momx, actor->momy));
2492
2493 if (momm > speedmax/2 && actor->reactiontime == 0)
2494 {
2495 P_SetMobjState(actor, actor->info->seestate);
2496 return;
2497 }
2498
2499 //Hits a wall hard?
2500 if (actor->extravalue1 - momm > 15*FRACUNIT)
2501 {
2502 actor->flags &= ~MF_NOGRAVITY;
2503 P_SetMobjState(actor, actor->info->painstate);
2504 S_StopSound(actor);
2505 S_StartSound(actor, actor->info->painsound);
2506 return;
2507 }
2508 actor->extravalue1 = momm;
2509
2510 if (momm > speedmax)
2511 {
2512 actor->momx = FixedMul(FixedDiv(actor->momx, momm), speedmax);
2513 actor->momy = FixedMul(FixedDiv(actor->momy, momm), speedmax);
2514 actor->momz = FixedMul(FixedDiv(actor->momz, momm), speedmax);
2515 }
2516
2517 actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy);
2518 }
2519
2520 // Function: A_SkimChase
2521 //
2522 // Description: Thinker/Chase routine for Skims
2523 //
2524 // var1 = unused
2525 // var2 = unused
2526 //
A_SkimChase(mobj_t * actor)2527 void A_SkimChase(mobj_t *actor)
2528 {
2529 INT32 delta;
2530
2531 if (LUA_CallAction(A_SKIMCHASE, actor))
2532 return;
2533
2534 if (actor->reactiontime)
2535 actor->reactiontime--;
2536
2537 // modify target threshold
2538 if (actor->threshold)
2539 {
2540 if (!actor->target || actor->target->health <= 0)
2541 actor->threshold = 0;
2542 else
2543 actor->threshold--;
2544 }
2545
2546 // turn towards movement direction if not there yet
2547 if (actor->movedir < NUMDIRS)
2548 {
2549 actor->angle &= (7<<29);
2550 delta = actor->angle - (actor->movedir << 29);
2551
2552 if (delta > 0)
2553 actor->angle -= ANGLE_45;
2554 else if (delta < 0)
2555 actor->angle += ANGLE_45;
2556 }
2557
2558 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
2559 {
2560 // look for a new target
2561 P_LookForPlayers(actor, true, false, 0);
2562
2563 // the spawnstate for skims already calls this function so just return either way
2564 // without changing state
2565 return;
2566 }
2567
2568 // do not attack twice in a row
2569 if (actor->flags2 & MF2_JUSTATTACKED)
2570 {
2571 actor->flags2 &= ~MF2_JUSTATTACKED;
2572 P_NewChaseDir(actor);
2573 return;
2574 }
2575
2576 // check for melee attack
2577 if (actor->info->meleestate && P_SkimCheckMeleeRange(actor))
2578 {
2579 if (actor->info->attacksound)
2580 S_StartAttackSound(actor, actor->info->attacksound);
2581
2582 P_SetMobjState(actor, actor->info->meleestate);
2583 return;
2584 }
2585
2586 // check for missile attack
2587 if (actor->info->missilestate)
2588 {
2589 if (actor->movecount || !P_CheckMissileRange(actor))
2590 goto nomissile;
2591
2592 P_SetMobjState(actor, actor->info->missilestate);
2593 actor->flags2 |= MF2_JUSTATTACKED;
2594 return;
2595 }
2596
2597 nomissile:
2598 // possibly choose another target
2599 if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
2600 && P_LookForPlayers(actor, true, false, 0))
2601 return; // got a new target
2602
2603 // chase towards player
2604 if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
2605 P_NewChaseDir(actor);
2606 }
2607
2608 // Function: A_FaceTarget
2609 //
2610 // Description: Immediately turn to face towards your target.
2611 //
2612 // var1 = unused
2613 // var2 = unused
2614 //
A_FaceTarget(mobj_t * actor)2615 void A_FaceTarget(mobj_t *actor)
2616 {
2617 if (LUA_CallAction(A_FACETARGET, actor))
2618 return;
2619
2620 if (!actor->target)
2621 return;
2622
2623 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
2624 }
2625
2626 // Function: A_FaceTracer
2627 //
2628 // Description: Immediately turn to face towards your tracer.
2629 //
2630 // var1 = unused
2631 // var2 = unused
2632 //
A_FaceTracer(mobj_t * actor)2633 void A_FaceTracer(mobj_t *actor)
2634 {
2635 if (LUA_CallAction(A_FACETRACER, actor))
2636 return;
2637
2638 if (!actor->tracer)
2639 return;
2640
2641 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
2642 }
2643
2644 // Function: A_LobShot
2645 //
2646 // Description: Lob an object at your target.
2647 //
2648 // var1 = object # to lob
2649 // var2:
2650 // var2 >> 16 = height offset
2651 // var2 & 65535 = airtime
2652 //
A_LobShot(mobj_t * actor)2653 void A_LobShot(mobj_t *actor)
2654 {
2655 INT32 locvar1 = var1;
2656 INT32 locvar2 = var2 >> 16;
2657 mobj_t *shot, *hitspot;
2658 angle_t an;
2659 fixed_t z;
2660 fixed_t dist;
2661 fixed_t vertical, horizontal;
2662 fixed_t airtime = var2 & 65535;
2663
2664 if (LUA_CallAction(A_LOBSHOT, actor))
2665 return;
2666
2667 if (!actor->target)
2668 return;
2669
2670 A_FaceTarget(actor);
2671
2672 if (actor->eflags & MFE_VERTICALFLIP)
2673 {
2674 z = actor->z + actor->height - FixedMul(locvar2*FRACUNIT, actor->scale);
2675 if (actor->type == MT_BLACKEGGMAN)
2676 z -= FixedMul(mobjinfo[locvar1].height, actor->scale/2);
2677 else
2678 z -= FixedMul(mobjinfo[locvar1].height, actor->scale);
2679 }
2680 else
2681 z = actor->z + FixedMul(locvar2*FRACUNIT, actor->scale);
2682
2683 shot = P_SpawnMobj(actor->x, actor->y, z, locvar1);
2684
2685 if (actor->type == MT_BLACKEGGMAN)
2686 {
2687 shot->destscale = actor->scale/2;
2688 P_SetScale(shot, actor->scale/2);
2689 }
2690 else
2691 {
2692 shot->destscale = actor->scale;
2693 P_SetScale(shot, actor->scale);
2694 }
2695
2696 // Keep track of where it's going to land
2697 hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL);
2698 hitspot->tics = airtime;
2699 P_SetTarget(&shot->tracer, hitspot);
2700
2701 P_SetTarget(&shot->target, actor); // where it came from
2702
2703 shot->angle = an = actor->angle;
2704 an >>= ANGLETOFINESHIFT;
2705
2706 dist = P_AproxDistance(actor->target->x - shot->x, actor->target->y - shot->y);
2707
2708 horizontal = dist / airtime;
2709 vertical = FixedMul((gravity*airtime)/2, shot->scale);
2710
2711 shot->momx = FixedMul(horizontal, FINECOSINE(an));
2712 shot->momy = FixedMul(horizontal, FINESINE(an));
2713 shot->momz = vertical;
2714
2715 /* Try to adjust when destination is not the same height
2716 if (actor->z != actor->target->z)
2717 {
2718 fixed_t launchhyp;
2719 fixed_t diff;
2720 fixed_t orig;
2721
2722 diff = actor->z - actor->target->z;
2723 {
2724 launchhyp = P_AproxDistance(horizontal, vertical);
2725
2726 orig = FixedMul(FixedDiv(vertical, horizontal), diff);
2727
2728 CONS_Debug(DBG_GAMELOGIC, "orig: %d\n", (orig)>>FRACBITS);
2729
2730 horizontal = dist / airtime;
2731 vertical = (gravity*airtime)/2;
2732 }
2733 dist -= orig;
2734 shot->momx = FixedMul(horizontal, FINECOSINE(an));
2735 shot->momy = FixedMul(horizontal, FINESINE(an));
2736 shot->momz = vertical;
2737 */
2738
2739 if (shot->info->seesound)
2740 S_StartSound(shot, shot->info->seesound);
2741
2742 if (!(actor->flags & MF_BOSS))
2743 {
2744 if (ultimatemode)
2745 actor->reactiontime = actor->info->reactiontime*TICRATE;
2746 else
2747 actor->reactiontime = actor->info->reactiontime*TICRATE*2;
2748 }
2749 }
2750
2751 // Function: A_FireShot
2752 //
2753 // Description: Shoot an object at your target.
2754 //
2755 // var1 = object # to shoot
2756 // var2 = height offset
2757 //
A_FireShot(mobj_t * actor)2758 void A_FireShot(mobj_t *actor)
2759 {
2760 fixed_t z;
2761 INT32 locvar1 = var1;
2762 INT32 locvar2 = var2;
2763
2764 if (LUA_CallAction(A_FIRESHOT, actor))
2765 return;
2766
2767 if (!actor->target)
2768 return;
2769
2770 A_FaceTarget(actor);
2771
2772 if (actor->eflags & MFE_VERTICALFLIP)
2773 z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
2774 else
2775 z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
2776
2777 P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
2778
2779 if (!(actor->flags & MF_BOSS))
2780 {
2781 if (ultimatemode)
2782 actor->reactiontime = actor->info->reactiontime*TICRATE;
2783 else
2784 actor->reactiontime = actor->info->reactiontime*TICRATE*2;
2785 }
2786 }
2787
2788 // Function: A_SuperFireShot
2789 //
2790 // Description: Shoot an object at your target that will even stall Super Sonic.
2791 //
2792 // var1 = object # to shoot
2793 // var2 = height offset
2794 //
A_SuperFireShot(mobj_t * actor)2795 void A_SuperFireShot(mobj_t *actor)
2796 {
2797 fixed_t z;
2798 mobj_t *mo;
2799 INT32 locvar1 = var1;
2800 INT32 locvar2 = var2;
2801
2802 if (LUA_CallAction(A_SUPERFIRESHOT, actor))
2803 return;
2804
2805 if (!actor->target)
2806 return;
2807
2808 A_FaceTarget(actor);
2809
2810 if (actor->eflags & MFE_VERTICALFLIP)
2811 z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
2812 else
2813 z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
2814
2815 mo = P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
2816
2817 if (mo)
2818 mo->flags2 |= MF2_SUPERFIRE;
2819
2820 if (!(actor->flags & MF_BOSS))
2821 {
2822 if (ultimatemode)
2823 actor->reactiontime = actor->info->reactiontime*TICRATE;
2824 else
2825 actor->reactiontime = actor->info->reactiontime*TICRATE*2;
2826 }
2827 }
2828
2829 // Function: A_BossFireShot
2830 //
2831 // Description: Shoot an object at your target ala Bosses:
2832 //
2833 // var1 = object # to shoot
2834 // var2:
2835 // 0 - Boss 1 Left side
2836 // 1 - Boss 1 Right side
2837 // 2 - Boss 3 Left side upper
2838 // 3 - Boss 3 Left side lower
2839 // 4 - Boss 3 Right side upper
2840 // 5 - Boss 3 Right side lower
2841 //
A_BossFireShot(mobj_t * actor)2842 void A_BossFireShot(mobj_t *actor)
2843 {
2844 fixed_t x, y, z;
2845 INT32 locvar1 = var1;
2846 INT32 locvar2 = var2;
2847 mobj_t *missile;
2848
2849 if (LUA_CallAction(A_BOSSFIRESHOT, actor))
2850 return;
2851
2852 if (!actor->target)
2853 return;
2854
2855 A_FaceTarget(actor);
2856
2857 switch (locvar2)
2858 {
2859 case 0:
2860 x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
2861 y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
2862 if (actor->eflags & MFE_VERTICALFLIP)
2863 z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
2864 else
2865 z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
2866 break;
2867 case 1:
2868 x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
2869 y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
2870 if (actor->eflags & MFE_VERTICALFLIP)
2871 z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
2872 else
2873 z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
2874 break;
2875 case 2:
2876 x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
2877 y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
2878 if (actor->eflags & MFE_VERTICALFLIP)
2879 z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
2880 else
2881 z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
2882 break;
2883 case 3:
2884 x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
2885 y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
2886 if (actor->eflags & MFE_VERTICALFLIP)
2887 z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
2888 else
2889 z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
2890 break;
2891 case 4:
2892 x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
2893 y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
2894 if (actor->eflags & MFE_VERTICALFLIP)
2895 z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
2896 else
2897 z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
2898 break;
2899 case 5:
2900 x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
2901 y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
2902 if (actor->eflags & MFE_VERTICALFLIP)
2903 z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
2904 else
2905 z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
2906 break;
2907 default:
2908 x = actor->x;
2909 y = actor->y;
2910 z = actor->z + actor->height/2;
2911 break;
2912 }
2913
2914 missile = P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
2915
2916 if (missile && actor->tracer && (actor->tracer->flags & MF_BOSS)) // Don't harm your papa.
2917 P_SetTarget(&missile->target, actor->tracer);
2918 }
2919
2920 // Function: A_Boss7FireMissiles
2921 //
2922 // Description: Shoot 4 missiles of a specific object type at your target ala Black Eggman
2923 //
2924 // var1 = object # to shoot
2925 // var2 = firing sound
2926 //
A_Boss7FireMissiles(mobj_t * actor)2927 void A_Boss7FireMissiles(mobj_t *actor)
2928 {
2929 mobj_t dummymo;
2930 INT32 locvar1 = var1;
2931 INT32 locvar2 = var2;
2932
2933 if (LUA_CallAction(A_BOSS7FIREMISSILES, actor))
2934 return;
2935
2936 if (!actor->target)
2937 {
2938 P_SetMobjState(actor, actor->info->spawnstate);
2939 return;
2940 }
2941
2942 A_FaceTarget(actor);
2943
2944 S_StartSound(NULL, locvar2);
2945
2946 // set dummymo's coordinates
2947 dummymo.x = actor->target->x;
2948 dummymo.y = actor->target->y;
2949 dummymo.z = actor->target->z + FixedMul(16*FRACUNIT, actor->scale); // raised height
2950
2951 P_SpawnXYZMissile(actor, &dummymo, locvar1,
2952 actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
2953 actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
2954 actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
2955
2956 P_SpawnXYZMissile(actor, &dummymo, locvar1,
2957 actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
2958 actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
2959 actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
2960
2961 P_SpawnXYZMissile(actor, &dummymo, locvar1,
2962 actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
2963 actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
2964 actor->z + actor->height/2);
2965
2966 P_SpawnXYZMissile(actor, &dummymo, locvar1,
2967 actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
2968 actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
2969 actor->z + actor->height/2);
2970 }
2971
2972 // Function: A_Boss1Laser
2973 //
2974 // Description: Shoot an object at your target ala Bosses:
2975 //
2976 // var1 = object # to shoot
2977 // var2:
2978 // 0 - Boss 1 Left side
2979 // 1 - Boss 1 Right side
2980 // 2 - Triple laser
2981 // 3 - Boss 1 Middle
2982 // >=3 - Generic middle
2983 //
A_Boss1Laser(mobj_t * actor)2984 void A_Boss1Laser(mobj_t *actor)
2985 {
2986 fixed_t x, y, z, floorz, speed;
2987 INT32 locvar1 = var1;
2988 INT32 locvar2 = (var2 & 65535);
2989 INT32 upperend = (var2>>16);
2990 INT32 i;
2991 angle_t angle;
2992 mobj_t *point;
2993 tic_t dur;
2994 static const UINT8 LASERCOLORS[] =
2995 {
2996 SKINCOLOR_SUPERRED3,
2997 SKINCOLOR_SUPERRED4,
2998 SKINCOLOR_SUPERRED5,
2999 SKINCOLOR_FLAME,
3000 SKINCOLOR_RED,
3001 SKINCOLOR_RED,
3002 SKINCOLOR_FLAME,
3003 SKINCOLOR_SUPERRED5,
3004 SKINCOLOR_SUPERRED4,
3005 SKINCOLOR_SUPERRED3,
3006 };
3007
3008 if (LUA_CallAction(A_BOSS1LASER, actor))
3009 return;
3010
3011 if (!actor->target)
3012 return;
3013
3014 if (actor->state->tics > 1)
3015 dur = actor->tics;
3016 else
3017 {
3018 if ((upperend & 1) && (actor->extravalue2 > 1))
3019 actor->extravalue2--;
3020
3021 dur = actor->extravalue2;
3022 }
3023
3024 switch (locvar2)
3025 {
3026 case 0:
3027 x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(44*FRACUNIT, actor->scale));
3028 y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(44*FRACUNIT, actor->scale));
3029 if (actor->eflags & MFE_VERTICALFLIP)
3030 z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
3031 else
3032 z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
3033 break;
3034 case 1:
3035 x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(44*FRACUNIT, actor->scale));
3036 y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(44*FRACUNIT, actor->scale));
3037 if (actor->eflags & MFE_VERTICALFLIP)
3038 z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
3039 else
3040 z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
3041 break;
3042 case 2:
3043 var1 = locvar1; var2 = 3; // Fire middle laser
3044 A_Boss1Laser(actor);
3045 var1 = locvar1; var2 = 0; // Fire left laser
3046 A_Boss1Laser(actor);
3047 var1 = locvar1; var2 = 1; // Fire right laser
3048 A_Boss1Laser(actor);
3049 return;
3050 break;
3051 case 3:
3052 x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(42*FRACUNIT, actor->scale));
3053 y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(42*FRACUNIT, actor->scale));
3054 z = actor->z + actor->height/2;
3055 break;
3056 default:
3057 x = actor->x;
3058 y = actor->y;
3059 z = actor->z + actor->height/2;
3060 break;
3061 }
3062
3063 if (!(actor->flags2 & MF2_FIRING) && dur > 1)
3064 {
3065 actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);
3066 if (mobjinfo[locvar1].seesound)
3067 S_StartSound(actor, mobjinfo[locvar1].seesound);
3068
3069 point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET);
3070 point->angle = actor->angle;
3071 point->fuse = dur+1;
3072 P_SetTarget(&point->target, actor->target);
3073 P_SetTarget(&actor->target, point);
3074 }
3075
3076 angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y));
3077
3078 point = P_SpawnMobj(x, y, z, locvar1);
3079 P_SetTarget(&point->target, actor);
3080 point->angle = actor->angle;
3081 speed = point->radius;
3082 point->momz = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), speed);
3083 point->momx = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(point->angle>>ANGLETOFINESHIFT), speed));
3084 point->momy = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINESINE(point->angle>>ANGLETOFINESHIFT), speed));
3085
3086 for (i = 0; i < 256; i++)
3087 {
3088 mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type);
3089 mo->angle = point->angle;
3090 mo->color = LASERCOLORS[((UINT8)(i + 3*dur) >> 2) % sizeof(LASERCOLORS)]; // codeing
3091 P_UnsetThingPosition(mo);
3092 mo->flags = MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY;
3093 P_SetThingPosition(mo);
3094
3095 if (dur & 1 && mo->info->missilestate)
3096 {
3097 P_SetMobjState(mo, mo->info->missilestate);
3098 if (mo->info->meleestate)
3099 {
3100 mobj_t *mo2 = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_PARTICLE);
3101 mo2->flags2 |= MF2_LINKDRAW;
3102 P_SetTarget(&mo2->tracer, actor);
3103 P_SetMobjState(mo2, mo->info->meleestate);
3104 }
3105 }
3106
3107 if (dur == 1)
3108 P_SpawnGhostMobj(mo);
3109
3110 x = point->x, y = point->y, z = point->z;
3111 if (P_RailThinker(point))
3112 break;
3113 }
3114
3115 x += point->momx;
3116 y += point->momy;
3117 floorz = P_FloorzAtPos(x, y, z, mobjinfo[MT_EGGMOBILE_FIRE].height);
3118 if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1 && dur & 1)
3119 {
3120 point = P_SpawnMobj(x, y, floorz, MT_EGGMOBILE_FIRE);
3121 point->angle = actor->angle;
3122 point->destscale = actor->scale;
3123 P_SetScale(point, point->destscale);
3124 P_SetTarget(&point->target, actor);
3125 P_MobjCheckWater(point);
3126 if (point->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER))
3127 {
3128 for (i = 0; i < 2; i++)
3129 {
3130 UINT8 size = 3;
3131 mobj_t *steam = P_SpawnMobj(x, y, point->watertop - size*mobjinfo[MT_DUST].height, MT_DUST);
3132 P_SetScale(steam, size*actor->scale);
3133 P_SetObjectMomZ(steam, FRACUNIT + 2*P_RandomFixed(), true);
3134 P_InstaThrust(steam, FixedAngle(P_RandomKey(360)*FRACUNIT), 2*P_RandomFixed());
3135 if (point->info->painsound)
3136 S_StartSound(steam, point->info->painsound);
3137 }
3138 }
3139 else
3140 {
3141 fixed_t distx = P_ReturnThrustX(point, point->angle, point->radius);
3142 fixed_t disty = P_ReturnThrustY(point, point->angle, point->radius);
3143 if (P_TryMove(point, point->x + distx, point->y + disty, false) // prevents the sprite from clipping into the wall or dangling off ledges
3144 && P_TryMove(point, point->x - 2*distx, point->y - 2*disty, false)
3145 && P_TryMove(point, point->x + distx, point->y + disty, false))
3146 {
3147 if (point->info->seesound)
3148 S_StartSound(point, point->info->seesound);
3149 }
3150 else
3151 P_RemoveMobj(point);
3152 }
3153 }
3154
3155 if (dur > 1)
3156 actor->flags2 |= MF2_FIRING;
3157 else
3158 actor->flags2 &= ~MF2_FIRING;
3159 }
3160
3161 // Function: A_FocusTarget
3162 //
3163 // Description: Home in on your target.
3164 //
3165 // var1:
3166 // 0 - accelerative focus with friction
3167 // 1 - steady focus with fixed movement speed
3168 // anything else - don't move
3169 // var2:
3170 // 0 - don't trace target, just move forwards
3171 // & 1 - change horizontal angle
3172 // & 2 - change vertical angle
3173 //
A_FocusTarget(mobj_t * actor)3174 void A_FocusTarget(mobj_t *actor)
3175 {
3176 INT32 locvar1 = var1;
3177 INT32 locvar2 = var2;
3178
3179 if (LUA_CallAction(A_FOCUSTARGET, actor))
3180 return;
3181
3182 if (actor->target)
3183 {
3184 fixed_t speed = FixedMul(actor->info->speed, actor->scale);
3185 fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1);
3186 angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle);
3187 angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90);
3188 switch(locvar1)
3189 {
3190 case 0:
3191 {
3192 actor->momx -= actor->momx>>4, actor->momy -= actor->momy>>4, actor->momz -= actor->momz>>4;
3193 actor->momz += FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
3194 actor->momx += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
3195 actor->momy += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
3196 }
3197 break;
3198 case 1:
3199 if (dist > speed)
3200 {
3201 actor->momz = FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
3202 actor->momx = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
3203 actor->momy = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
3204 }
3205 else
3206 {
3207 actor->momx = 0, actor->momy = 0, actor->momz = 0;
3208 actor->z = actor->target->z + (actor->target->height>>1);
3209 P_TryMove(actor, actor->target->x, actor->target->y, true);
3210 }
3211 break;
3212 default:
3213 break;
3214 }
3215 }
3216 }
3217
3218 // Function: A_Boss4Reverse
3219 //
3220 // Description: Reverse arms direction.
3221 //
3222 // var1 = sfx to play
3223 // var2 = sfx to play in pinch
3224 //
A_Boss4Reverse(mobj_t * actor)3225 void A_Boss4Reverse(mobj_t *actor)
3226 {
3227 sfxenum_t locvar1 = (sfxenum_t)var1;
3228 sfxenum_t locvar2 = (sfxenum_t)var2;
3229
3230 if (LUA_CallAction(A_BOSS4REVERSE, actor))
3231 return;
3232
3233 actor->reactiontime = 0;
3234 if (actor->movedir < 3)
3235 {
3236 S_StartSound(NULL, locvar1);
3237 if (actor->movedir == 1)
3238 actor->movedir = 2;
3239 else
3240 actor->movedir = 1;
3241 }
3242 else
3243 {
3244 S_StartSound(NULL, locvar2);
3245 if (actor->movedir == 4)
3246 actor->movedir = 5;
3247 else
3248 actor->movedir = 4;
3249 actor->angle += ANGLE_180;
3250 actor->movefactor = -actor->movefactor;
3251 }
3252 }
3253
3254 // Function: A_Boss4SpeedUp
3255 //
3256 // Description: Speed up arms
3257 //
3258 // var1 = sfx to play
3259 // var2 = unused
3260 //
A_Boss4SpeedUp(mobj_t * actor)3261 void A_Boss4SpeedUp(mobj_t *actor)
3262 {
3263 sfxenum_t locvar1 = (sfxenum_t)var1;
3264
3265 if (LUA_CallAction(A_BOSS4SPEEDUP, actor))
3266 return;
3267
3268 S_StartSound(NULL, locvar1);
3269 actor->reactiontime = 2;
3270 }
3271
3272 // Function: A_Boss4Raise
3273 //
3274 // Description: Raise helmet
3275 //
3276 // var1 = sfx to play
3277 // var2 = unused
3278 //
A_Boss4Raise(mobj_t * actor)3279 void A_Boss4Raise(mobj_t *actor)
3280 {
3281 sfxenum_t locvar1 = (sfxenum_t)var1;
3282
3283 if (LUA_CallAction(A_BOSS4RAISE, actor))
3284 return;
3285
3286 S_StartSound(NULL, locvar1);
3287 actor->reactiontime = 1;
3288 }
3289
3290 // Function: A_SkullAttack
3291 //
3292 // Description: Fly at the player like a missile.
3293 //
3294 // var1:
3295 // 0 - Fly at the player
3296 // 1 - Fly away from the player
3297 // 2 - Strafe in relation to the player
3298 // 3 - Dynamic mode - don't get too close to walls
3299 // var2:
3300 // 0 - Fly horizontally and vertically
3301 // 1 - Fly horizontal-only (momz = 0)
3302 //
3303 #define SKULLSPEED (20*FRACUNIT)
3304
A_SkullAttack(mobj_t * actor)3305 void A_SkullAttack(mobj_t *actor)
3306 {
3307 mobj_t *dest;
3308 angle_t an;
3309 INT32 dist;
3310 INT32 speed;
3311 INT32 locvar1 = var1;
3312 INT32 locvar2 = var2;
3313
3314 if (LUA_CallAction(A_SKULLATTACK, actor))
3315 return;
3316
3317 if (!actor->target)
3318 return;
3319
3320 speed = FixedMul(SKULLSPEED, actor->scale);
3321
3322 dest = actor->target;
3323 actor->flags2 |= MF2_SKULLFLY;
3324 if (actor->info->activesound)
3325 S_StartSound(actor, actor->info->activesound);
3326 A_FaceTarget(actor);
3327
3328 dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
3329
3330 if (locvar1 == 1)
3331 actor->angle += ANGLE_180;
3332 else if (locvar1 == 2)
3333 actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90;
3334 else if (locvar1 == 3)
3335 {
3336 statenum_t oldspawnstate = mobjinfo[MT_NULL].spawnstate;
3337 UINT32 oldflags = mobjinfo[MT_NULL].flags;
3338 fixed_t oldradius = mobjinfo[MT_NULL].radius;
3339 fixed_t oldheight = mobjinfo[MT_NULL].height;
3340 mobj_t *check;
3341 INT32 i, j;
3342 static INT32 k;/* static for (at least) GCC 9.1 weirdness */
3343 boolean allow;
3344 angle_t testang = 0;
3345
3346 mobjinfo[MT_NULL].spawnstate = S_INVISIBLE;
3347 mobjinfo[MT_NULL].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP;
3348 mobjinfo[MT_NULL].radius = mobjinfo[actor->type].radius;
3349 mobjinfo[MT_NULL].height = mobjinfo[actor->type].height;
3350
3351 if (P_RandomChance(FRACUNIT/2)) // port priority 1?
3352 {
3353 i = 9;
3354 j = 27;
3355 }
3356 else
3357 {
3358 i = 27;
3359 j = 9;
3360 }
3361
3362 #define dostuff(q) check = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_NULL);\
3363 testang = actor->angle + ((i+(q))*ANG10);\
3364 allow = (P_TryMove(check,\
3365 P_ReturnThrustX(check, testang, dist + 2*actor->radius),\
3366 P_ReturnThrustY(check, testang, dist + 2*actor->radius),\
3367 true));\
3368 P_RemoveMobj(check);\
3369 if (allow)\
3370 break;
3371
3372 if (P_RandomChance(FRACUNIT/2)) // port priority 2?
3373 {
3374 for (k = 0; k < 9; k++)
3375 {
3376 dostuff(i+k)
3377 dostuff(i-k)
3378 dostuff(j+k)
3379 dostuff(j-k)
3380 }
3381 }
3382 else
3383 {
3384 for (k = 0; k < 9; k++)
3385 {
3386 dostuff(i-k)
3387 dostuff(i+k)
3388 dostuff(j-k)
3389 dostuff(j+k)
3390 }
3391 }
3392 actor->angle = testang;
3393
3394 #undef dostuff
3395
3396 mobjinfo[MT_NULL].spawnstate = oldspawnstate;
3397 mobjinfo[MT_NULL].flags = oldflags;
3398 mobjinfo[MT_NULL].radius = oldradius;
3399 mobjinfo[MT_NULL].height = oldheight;
3400 }
3401
3402 an = actor->angle >> ANGLETOFINESHIFT;
3403
3404 actor->momx = FixedMul(speed, FINECOSINE(an));
3405 actor->momy = FixedMul(speed, FINESINE(an));
3406 dist = dist / speed;
3407
3408 if (dist < 1)
3409 dist = 1;
3410
3411 actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
3412
3413 if (locvar1 == 1)
3414 actor->momz = -actor->momz;
3415 if (locvar2 == 1)
3416 actor->momz = 0;
3417 }
3418
3419 // Function: A_BossZoom
3420 //
3421 // Description: Like A_SkullAttack, but used by Boss 1.
3422 //
3423 // var1 = unused
3424 // var2 = unused
3425 //
A_BossZoom(mobj_t * actor)3426 void A_BossZoom(mobj_t *actor)
3427 {
3428 mobj_t *dest;
3429 angle_t an;
3430 INT32 dist;
3431
3432 if (LUA_CallAction(A_BOSSZOOM, actor))
3433 return;
3434
3435 if (!actor->target)
3436 return;
3437
3438 dest = actor->target;
3439 actor->flags2 |= MF2_SKULLFLY;
3440 if (actor->info->attacksound)
3441 S_StartAttackSound(actor, actor->info->attacksound);
3442 A_FaceTarget(actor);
3443 an = actor->angle >> ANGLETOFINESHIFT;
3444 actor->momx = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINECOSINE(an));
3445 actor->momy = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINESINE(an));
3446 dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
3447 dist = dist / FixedMul(actor->info->speed*5*FRACUNIT, actor->scale);
3448
3449 if (dist < 1)
3450 dist = 1;
3451 actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
3452 }
3453
3454 // Function: A_BossScream
3455 //
3456 // Description: Spawns explosions and plays appropriate sounds around the defeated boss.
3457 //
3458 // var1:
3459 // & 1 - Use P_Random to spawn explosions at complete random
3460 // & 2 - Use entire vertical range of object to spawn
3461 // var2 = Object to spawn. Default is MT_SONIC3KBOSSEXPLODE.
3462 //
A_BossScream(mobj_t * actor)3463 void A_BossScream(mobj_t *actor)
3464 {
3465 mobj_t *mo;
3466 fixed_t x, y, z;
3467 angle_t fa;
3468 INT32 locvar1 = var1;
3469 INT32 locvar2 = var2;
3470 mobjtype_t explodetype;
3471
3472 if (LUA_CallAction(A_BOSSSCREAM, actor))
3473 return;
3474
3475 if (locvar1 & 1)
3476 fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
3477 else
3478 {
3479 actor->movecount += 4*16;
3480 actor->movecount %= 360;
3481 fa = (FixedAngle(actor->movecount*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
3482 }
3483 x = actor->x + FixedMul(FINECOSINE(fa),actor->radius);
3484 y = actor->y + FixedMul(FINESINE(fa),actor->radius);
3485
3486 // Determine what mobj to spawn. If undefined or invalid, use MT_BOSSEXPLODE as default.
3487 if (locvar2 <= 0 || locvar2 >= NUMMOBJTYPES)
3488 explodetype = MT_SONIC3KBOSSEXPLODE; //MT_BOSSEXPLODE; -- piss to you, sonic 2
3489 else
3490 explodetype = (mobjtype_t)locvar2;
3491
3492 if (locvar1 & 2)
3493 z = actor->z + (P_RandomKey((actor->height - mobjinfo[explodetype].height)>>FRACBITS)<<FRACBITS);
3494 else if (actor->eflags & MFE_VERTICALFLIP)
3495 z = actor->z + actor->height - mobjinfo[explodetype].height - FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
3496 else
3497 z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
3498
3499 mo = P_SpawnMobj(x, y, z, explodetype);
3500 if (actor->eflags & MFE_VERTICALFLIP)
3501 mo->flags2 |= MF2_OBJECTFLIP;
3502 mo->destscale = actor->scale;
3503 P_SetScale(mo, mo->destscale);
3504 if (actor->info->deathsound)
3505 S_StartSound(mo, actor->info->deathsound);
3506 }
3507
3508 // Function: A_Scream
3509 //
3510 // Description: Starts the death sound of the object.
3511 //
3512 // var1 = unused
3513 // var2 = unused
3514 //
A_Scream(mobj_t * actor)3515 void A_Scream(mobj_t *actor)
3516 {
3517 if (LUA_CallAction(A_SCREAM, actor))
3518 return;
3519
3520 if (actor->tracer && (actor->tracer->type == MT_SHELL || actor->tracer->type == MT_FIREBALL))
3521 S_StartScreamSound(actor, sfx_mario2);
3522 else if (actor->info->deathsound)
3523 S_StartScreamSound(actor, actor->info->deathsound);
3524 }
3525
3526 // Function: A_Pain
3527 //
3528 // Description: Starts the pain sound of the object.
3529 //
3530 // var1 = unused
3531 // var2 = unused
3532 //
A_Pain(mobj_t * actor)3533 void A_Pain(mobj_t *actor)
3534 {
3535 if (LUA_CallAction(A_PAIN, actor))
3536 return;
3537
3538 if (actor->info->painsound)
3539 S_StartSound(actor, actor->info->painsound);
3540
3541 actor->flags2 &= ~MF2_FIRING;
3542 actor->flags2 &= ~MF2_SUPERFIRE;
3543 }
3544
3545 // Function: A_Fall
3546 //
3547 // Description: Changes a dying object's flags to reflect its having fallen to the ground.
3548 //
3549 // var1 = value to set repeat to if nonzero
3550 // var2 = unused
3551 //
A_Fall(mobj_t * actor)3552 void A_Fall(mobj_t *actor)
3553 {
3554 INT32 locvar1 = var1;
3555
3556 if (LUA_CallAction(A_FALL, actor))
3557 return;
3558
3559 // actor is on ground, it can be walked over
3560 actor->flags &= ~MF_SOLID;
3561
3562 // fall through the floor
3563 actor->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY;
3564
3565 // So change this if corpse objects
3566 // are meant to be obstacles.
3567
3568 if (locvar1)
3569 actor->extravalue2 = locvar1;
3570 }
3571
3572 #define LIVESBOXDISPLAYPLAYER // Use displayplayer instead of closest player
3573
3574 // Function: A_1upThinker
3575 //
3576 // Description: Used by the 1up box to show the player's face.
3577 //
3578 // var1 = unused
3579 // var2 = unused
3580 //
A_1upThinker(mobj_t * actor)3581 void A_1upThinker(mobj_t *actor)
3582 {
3583 INT32 i;
3584 fixed_t dist = INT32_MAX;
3585 fixed_t temp;
3586 INT32 closestplayer = -1;
3587
3588 if (LUA_CallAction(A_1UPTHINKER, actor))
3589 return;
3590
3591 for (i = 0; i < MAXPLAYERS; i++)
3592 {
3593 if (!playeringame[i] || players[i].bot || players[i].spectator)
3594 continue;
3595
3596 if (!players[i].mo)
3597 continue;
3598
3599 if ((netgame || multiplayer) && players[i].playerstate != PST_LIVE)
3600 continue;
3601
3602 temp = P_AproxDistance(players[i].mo->x-actor->x, players[i].mo->y-actor->y);
3603
3604 if (temp < dist)
3605 {
3606 closestplayer = i;
3607 dist = temp;
3608 }
3609 }
3610
3611 if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0)
3612 { // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite.
3613 if (actor->tracer)
3614 {
3615 mobj_t *tracer = actor->tracer;
3616 P_SetTarget(&actor->tracer, NULL);
3617 P_RemoveMobj(tracer);
3618 }
3619 return;
3620 }
3621
3622 // We're using the overlay, so use the overlay 1up box (no text)
3623 actor->sprite = SPR_TV1P;
3624
3625 if (!actor->tracer)
3626 {
3627 P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY));
3628 P_SetTarget(&actor->tracer->target, actor);
3629 actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame
3630 P_SetMobjState(actor->tracer, actor->info->seestate);
3631
3632 // The overlay is going to be one tic early turning off and on
3633 // because it's going to get its thinker run the frame we spawned it.
3634 // So make it take one tic longer if it just spawned.
3635 ++actor->tracer->tics;
3636 }
3637
3638 actor->tracer->color = players[closestplayer].mo->color;
3639 actor->tracer->skin = &skins[players[closestplayer].skin];
3640 }
3641
3642 // Function: A_MonitorPop
3643 //
3644 // Description: Used by monitors when they explode.
3645 //
3646 // var1 = unused
3647 // var2 = unused
3648 //
A_MonitorPop(mobj_t * actor)3649 void A_MonitorPop(mobj_t *actor)
3650 {
3651 mobjtype_t item = 0;
3652 mobj_t *newmobj;
3653
3654 if (LUA_CallAction(A_MONITORPOP, actor))
3655 return;
3656
3657 // Spawn the "pop" explosion.
3658 if (actor->info->deathsound)
3659 S_StartSound(actor, actor->info->deathsound);
3660 P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_EXPLODE);
3661
3662 // We're dead now. De-solidify.
3663 actor->health = 0;
3664 P_UnsetThingPosition(actor);
3665 actor->flags &= ~MF_SOLID;
3666 actor->flags |= MF_NOCLIP;
3667 P_SetThingPosition(actor);
3668
3669 if (actor->info->damage == MT_UNKNOWN)
3670 {
3671 // MT_UNKNOWN is random. Because it's unknown to us... get it?
3672 item = P_DoRandomBoxChances();
3673
3674 if (item == MT_NULL)
3675 {
3676 CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
3677 return;
3678 }
3679 }
3680 else
3681 item = actor->info->damage;
3682
3683 if (item == 0)
3684 {
3685 CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n");
3686 return;
3687 }
3688
3689 newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item);
3690 P_SetTarget(&newmobj->target, actor->target); // Transfer target
3691
3692 if (item == MT_1UP_ICON)
3693 {
3694 if (actor->tracer) // Remove the old lives icon.
3695 P_RemoveMobj(actor->tracer);
3696
3697 if (!newmobj->target
3698 || !newmobj->target->player
3699 || !newmobj->target->skin
3700 || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
3701 {} // No lives icon for this player, use the default.
3702 else
3703 { // Spawn the lives icon.
3704 mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
3705 P_SetTarget(&livesico->target, newmobj);
3706 P_SetTarget(&newmobj->tracer, livesico);
3707
3708 livesico->color = newmobj->target->player->mo->color;
3709 livesico->skin = &skins[newmobj->target->player->skin];
3710 P_SetMobjState(livesico, newmobj->info->seestate);
3711
3712 // We're using the overlay, so use the overlay 1up sprite (no text)
3713 newmobj->sprite = SPR_TV1P;
3714 }
3715 }
3716
3717 // Run a linedef executor immediately upon popping
3718 // You may want to delay your effects by 18 tics to sync with the reward giving
3719 if (actor->spawnpoint && actor->lastlook)
3720 P_LinedefExecute(actor->lastlook, actor->target, NULL);
3721 }
3722
3723 // Function: A_GoldMonitorPop
3724 //
3725 // Description: Used by repeating monitors when they turn off. They don't really pop, but, you know...
3726 //
3727 // var1 = unused
3728 // var2 = unused
3729 //
A_GoldMonitorPop(mobj_t * actor)3730 void A_GoldMonitorPop(mobj_t *actor)
3731 {
3732 mobjtype_t item = 0;
3733 mobj_t *newmobj;
3734
3735 if (LUA_CallAction(A_GOLDMONITORPOP, actor))
3736 return;
3737
3738 // Don't spawn the "pop" explosion, because the monitor isn't broken.
3739 if (actor->info->deathsound)
3740 S_StartSound(actor, actor->info->deathsound);
3741 //P_SpawnMobjFromMobj(actor, 0, 0, actor.height/4, MT_EXPLODE);
3742
3743 // Remove our flags for a bit.
3744 // Players can now stand on top of us.
3745 P_UnsetThingPosition(actor);
3746 actor->flags &= ~(MF_MONITOR|MF_SHOOTABLE);
3747 P_SetThingPosition(actor);
3748
3749 // Don't count this box in statistics. Sorry.
3750 if (actor->target && actor->target->player)
3751 --actor->target->player->numboxes;
3752 actor->fuse = 0; // Don't let the monitor code screw us up.
3753
3754 if (actor->info->damage == MT_UNKNOWN)
3755 {
3756 // MT_UNKNOWN is random. Because it's unknown to us... get it?
3757 item = P_DoRandomBoxChances();
3758
3759 if (item == MT_NULL)
3760 {
3761 CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
3762 return;
3763 }
3764 }
3765 else
3766 item = actor->info->damage;
3767
3768 if (item == 0)
3769 {
3770 CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_GoldMonitorPop\n");
3771 return;
3772 }
3773
3774 // Note: the icon spawns 1 fracunit higher
3775 newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item);
3776 P_SetTarget(&newmobj->target, actor->target); // Transfer target
3777
3778 if (item == MT_1UP_ICON)
3779 {
3780 if (actor->tracer) // Remove the old lives icon.
3781 P_RemoveMobj(actor->tracer);
3782
3783 if (!newmobj->target
3784 || !newmobj->target->player
3785 || !newmobj->target->skin
3786 || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
3787 {} // No lives icon for this player, use the default.
3788 else
3789 { // Spawn the lives icon.
3790 mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
3791 P_SetTarget(&livesico->target, newmobj);
3792 P_SetTarget(&newmobj->tracer, livesico);
3793
3794 livesico->color = newmobj->target->player->mo->color;
3795 livesico->skin = &skins[newmobj->target->player->skin];
3796 P_SetMobjState(livesico, newmobj->info->seestate);
3797
3798 // We're using the overlay, so use the overlay 1up sprite (no text)
3799 newmobj->sprite = SPR_TV1P;
3800 }
3801 }
3802
3803 // Run a linedef executor immediately upon popping
3804 // You may want to delay your effects by 18 tics to sync with the reward giving
3805 if (actor->spawnpoint && actor->lastlook)
3806 P_LinedefExecute(actor->lastlook, actor->target, NULL);
3807 }
3808
3809 // Function: A_GoldMonitorRestore
3810 //
3811 // Description: A repeating monitor is coming back to life. Reset monitor flags, etc.
3812 //
3813 // var1 = unused
3814 // var2 = unused
3815 //
A_GoldMonitorRestore(mobj_t * actor)3816 void A_GoldMonitorRestore(mobj_t *actor)
3817 {
3818 if (LUA_CallAction(A_GOLDMONITORRESTORE, actor))
3819 return;
3820
3821 actor->flags |= MF_MONITOR|MF_SHOOTABLE;
3822 actor->health = 1; // Just in case.
3823 }
3824
3825 // Function: A_GoldMonitorSparkle
3826 //
3827 // Description: Spawns the little sparkly effect around big monitors. Looks pretty, doesn't it?
3828 //
3829 // var1 = unused
3830 // var2 = unused
3831 //
A_GoldMonitorSparkle(mobj_t * actor)3832 void A_GoldMonitorSparkle(mobj_t *actor)
3833 {
3834 fixed_t i, ngangle, xofs, yofs;
3835
3836 if (LUA_CallAction(A_GOLDMONITORSPARKLE, actor))
3837 return;
3838
3839 ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS);
3840 xofs = FINESINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
3841 yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
3842
3843 for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2)
3844 P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false);
3845 }
3846
3847 // Function: A_Explode
3848 //
3849 // Description: Explodes an object, doing damage to any objects nearby. The target is used as the cause of the explosion. Damage value is used as explosion range.
3850 //
3851 // var1 = damagetype
3852 // var2 = unused
3853 //
A_Explode(mobj_t * actor)3854 void A_Explode(mobj_t *actor)
3855 {
3856 INT32 locvar1 = var1;
3857
3858 if (LUA_CallAction(A_EXPLODE, actor))
3859 return;
3860
3861 P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1, true);
3862 }
3863
3864 // Function: A_BossDeath
3865 //
3866 // Description: Possibly trigger special effects when boss dies.
3867 //
3868 // var1 = unused
3869 // var2 = unused
3870 //
A_BossDeath(mobj_t * mo)3871 void A_BossDeath(mobj_t *mo)
3872 {
3873 thinker_t *th;
3874 mobj_t *mo2;
3875 line_t junk;
3876 INT32 i;
3877
3878 if (LUA_CallAction(A_BOSSDEATH, mo))
3879 return;
3880
3881 if (mo->spawnpoint && mo->spawnpoint->extrainfo)
3882 P_LinedefExecute(LE_BOSSDEAD+(mo->spawnpoint->extrainfo*LE_PARAMWIDTH), mo, NULL);
3883 else
3884 P_LinedefExecute(LE_BOSSDEAD, mo, NULL);
3885 mo->health = 0;
3886
3887 // Boss is dead (but not necessarily fleeing...)
3888 // Lua may use this to ignore bosses after they start fleeing
3889 mo->flags2 |= MF2_BOSSDEAD;
3890
3891 // make sure there is a player alive for victory
3892 for (i = 0; i < MAXPLAYERS; i++)
3893 if (playeringame[i] && ((players[i].mo && players[i].mo->health)
3894 || ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
3895 break;
3896
3897 if (i == MAXPLAYERS)
3898 return; // no one left alive, so do not end game
3899
3900 // scan the remaining thinkers to see
3901 // if all bosses are dead
3902 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
3903 {
3904 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
3905 continue;
3906
3907 mo2 = (mobj_t *)th;
3908 if (mo2 != mo && (mo2->flags & MF_BOSS) && mo2->health > 0)
3909 goto bossjustdie; // other boss not dead - just go straight to dying!
3910 }
3911
3912 // victory!
3913 P_LinedefExecute(LE_ALLBOSSESDEAD, mo, NULL);
3914 if (stoppedclock && modeattacking) // if you're just time attacking, skip making the capsule appear since you don't need to step on it anyways.
3915 goto bossjustdie;
3916 if (mo->flags2 & MF2_BOSSNOTRAP)
3917 {
3918 for (i = 0; i < MAXPLAYERS; i++)
3919 {
3920 if (!playeringame[i])
3921 continue;
3922 P_DoPlayerExit(&players[i]);
3923 }
3924 }
3925 else
3926 {
3927 // Initialize my junk
3928 junk.tags.tags = NULL;
3929 junk.tags.count = 0;
3930
3931 // Bring the egg trap up to the surface
3932 // Incredibly shitty code ahead
3933 Tag_FSet(&junk.tags, LE_CAPSULE0);
3934 EV_DoElevator(&junk, elevateHighest, false);
3935 Tag_FSet(&junk.tags, LE_CAPSULE1);
3936 EV_DoElevator(&junk, elevateUp, false);
3937 Tag_FSet(&junk.tags, LE_CAPSULE2);
3938 EV_DoElevator(&junk, elevateHighest, false);
3939
3940 if (mapheaderinfo[gamemap-1]->muspostbossname[0] &&
3941 S_MusicExists(mapheaderinfo[gamemap-1]->muspostbossname, !midi_disabled, !digital_disabled))
3942 {
3943 // Touching the egg trap button calls P_DoPlayerExit, which calls P_RestoreMusic.
3944 // So just park ourselves in the mapmus variables.
3945 // But don't change the mapmus variables if they were modified from their level header values (e.g., TUNES).
3946 boolean changed = strnicmp(mapheaderinfo[gamemap-1]->musname, S_MusicName(), 7);
3947 if (!strnicmp(mapheaderinfo[gamemap-1]->musname, mapmusname, 7))
3948 {
3949 strncpy(mapmusname, mapheaderinfo[gamemap-1]->muspostbossname, 7);
3950 mapmusname[6] = 0;
3951 mapmusflags = (mapheaderinfo[gamemap-1]->muspostbosstrack & MUSIC_TRACKMASK) | MUSIC_RELOADRESET;
3952 mapmusposition = mapheaderinfo[gamemap-1]->muspostbosspos;
3953 }
3954
3955 // don't change if we're in another tune
3956 // but in case we're in jingle, use our parked mapmus variables so the correct track restores
3957 if (!changed)
3958 S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, (1*MUSICRATE)+(MUSICRATE/2),
3959 mapheaderinfo[gamemap-1]->muspostbossfadein);
3960 }
3961 }
3962
3963 bossjustdie:
3964 if (LUAh_BossDeath(mo))
3965 return;
3966 else if (P_MobjWasRemoved(mo))
3967 return;
3968
3969 // Spawn your junk
3970 switch (mo->type)
3971 {
3972 default:
3973 break;
3974 case MT_EGGMOBILE: // twin laser pods
3975 {
3976 mo2 = P_SpawnMobjFromMobj(mo,
3977 P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<<FRACBITS),
3978 P_ReturnThrustY(mo, mo->angle - ANGLE_90, 32<<FRACBITS),
3979 32<<FRACBITS, MT_BOSSJUNK);
3980 mo2->angle = mo->angle;
3981 P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale);
3982 P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
3983 P_SetMobjState(mo2, S_BOSSEGLZ1);
3984
3985 mo2 = P_SpawnMobjFromMobj(mo,
3986 P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<<FRACBITS),
3987 P_ReturnThrustY(mo, mo->angle + ANGLE_90, 32<<FRACBITS),
3988 32<<FRACBITS, MT_BOSSJUNK);
3989 mo2->angle = mo->angle;
3990 P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale);
3991 P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
3992 P_SetMobjState(mo2, S_BOSSEGLZ2);
3993 }
3994 break;
3995 case MT_EGGMOBILE2: // twin tanks + spigot
3996 {
3997 mo2 = P_SpawnMobjFromMobj(mo,
3998 P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<<FRACBITS),
3999 P_ReturnThrustY(mo, mo->angle - ANGLE_90, 32<<FRACBITS),
4000 32<<FRACBITS, MT_BOSSJUNK);
4001 mo2->angle = mo->angle;
4002 P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale);
4003 P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
4004 P_SetMobjState(mo2, S_BOSSTANK1);
4005
4006 mo2 = P_SpawnMobjFromMobj(mo,
4007 P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<<FRACBITS),
4008 P_ReturnThrustY(mo, mo->angle + ANGLE_90, 32<<FRACBITS),
4009 32<<FRACBITS, MT_BOSSJUNK);
4010 mo2->angle = mo->angle;
4011 P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale);
4012 P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
4013 P_SetMobjState(mo2, S_BOSSTANK2);
4014
4015 mo2 = P_SpawnMobjFromMobj(mo, 0, 0,
4016 mobjinfo[MT_EGGMOBILE2].height + (32<<FRACBITS),
4017 MT_BOSSJUNK);
4018 mo2->angle = mo->angle;
4019 P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
4020 mo2->momz += mo->momz;
4021 P_SetMobjState(mo2, S_BOSSSPIGOT);
4022 }
4023 break;
4024 case MT_EGGMOBILE3:
4025 {
4026 mo2 = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_BOSSJUNK);
4027 mo2->angle = mo->angle;
4028 P_SetMobjState(mo2, S_BOSSSEBH1);
4029 }
4030 break;
4031 }
4032
4033 // now do another switch case for escaping
4034 switch (mo->type)
4035 {
4036 case MT_BLACKEGGMAN:
4037 {
4038 mo->flags |= MF_NOCLIP;
4039 mo->flags &= ~MF_SPECIAL;
4040
4041 S_StartSound(NULL, sfx_befall);
4042 break;
4043 }
4044 case MT_CYBRAKDEMON:
4045 {
4046 mo->flags |= MF_NOCLIP;
4047 mo->flags &= ~(MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
4048
4049 S_StartSound(NULL, sfx_bedie2);
4050 P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_CYBRAKDEMON_VILE_EXPLOSION);
4051 mo->z += P_MobjFlip(mo);
4052 P_SetObjectMomZ(mo, 12*FRACUNIT, false);
4053 S_StartSound(mo, sfx_bgxpld);
4054 if (mo->spawnpoint && !(mo->spawnpoint->options & MTF_EXTRA))
4055 P_InstaThrust(mo, R_PointToAngle2(0, 0, mo->x, mo->y), 14*FRACUNIT);
4056 break;
4057 }
4058 case MT_KOOPA:
4059 {
4060 // Initialize my junk
4061 junk.tags.tags = NULL;
4062 junk.tags.count = 0;
4063
4064 Tag_FSet(&junk.tags, LE_KOOPA);
4065 EV_DoCeiling(&junk, raiseToHighest);
4066 return;
4067 }
4068 case MT_FANG:
4069 {
4070 if (mo->flags2 & MF2_SLIDEPUSH)
4071 {
4072 P_RemoveMobj(mo);
4073 return;
4074 }
4075 if (mo->tracer)
4076 {
4077 var1 = var2 = 0;
4078 A_Boss5Jump(mo);
4079 mo->momx = ((16 - 1)*mo->momx)/16;
4080 mo->momy = ((16 - 1)*mo->momy)/16;
4081 {
4082 const fixed_t time = FixedHypot(mo->tracer->x - mo->x, mo->tracer->y - mo->y)/FixedHypot(mo->momx, mo->momy);
4083 const fixed_t speed = 64*FRACUNIT;
4084 mobj_t *pole = P_SpawnMobj(
4085 mo->tracer->x - P_ReturnThrustX(mo->tracer, mo->tracer->angle, speed*time),
4086 mo->tracer->y - P_ReturnThrustY(mo->tracer, mo->tracer->angle, speed*time),
4087 mo->tracer->floorz + (256+1)*FRACUNIT,
4088 MT_FSGNB);
4089 P_SetTarget(&pole->tracer, P_SpawnMobj(
4090 pole->x, pole->y,
4091 pole->z - 256*FRACUNIT,
4092 MT_FSGNB));
4093 P_SetTarget(&pole->tracer->tracer, P_SpawnMobj(
4094 pole->x + P_ReturnThrustX(pole, mo->tracer->angle, FRACUNIT),
4095 pole->y + P_ReturnThrustY(pole, mo->tracer->angle, FRACUNIT),
4096 pole->z + 256*FRACUNIT,
4097 MT_FSGNA));
4098 pole->tracer->flags |= MF_NOCLIPTHING;
4099 P_SetScale(pole, (pole->destscale = 2*FRACUNIT));
4100 P_SetScale(pole->tracer, (pole->tracer->destscale = 2*FRACUNIT));
4101 pole->angle = pole->tracer->angle = mo->tracer->angle;
4102 pole->tracer->tracer->angle = pole->angle - ANGLE_90;
4103 pole->momx = P_ReturnThrustX(pole, pole->angle, speed);
4104 pole->momy = P_ReturnThrustY(pole, pole->angle, speed);
4105 pole->tracer->momx = pole->momx;
4106 pole->tracer->momy = pole->momy;
4107 pole->tracer->tracer->momx = pole->momx;
4108 pole->tracer->tracer->momy = pole->momy;
4109 }
4110 }
4111 else
4112 {
4113 P_SetObjectMomZ(mo, 10*FRACUNIT, false);
4114 mo->flags |= MF_NOGRAVITY;
4115 }
4116 mo->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
4117 return;
4118 }
4119 default: //eggmobiles
4120 {
4121 UINT8 extrainfo = (mo->spawnpoint ? mo->spawnpoint->extrainfo : 0);
4122
4123 // Stop exploding and prepare to run.
4124 P_SetMobjState(mo, mo->info->xdeathstate);
4125 if (P_MobjWasRemoved(mo))
4126 return;
4127
4128 P_SetTarget(&mo->target, NULL);
4129
4130 // Flee! Flee! Find a point to escape to! If none, just shoot upward!
4131 // scan the thinkers to find the runaway point
4132 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
4133 {
4134 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
4135 continue;
4136
4137 mo2 = (mobj_t *)th;
4138
4139 if (mo2->type != MT_BOSSFLYPOINT)
4140 continue;
4141
4142 if (mo2->spawnpoint && mo2->spawnpoint->extrainfo != extrainfo)
4143 continue;
4144
4145 // If this one's further then the last one, don't go for it.
4146 if (mo->target &&
4147 P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) >
4148 P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z))
4149 continue;
4150
4151 // Otherwise... Do!
4152 P_SetTarget(&mo->target, mo2);
4153 }
4154
4155 mo->flags |= MF_NOGRAVITY|MF_NOCLIP;
4156 mo->flags |= MF_NOCLIPHEIGHT;
4157
4158 mo->movedir = 0;
4159 mo->extravalue1 = 35;
4160 mo->flags2 |= MF2_BOSSFLEE;
4161 mo->momz = P_MobjFlip(mo)*2*mo->scale;
4162
4163 if (mo->target)
4164 {
4165 angle_t diff = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y) - mo->angle;
4166 if (diff)
4167 {
4168 if (diff > ANGLE_180)
4169 diff = InvAngle(InvAngle(diff)/mo->extravalue1);
4170 else
4171 diff /= mo->extravalue1;
4172 mo->movedir = diff;
4173 }
4174 }
4175
4176 break;
4177 }
4178 }
4179 }
4180
4181 // Function: A_CustomPower
4182 //
4183 // Description: Provides a custom powerup. Target (must be a player) is awarded the powerup. Reactiontime of the object is used as an index to the powers array.
4184 //
4185 // var1 = Power index #
4186 // var2 = Power duration in tics
4187 //
A_CustomPower(mobj_t * actor)4188 void A_CustomPower(mobj_t *actor)
4189 {
4190 player_t *player;
4191 INT32 locvar1 = var1;
4192 INT32 locvar2 = var2;
4193 boolean spawnshield = false;
4194
4195 if (LUA_CallAction(A_CUSTOMPOWER, actor))
4196 return;
4197
4198 if (!actor->target || !actor->target->player)
4199 {
4200 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4201 return;
4202 }
4203
4204 if (locvar1 >= NUMPOWERS || locvar1 < 0)
4205 {
4206 CONS_Debug(DBG_GAMELOGIC, "Power #%d out of range!\n", locvar1);
4207 return;
4208 }
4209
4210 player = actor->target->player;
4211
4212 if (locvar1 == pw_shield && player->powers[pw_shield] != locvar2)
4213 spawnshield = true;
4214
4215 player->powers[locvar1] = (UINT16)locvar2;
4216 if (actor->info->seesound)
4217 S_StartSound(player->mo, actor->info->seesound);
4218
4219 if (spawnshield) //workaround for a bug
4220 P_SpawnShieldOrb(player);
4221 }
4222
4223 // Function: A_GiveWeapon
4224 //
4225 // Description: Gives the player the specified weapon panels.
4226 //
4227 // var1 = Weapon index #
4228 // var2 = unused
4229 //
A_GiveWeapon(mobj_t * actor)4230 void A_GiveWeapon(mobj_t *actor)
4231 {
4232 player_t *player;
4233 INT32 locvar1 = var1;
4234
4235 if (LUA_CallAction(A_GIVEWEAPON, actor))
4236 return;
4237
4238 if (!actor->target || !actor->target->player)
4239 {
4240 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4241 return;
4242 }
4243
4244 if (locvar1 >= 1<<(NUM_WEAPONS-1))
4245 {
4246 CONS_Debug(DBG_GAMELOGIC, "Weapon #%d out of range!\n", locvar1);
4247 return;
4248 }
4249
4250 player = actor->target->player;
4251
4252 player->ringweapons |= locvar1;
4253 if (actor->info->seesound)
4254 S_StartSound(player->mo, actor->info->seesound);
4255 }
4256
4257 // Function: A_RingBox
4258 //
4259 // Description: Awards the player 10 rings.
4260 //
4261 // var1 = unused
4262 // var2 = unused
4263 //
A_RingBox(mobj_t * actor)4264 void A_RingBox(mobj_t *actor)
4265 {
4266 player_t *player;
4267
4268 if (LUA_CallAction(A_RINGBOX, actor))
4269 return;
4270
4271 if (!actor->target || !actor->target->player)
4272 {
4273 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4274 return;
4275 }
4276
4277 player = actor->target->player;
4278
4279 P_GivePlayerRings(player, actor->info->reactiontime);
4280 if (actor->info->seesound)
4281 S_StartSound(player->mo, actor->info->seesound);
4282 }
4283
4284 // Function: A_Invincibility
4285 //
4286 // Description: Awards the player invincibility.
4287 //
4288 // var1 = unused
4289 // var2 = unused
4290 //
A_Invincibility(mobj_t * actor)4291 void A_Invincibility(mobj_t *actor)
4292 {
4293 player_t *player;
4294
4295 if (LUA_CallAction(A_INVINCIBILITY, actor))
4296 return;
4297
4298 if (!actor->target || !actor->target->player)
4299 {
4300 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4301 return;
4302 }
4303
4304 player = actor->target->player;
4305 player->powers[pw_invulnerability] = invulntics + 1;
4306
4307 if (P_IsLocalPlayer(player) && !player->powers[pw_super])
4308 {
4309 if (mariomode)
4310 G_GhostAddColor(GHC_INVINCIBLE);
4311 P_PlayJingle(player, (mariomode) ? JT_MINV : JT_INV);
4312 strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
4313 S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
4314 }
4315 }
4316
4317 // Function: A_SuperSneakers
4318 //
4319 // Description: Awards the player super sneakers.
4320 //
4321 // var1 = unused
4322 // var2 = unused
4323 //
A_SuperSneakers(mobj_t * actor)4324 void A_SuperSneakers(mobj_t *actor)
4325 {
4326 player_t *player;
4327
4328 if (LUA_CallAction(A_SUPERSNEAKERS, actor))
4329 return;
4330
4331 if (!actor->target || !actor->target->player)
4332 {
4333 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4334 return;
4335 }
4336
4337 player = actor->target->player;
4338
4339 actor->target->player->powers[pw_sneakers] = sneakertics + 1;
4340
4341 if (P_IsLocalPlayer(player) && !player->powers[pw_super])
4342 {
4343 if (S_SpeedMusic(0.0f) && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC))
4344 S_SpeedMusic(1.4f);
4345 else
4346 P_PlayJingle(player, JT_SHOES);
4347 strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
4348 S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]);
4349 }
4350 }
4351
4352 // Function: A_AwardScore
4353 //
4354 // Description: Adds a set amount of points to the player's score.
4355 //
4356 // var1 = unused
4357 // var2 = unused
4358 //
A_AwardScore(mobj_t * actor)4359 void A_AwardScore(mobj_t *actor)
4360 {
4361 player_t *player;
4362
4363 if (LUA_CallAction(A_AWARDSCORE, actor))
4364 return;
4365
4366 if (!actor->target || !actor->target->player)
4367 {
4368 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4369 return;
4370 }
4371
4372 player = actor->target->player;
4373
4374 P_AddPlayerScore(player, actor->info->reactiontime);
4375 if (actor->info->seesound)
4376 S_StartSound(player->mo, actor->info->seesound);
4377 }
4378
4379 // Function: A_ExtraLife
4380 //
4381 // Description: Awards the player an extra life.
4382 //
4383 // var1 = unused
4384 // var2 = unused
4385 //
A_ExtraLife(mobj_t * actor)4386 void A_ExtraLife(mobj_t *actor)
4387 {
4388 player_t *player;
4389
4390 if (LUA_CallAction(A_EXTRALIFE, actor))
4391 return;
4392
4393 if (!actor->target || !actor->target->player)
4394 {
4395 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4396 return;
4397 }
4398
4399 player = actor->target->player;
4400
4401 if (actor->type == MT_1UP_ICON && actor->tracer)
4402 {
4403 // We're using the overlay, so use the overlay 1up sprite (no text)
4404 actor->sprite = SPR_TV1P;
4405 }
4406
4407 if (ultimatemode) //I don't THINK so!
4408 {
4409 S_StartSound(player->mo, sfx_lose);
4410 return;
4411 }
4412
4413 P_GiveCoopLives(player, 1, true);
4414 }
4415
4416 // Function: A_GiveShield
4417 //
4418 // Description: Awards the player a specified shield.
4419 //
4420 // var1 = Shield type (make with SH_ constants)
4421 // var2 = unused
4422 //
A_GiveShield(mobj_t * actor)4423 void A_GiveShield(mobj_t *actor)
4424 {
4425 player_t *player;
4426 UINT16 locvar1 = var1;
4427
4428 if (LUA_CallAction(A_GIVESHIELD, actor))
4429 return;
4430
4431 if (!actor->target || !actor->target->player)
4432 {
4433 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4434 return;
4435 }
4436
4437 player = actor->target->player;
4438
4439 P_SwitchShield(player, locvar1);
4440 S_StartSound(player->mo, actor->info->seesound);
4441 }
4442
4443 // Function: A_GravityBox
4444 //
4445 // Description: Awards the player gravity boots.
4446 //
4447 // var1 = unused
4448 // var2 = unused
4449 //
A_GravityBox(mobj_t * actor)4450 void A_GravityBox(mobj_t *actor)
4451 {
4452 player_t *player;
4453
4454 if (LUA_CallAction(A_GRAVITYBOX, actor))
4455 return;
4456
4457 if (!actor->target || !actor->target->player)
4458 {
4459 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
4460 return;
4461 }
4462
4463 player = actor->target->player;
4464
4465 S_StartSound(player, actor->info->activesound);
4466
4467 player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
4468 }
4469
4470 // Function: A_ScoreRise
4471 //
4472 // Description: Makes the little score logos rise. Speed value sets speed.
4473 //
4474 // var1 = unused
4475 // var2 = unused
4476 //
A_ScoreRise(mobj_t * actor)4477 void A_ScoreRise(mobj_t *actor)
4478 {
4479 if (LUA_CallAction(A_SCORERISE, actor))
4480 return;
4481
4482 // make logo rise!
4483 P_SetObjectMomZ(actor, actor->info->speed, false);
4484 }
4485
4486 // Function: A_BunnyHop
4487 //
4488 // Description: Makes object hop like a bunny.
4489 //
4490 // var1 = jump strength
4491 // var2 = horizontal movement
4492 //
A_BunnyHop(mobj_t * actor)4493 void A_BunnyHop(mobj_t *actor)
4494 {
4495 INT32 locvar1 = var1;
4496 INT32 locvar2 = var2;
4497
4498 if (LUA_CallAction(A_BUNNYHOP, actor))
4499 return;
4500
4501 if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
4502 || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
4503 {
4504 P_SetObjectMomZ(actor, locvar1*FRACUNIT, false);
4505 P_InstaThrust(actor, actor->angle, FixedMul(locvar2*FRACUNIT, actor->scale)); // Launch the hopping action! PHOOM!!
4506 }
4507 }
4508
4509 // Function: A_BubbleSpawn
4510 //
4511 // Description: Spawns a randomly sized bubble from the object's location. Only works underwater.
4512 //
4513 // var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
4514 // var2 = unused
4515 //
A_BubbleSpawn(mobj_t * actor)4516 void A_BubbleSpawn(mobj_t *actor)
4517 {
4518 INT32 i, locvar1 = var1;
4519 UINT8 prandom;
4520 mobj_t *bubble = NULL;
4521
4522 if (LUA_CallAction(A_BUBBLESPAWN, actor))
4523 return;
4524
4525 if (!(actor->eflags & MFE_UNDERWATER))
4526 {
4527 // Don't draw or spawn bubbles above water
4528 actor->flags2 |= MF2_DONTDRAW;
4529 return;
4530 }
4531 actor->flags2 &= ~MF2_DONTDRAW;
4532
4533 if (!(actor->flags2 & MF2_AMBUSH))
4534 {
4535 // Quick! Look through players!
4536 // Don't spawn bubbles unless a player is relatively close by (var1).
4537 for (i = 0; i < MAXPLAYERS; ++i)
4538 if (playeringame[i] && players[i].mo
4539 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<<FRACBITS))
4540 break; // Stop looking.
4541 if (i == MAXPLAYERS)
4542 return; // don't make bubble!
4543 }
4544
4545 prandom = P_RandomByte();
4546
4547 if (leveltime % (3*TICRATE) < 8)
4548 bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_EXTRALARGEBUBBLE);
4549 else if (prandom > 128)
4550 bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_SMALLBUBBLE);
4551 else if (prandom < 128 && prandom > 96)
4552 bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_MEDIUMBUBBLE);
4553
4554 if (bubble)
4555 {
4556 bubble->destscale = actor->scale;
4557 P_SetScale(bubble, actor->scale);
4558 }
4559 }
4560
4561 // Function: A_FanBubbleSpawn
4562 //
4563 // Description: Spawns bubbles from fans, if they're underwater.
4564 //
4565 // var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
4566 // var2 = unused
4567 //
A_FanBubbleSpawn(mobj_t * actor)4568 void A_FanBubbleSpawn(mobj_t *actor)
4569 {
4570 INT32 i, locvar1 = var1;
4571 UINT8 prandom;
4572 mobj_t *bubble = NULL;
4573 fixed_t hz = actor->z + (4*actor->height)/5;
4574
4575 if (LUA_CallAction(A_FANBUBBLESPAWN, actor))
4576 return;
4577
4578 if (!(actor->eflags & MFE_UNDERWATER))
4579 return;
4580
4581 if (!(actor->flags2 & MF2_AMBUSH))
4582 {
4583 // Quick! Look through players!
4584 // Don't spawn bubbles unless a player is relatively close by (var2).
4585 for (i = 0; i < MAXPLAYERS; ++i)
4586 if (playeringame[i] && players[i].mo
4587 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<<FRACBITS))
4588 break; // Stop looking.
4589 if (i == MAXPLAYERS)
4590 return; // don't make bubble!
4591 }
4592
4593 prandom = P_RandomByte();
4594
4595 if ((prandom & 0x7) == 0x7)
4596 bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_SMALLBUBBLE);
4597 else if ((prandom & 0xF0) == 0xF0)
4598 bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_MEDIUMBUBBLE);
4599
4600 if (bubble)
4601 {
4602 bubble->destscale = actor->scale;
4603 P_SetScale(bubble, actor->scale);
4604 }
4605 }
4606
4607 // Function: A_BubbleRise
4608 //
4609 // Description: Raises a bubble
4610 //
4611 // var1:
4612 // 0 = Bend around the water abit, looking more realistic
4613 // 1 = Rise straight up
4614 // var2 = rising speed
4615 //
A_BubbleRise(mobj_t * actor)4616 void A_BubbleRise(mobj_t *actor)
4617 {
4618 INT32 locvar1 = var1;
4619 INT32 locvar2 = var2;
4620
4621 if (LUA_CallAction(A_BUBBLERISE, actor))
4622 return;
4623
4624 if (actor->type == MT_EXTRALARGEBUBBLE)
4625 P_SetObjectMomZ(actor, FixedDiv(6*FRACUNIT,5*FRACUNIT), false); // make bubbles rise!
4626 else
4627 {
4628 P_SetObjectMomZ(actor, locvar2, true); // make bubbles rise!
4629
4630 // Move around slightly to make it look like it's bending around the water
4631 if (!locvar1)
4632 {
4633 UINT8 prandom = P_RandomByte();
4634 if (!(prandom & 0x7)) // *****000
4635 {
4636 P_InstaThrust(actor, prandom & 0x70 ? actor->angle + ANGLE_90 : actor->angle,
4637 FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
4638 }
4639 else if (!(prandom & 0x38)) // **000***
4640 {
4641 P_InstaThrust(actor, prandom & 0x70 ? actor->angle - ANGLE_90 : actor->angle - ANGLE_180,
4642 FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
4643 }
4644 }
4645 }
4646 }
4647
4648 // Function: A_BubbleCheck
4649 //
4650 // Description: Checks if a bubble should be drawn or not. Bubbles are not drawn above water.
4651 //
4652 // var1 = unused
4653 // var2 = unused
4654 //
A_BubbleCheck(mobj_t * actor)4655 void A_BubbleCheck(mobj_t *actor)
4656 {
4657 if (LUA_CallAction(A_BUBBLECHECK, actor))
4658 return;
4659
4660 if (actor->eflags & MFE_UNDERWATER)
4661 actor->flags2 &= ~MF2_DONTDRAW; // underwater so draw
4662 else
4663 actor->flags2 |= MF2_DONTDRAW; // above water so don't draw
4664 }
4665
4666 // Function: A_AttractChase
4667 //
4668 // Description: Makes a ring chase after a player with a ring shield and also causes spilled rings to flicker.
4669 //
4670 // var1 = unused
4671 // var2 = unused
4672 //
A_AttractChase(mobj_t * actor)4673 void A_AttractChase(mobj_t *actor)
4674 {
4675 if (LUA_CallAction(A_ATTRACTCHASE, actor))
4676 return;
4677
4678 if (actor->flags2 & MF2_NIGHTSPULL || !actor->health)
4679 return;
4680
4681 // spilled rings flicker before disappearing
4682 if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE)
4683 actor->flags2 |= MF2_DONTDRAW;
4684 else
4685 actor->flags2 &= ~MF2_DONTDRAW;
4686
4687 // Turn flingrings back into regular rings if attracted.
4688 if (actor->tracer && actor->tracer->player
4689 && !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
4690 {
4691 mobj_t *newring;
4692 newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
4693 newring->momx = actor->momx;
4694 newring->momy = actor->momy;
4695 newring->momz = actor->momz;
4696 P_RemoveMobj(actor);
4697 return;
4698 }
4699
4700 P_LookForShield(actor); // Go find 'em, boy!
4701
4702 if (!actor->tracer
4703 || !actor->tracer->player
4704 || !actor->tracer->health
4705 || !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta
4706 {
4707 // Lost attracted rings don't through walls anymore.
4708 actor->flags &= ~MF_NOCLIP;
4709 P_SetTarget(&actor->tracer, NULL);
4710 return;
4711 }
4712
4713 // If a FlingRing gets attracted by a shield, change it into a normal ring.
4714 if (actor->type == (mobjtype_t)actor->info->reactiontime)
4715 {
4716 P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance);
4717 P_RemoveMobj(actor);
4718 return;
4719 }
4720
4721 // Keep stuff from going down inside floors and junk
4722 actor->flags &= ~MF_NOCLIPHEIGHT;
4723
4724 // Let attracted rings move through walls and such.
4725 actor->flags |= MF_NOCLIP;
4726
4727 P_Attract(actor, actor->tracer, false);
4728 }
4729
4730 // Function: A_DropMine
4731 //
4732 // Description: Drops a mine. Raisestate specifies the object # to use for the mine.
4733 //
4734 // var1 = height offset
4735 // var2:
4736 // lower 16 bits = proximity check distance (0 disables)
4737 // upper 16 bits = 0 to check proximity with target, 1 for tracer
4738 //
A_DropMine(mobj_t * actor)4739 void A_DropMine(mobj_t *actor)
4740 {
4741 INT32 locvar1 = var1;
4742 INT32 locvar2 = var2;
4743 fixed_t z;
4744 mobj_t *mine;
4745
4746 if (LUA_CallAction(A_DROPMINE, actor))
4747 return;
4748
4749 if (locvar2 & 65535)
4750 {
4751 fixed_t dist;
4752 mobj_t *target;
4753
4754 if (locvar2 >> 16)
4755 target = actor->tracer;
4756 else
4757 target = actor->target;
4758
4759 if (!target)
4760 return;
4761
4762 dist = P_AproxDistance(actor->x-target->x, actor->y-target->y)>>FRACBITS;
4763
4764 if (dist > FixedMul((locvar2 & 65535), actor->scale))
4765 return;
4766 }
4767
4768 if (actor->eflags & MFE_VERTICALFLIP)
4769 z = actor->z + actor->height - mobjinfo[actor->info->raisestate].height - FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
4770 else
4771 z = actor->z + FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
4772
4773 // Use raisestate instead of MT_MINE
4774 mine = P_SpawnMobj(actor->x, actor->y, z, (mobjtype_t)actor->info->raisestate);
4775 if (actor->eflags & MFE_VERTICALFLIP)
4776 mine->eflags |= MFE_VERTICALFLIP;
4777 mine->momz = actor->momz + actor->pmomz;
4778
4779 S_StartSound(actor, actor->info->attacksound);
4780 }
4781
4782 // Function: A_FishJump
4783 //
4784 // Description: Makes the stupid harmless fish in Greenflower Zone jump.
4785 //
4786 // var1 = Jump strength (in FRACBITS), if specified. Otherwise, uses the angle value.
4787 // var2 = Trail object to spawn, if desired.
4788 //
A_FishJump(mobj_t * actor)4789 void A_FishJump(mobj_t *actor)
4790 {
4791 INT32 locvar1 = var1;
4792 INT32 locvar2 = var2;
4793
4794 if (LUA_CallAction(A_FISHJUMP, actor))
4795 return;
4796
4797 if (locvar2)
4798 {
4799 UINT8 i;
4800 // Don't spawn trail unless a player is nearby.
4801 for (i = 0; i < MAXPLAYERS; ++i)
4802 if (playeringame[i] && players[i].mo
4803 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (actor->info->speed))
4804 break; // Stop looking.
4805 if (i < MAXPLAYERS)
4806 {
4807 fixed_t rad = actor->radius>>FRACBITS;
4808 P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<<FRACBITS, P_RandomRange(rad, -rad)<<FRACBITS, 0, (mobjtype_t)locvar2);
4809 }
4810 }
4811
4812 if ((actor->z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale)))
4813 {
4814 fixed_t jumpval;
4815
4816 if (locvar1)
4817 jumpval = var1;
4818 else
4819 jumpval = FixedMul(AngleFixed(actor->angle)/4, actor->scale);
4820
4821 if (!jumpval) jumpval = FixedMul(44*(FRACUNIT/4), actor->scale);
4822 actor->momz = jumpval;
4823 P_SetMobjStateNF(actor, actor->info->seestate);
4824 }
4825
4826 if (actor->momz < 0
4827 && (actor->state < &states[actor->info->meleestate] || actor->state > &states[actor->info->xdeathstate]))
4828 P_SetMobjStateNF(actor, actor->info->meleestate);
4829 }
4830
4831 // Function:A_ThrownRing
4832 //
4833 // Description: Thinker for thrown rings/sparkle trail
4834 //
4835 // var1 = unused
4836 // var2 = unused
4837 //
A_ThrownRing(mobj_t * actor)4838 void A_ThrownRing(mobj_t *actor)
4839 {
4840 INT32 c = 0;
4841 INT32 stop;
4842 player_t *player;
4843 fixed_t dist;
4844
4845 if (LUA_CallAction(A_THROWNRING, actor))
4846 return;
4847
4848 if (leveltime % (TICRATE/7) == 0)
4849 {
4850 mobj_t *ring = NULL;
4851
4852 if (actor->flags2 & MF2_EXPLOSION)
4853 {
4854 if (actor->momx != 0 || actor->momy != 0)
4855 ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE);
4856 // Else spawn nothing because it's totally stationary and constantly smoking would be weird -SH
4857 }
4858 else if (actor->flags2 & MF2_AUTOMATIC)
4859 ring = P_SpawnGhostMobj(actor);
4860 else if (!(actor->flags2 & MF2_RAILRING))
4861 ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPARK);
4862
4863 if (ring)
4864 {
4865 /*
4866 P_SetTarget(&ring->target, actor);
4867 ring->color = actor->color; //copy color
4868 */
4869 ring->destscale = actor->scale;
4870 P_SetScale(ring, actor->scale);
4871 }
4872 }
4873
4874 // A_GrenadeRing beeping lives once moooooore -SH
4875 if (actor->type == MT_THROWNGRENADE && actor->fuse % TICRATE == 0)
4876 S_StartSound(actor, actor->info->attacksound);
4877
4878 // decrement bounce ring time
4879 if (actor->flags2 & MF2_BOUNCERING)
4880 {
4881 if (actor->fuse)
4882 actor->fuse--;
4883 else {
4884 P_RemoveMobj(actor);
4885 return;
4886 }
4887 }
4888
4889 // spilled rings (and thrown bounce) flicker before disappearing
4890 if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE
4891 && actor->type != MT_THROWNGRENADE)
4892 actor->flags2 |= MF2_DONTDRAW;
4893 else
4894 actor->flags2 &= ~MF2_DONTDRAW;
4895
4896 if (actor->tracer && actor->tracer->health <= 0)
4897 P_SetTarget(&actor->tracer, NULL);
4898
4899 // Updated homing ring special capability
4900 // If you have a ring shield, all rings thrown
4901 // at you become homing (except rail)!
4902 if (actor->tracer)
4903 {
4904 // A non-homing ring getting attracted by a
4905 // magnetic player. If he gets too far away, make
4906 // sure to stop the attraction!
4907 if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
4908 && P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
4909 actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
4910 {
4911 P_SetTarget(&actor->tracer, NULL);
4912 }
4913
4914 if (actor->tracer && (actor->tracer->health)
4915 && (actor->tracer->player && actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
4916 {
4917 const INT32 temp = actor->threshold;
4918 actor->threshold = 32000;
4919 P_HomingAttack(actor, actor->tracer);
4920 actor->threshold = temp;
4921 return;
4922 }
4923 }
4924
4925 // first time init, this allow minimum lastlook changes
4926 if (actor->lastlook < 0)
4927 actor->lastlook = P_RandomByte();
4928
4929 actor->lastlook %= MAXPLAYERS;
4930
4931 stop = (actor->lastlook - 1) & PLAYERSMASK;
4932
4933 for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
4934 {
4935 // done looking
4936 if (actor->lastlook == stop)
4937 return;
4938
4939 if (!playeringame[actor->lastlook])
4940 continue;
4941
4942 if (c++ == 2)
4943 return;
4944
4945 player = &players[actor->lastlook];
4946
4947 if (!player->mo)
4948 continue;
4949
4950 if (player->mo->health <= 0)
4951 continue; // dead
4952
4953 if ((netgame || multiplayer) && player->spectator)
4954 continue; // spectator
4955
4956 if (actor->target && actor->target->player)
4957 {
4958 if (player->mo == actor->target)
4959 continue;
4960
4961 // Don't home in on teammates.
4962 if ((gametyperules & GTR_TEAMS)
4963 && actor->target->player->ctfteam == player->ctfteam)
4964 continue;
4965 }
4966
4967 dist = P_AproxDistance(P_AproxDistance(player->mo->x-actor->x,
4968 player->mo->y-actor->y), player->mo->z-actor->z);
4969
4970 // check distance
4971 if (actor->flags2 & MF2_RAILRING)
4972 {
4973 if (dist > FixedMul(RING_DIST/2, player->mo->scale))
4974 continue;
4975 }
4976 else if (dist > FixedMul(RING_DIST, player->mo->scale))
4977 continue;
4978
4979 // do this after distance check because it's more computationally expensive
4980 if (!P_CheckSight(actor, player->mo))
4981 continue; // out of sight
4982
4983 if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
4984 && dist < FixedMul(RING_DIST/4, player->mo->scale))
4985 P_SetTarget(&actor->tracer, player->mo);
4986 return;
4987 }
4988
4989 return;
4990 }
4991
4992 // Function: A_SetSolidSteam
4993 //
4994 // Description: Makes steam solid so it collides with the player to boost them.
4995 //
4996 // var1 = unused
4997 // var2 = unused
4998 //
A_SetSolidSteam(mobj_t * actor)4999 void A_SetSolidSteam(mobj_t *actor)
5000 {
5001 if (LUA_CallAction(A_SETSOLIDSTEAM, actor))
5002 return;
5003
5004 actor->flags &= ~MF_NOCLIP;
5005 actor->flags |= MF_SOLID;
5006 if (!(actor->flags2 & MF2_AMBUSH))
5007 {
5008 if (P_RandomChance(FRACUNIT/8))
5009 {
5010 if (actor->info->deathsound)
5011 S_StartSound(actor, actor->info->deathsound); // Hiss!
5012 }
5013 else
5014 {
5015 if (actor->info->painsound)
5016 S_StartSound(actor, actor->info->painsound);
5017 }
5018 }
5019
5020 P_SetObjectMomZ (actor, 1, true);
5021 }
5022
5023 // Function: A_UnsetSolidSteam
5024 //
5025 // Description: Makes an object non-solid and also noclip. Used by the steam.
5026 //
5027 // var1 = unused
5028 // var2 = unused
5029 //
A_UnsetSolidSteam(mobj_t * actor)5030 void A_UnsetSolidSteam(mobj_t *actor)
5031 {
5032 if (LUA_CallAction(A_UNSETSOLIDSTEAM, actor))
5033 return;
5034
5035 actor->flags &= ~MF_SOLID;
5036 actor->flags |= MF_NOCLIP;
5037 }
5038
5039 // Function: A_SignSpin
5040 //
5041 // Description: Spins a signpost until it hits the ground and reaches its mapthing's angle.
5042 //
5043 // var1 = degrees to rotate object (must be positive, because I'm lazy)
5044 // var2 = unused
5045 //
A_SignSpin(mobj_t * actor)5046 void A_SignSpin(mobj_t *actor)
5047 {
5048 INT32 locvar1 = var1;
5049 INT16 i;
5050 angle_t rotateangle = FixedAngle(locvar1 << FRACBITS);
5051
5052 if (LUA_CallAction(A_SIGNSPIN, actor))
5053 return;
5054
5055 if (P_IsObjectOnGround(actor) && P_MobjFlip(actor) * actor->momz <= 0)
5056 {
5057 if (actor->flags2 & MF2_BOSSFLEE)
5058 {
5059 S_StartSound(actor, actor->info->deathsound);
5060 actor->flags2 &= ~MF2_BOSSFLEE;
5061 }
5062 if (actor->spawnpoint)
5063 {
5064 angle_t mapangle = FixedAngle(actor->spawnpoint->angle << FRACBITS);
5065 angle_t diff = mapangle - actor->angle;
5066 if (diff < ANG2)
5067 {
5068 actor->angle = mapangle;
5069 P_SetMobjState(actor, actor->info->deathstate);
5070 return;
5071 }
5072 if ((statenum_t)(actor->state-states) != actor->info->painstate)
5073 P_SetMobjState(actor, actor->info->painstate);
5074 actor->movedir = min((mapangle - actor->angle) >> 2, actor->movedir);
5075 }
5076 else // no mapthing? just finish in your current angle
5077 {
5078 P_SetMobjState(actor, actor->info->deathstate);
5079 return;
5080 }
5081 }
5082 else
5083 {
5084 if (!(actor->flags2 & MF2_BOSSFLEE))
5085 {
5086 S_StartSound(actor, actor->info->painsound);
5087 actor->flags2 |= MF2_BOSSFLEE;
5088 }
5089 actor->movedir = rotateangle;
5090 }
5091
5092 actor->angle += actor->movedir;
5093 if (actor->tracer == NULL || P_MobjWasRemoved(actor->tracer)) return;
5094 for (i = -1; i < 2; i += 2)
5095 {
5096 P_SpawnMobjFromMobj(actor,
5097 P_ReturnThrustX(actor, actor->tracer->angle, i * actor->radius),
5098 P_ReturnThrustY(actor, actor->tracer->angle, i * actor->radius),
5099 (actor->eflags & MFE_VERTICALFLIP) ? 0 : actor->height,
5100 actor->info->painchance)->destscale >>= 1;
5101 }
5102 }
5103
5104 // Function: A_SignPlayer
5105 //
5106 // Description: Changes the state of a level end sign to reflect the player that hit it.
5107 // Also used to display Eggman or the skin roulette whilst spinning.
5108 //
5109 // var1 = number of skin to display (e.g. 2 = Knuckles; special cases: -1 = target's skin, -2 = skin roulette, -3 = Eggman)
5110 // var2 = custom sign color, if desired.
5111 //
A_SignPlayer(mobj_t * actor)5112 void A_SignPlayer(mobj_t *actor)
5113 {
5114 INT32 locvar1 = var1;
5115 INT32 locvar2 = var2;
5116 skin_t *skin = NULL;
5117 mobj_t *ov;
5118 UINT16 facecolor, signcolor = 0;
5119 UINT32 signframe = states[actor->info->raisestate].frame;
5120
5121 facecolor = signcolor = (UINT16)locvar2;
5122
5123 if (LUA_CallAction(A_SIGNPLAYER, actor))
5124 return;
5125
5126 if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins || signcolor >= numskincolors)
5127 return;
5128
5129 // if no face overlay, spawn one
5130 if (actor->tracer->tracer == NULL || P_MobjWasRemoved(actor->tracer->tracer))
5131 {
5132 ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
5133 P_SetTarget(&ov->target, actor->tracer);
5134 P_SetTarget(&actor->tracer->tracer, ov);
5135 }
5136 else
5137 ov = actor->tracer->tracer;
5138
5139 if (locvar1 == -1) // set to target's skin
5140 {
5141 if (!actor->target)
5142 return;
5143
5144 if (!actor->target->player)
5145 return;
5146
5147 skin = &skins[actor->target->player->skin];
5148 facecolor = actor->target->player->skincolor;
5149
5150 if (signcolor)
5151 ;
5152 else if (!skin->sprites[SPR2_SIGN].numframes)
5153 signcolor = facecolor;
5154 else if ((facecolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor?
5155 signcolor = skin->prefoppositecolor;
5156 else if (facecolor) // Set the sign to be an appropriate background color for this player's skincolor.
5157 signcolor = skincolors[facecolor].invcolor;
5158 }
5159 else if (locvar1 != -3) // set to a defined skin
5160 {
5161 // I turned this function into a fucking mess. I'm so sorry. -Lach
5162 if (locvar1 == -2) // random skin
5163 {
5164 #define skincheck(num) (player ? !R_SkinUsable(player-players, num) : skins[num].availability > 0)
5165 player_t *player = actor->target ? actor->target->player : NULL;
5166 UINT8 skinnum;
5167 UINT8 skincount = 0;
5168 for (skinnum = 0; skinnum < numskins; skinnum++)
5169 if (!skincheck(skinnum))
5170 skincount++;
5171 skinnum = P_RandomKey(skincount);
5172 for (skincount = 0; skincount < numskins; skincount++)
5173 {
5174 if (skincount > skinnum)
5175 break;
5176 if (skincheck(skincount))
5177 skinnum++;
5178 }
5179 skin = &skins[skinnum];
5180 #undef skincheck
5181 }
5182 else // specific skin
5183 skin = &skins[locvar1];
5184
5185 facecolor = skin->prefcolor;
5186 if (signcolor)
5187 ;
5188 else if (!skin->sprites[SPR2_SIGN].numframes)
5189 signcolor = facecolor;
5190 else if (skin->prefoppositecolor)
5191 signcolor = skin->prefoppositecolor;
5192 else if (facecolor)
5193 signcolor = skincolors[facecolor].invcolor;
5194 }
5195
5196 if (skin)
5197 {
5198 if (skin->sprites[SPR2_SIGN].numframes) // player face
5199 {
5200 ov->color = facecolor;
5201 ov->skin = skin;
5202 if ((statenum_t)(ov->state-states) != actor->info->seestate)
5203 P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN
5204 }
5205 else // CLEAR! sign
5206 {
5207 ov->color = SKINCOLOR_NONE;
5208 ov->skin = NULL; // needs to be NULL in the case of SF_HIRES characters
5209 if ((statenum_t)(ov->state-states) != actor->info->missilestate)
5210 P_SetMobjState(ov, actor->info->missilestate); // S_CLEARSIGN
5211 }
5212 }
5213 else // Eggman face
5214 {
5215 ov->color = SKINCOLOR_NONE;
5216 ov->skin = NULL;
5217 if ((statenum_t)(ov->state-states) != actor->info->meleestate)
5218 P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN
5219 if (!signcolor)
5220 signcolor = SKINCOLOR_CARBON;
5221 facecolor = signcolor;
5222 }
5223
5224 actor->tracer->color = signcolor;
5225 if (signcolor && signcolor < numskincolors)
5226 signframe += (15 - skincolors[skincolors[signcolor].invcolor].invshade);
5227 actor->tracer->frame = signframe;
5228 }
5229
5230 // Function: A_OverlayThink
5231 //
5232 // Description: Moves the overlay to the position of its target.
5233 //
5234 // var1 = unused
5235 // var2 = invert, z offset
5236 //
A_OverlayThink(mobj_t * actor)5237 void A_OverlayThink(mobj_t *actor)
5238 {
5239 fixed_t destx, desty;
5240
5241 if (LUA_CallAction(A_OVERLAYTHINK, actor))
5242 return;
5243
5244 if (!actor->target)
5245 return;
5246
5247 if (!splitscreen && rendermode != render_soft)
5248 {
5249 angle_t viewingangle;
5250
5251 if (players[displayplayer].awayviewtics)
5252 viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y);
5253 else if (!camera.chase && players[displayplayer].mo)
5254 viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y);
5255 else
5256 viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera.x, camera.y);
5257
5258 destx = actor->target->x + P_ReturnThrustX(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
5259 desty = actor->target->y + P_ReturnThrustY(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
5260 }
5261 else
5262 {
5263 destx = actor->target->x;
5264 desty = actor->target->y;
5265 }
5266 P_UnsetThingPosition(actor);
5267 actor->x = destx;
5268 actor->y = desty;
5269 P_SetThingPosition(actor);
5270 if (actor->eflags & MFE_VERTICALFLIP)
5271 actor->z = actor->target->z + actor->target->height - mobjinfo[actor->type].height - ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
5272 else
5273 actor->z = actor->target->z + ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
5274 actor->angle = actor->target->angle + actor->movedir;
5275 actor->eflags = actor->target->eflags;
5276
5277 actor->momx = actor->target->momx;
5278 actor->momy = actor->target->momy;
5279 actor->momz = actor->target->momz; // assume target has correct momz! Do not use P_SetObjectMomZ!
5280 }
5281
5282 // Function: A_JetChase
5283 //
5284 // Description: A_Chase for Jettysyns
5285 //
5286 // var1 = unused
5287 // var2 = unused
5288 //
A_JetChase(mobj_t * actor)5289 void A_JetChase(mobj_t *actor)
5290 {
5291 fixed_t thefloor;
5292
5293 if (LUA_CallAction(A_JETCHASE, actor))
5294 return;
5295
5296 if (actor->flags2 & MF2_AMBUSH)
5297 return;
5298
5299 if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
5300 && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
5301 thefloor = actor->watertop;
5302 else
5303 thefloor = actor->floorz;
5304
5305 if (actor->reactiontime)
5306 actor->reactiontime--;
5307
5308 if (P_RandomChance(FRACUNIT/32))
5309 {
5310 actor->momx = actor->momx / 2;
5311 actor->momy = actor->momy / 2;
5312 actor->momz = actor->momz / 2;
5313 }
5314
5315 // Bounce if too close to floor or ceiling -
5316 // ideal for Jetty-Syns above you on 3d floors
5317 if (actor->momz && ((actor->z - FixedMul((32<<FRACBITS), actor->scale)) < thefloor) && !((thefloor + FixedMul(32*FRACUNIT, actor->scale) + actor->height) > actor->ceilingz))
5318 actor->momz = -actor->momz/2;
5319
5320 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
5321 {
5322 // look for a new target
5323 if (P_LookForPlayers(actor, true, false, 0))
5324 return; // got a new target
5325
5326 actor->momx = actor->momy = actor->momz = 0;
5327 P_SetMobjState(actor, actor->info->spawnstate);
5328 return;
5329 }
5330
5331 // modify target threshold
5332 if (actor->threshold)
5333 {
5334 if (!actor->target || actor->target->health <= 0)
5335 actor->threshold = 0;
5336 else
5337 actor->threshold--;
5338 }
5339
5340 // turn towards movement direction if not there yet
5341 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
5342
5343 if ((multiplayer || netgame) && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)))
5344 if (P_LookForPlayers(actor, true, false, 0))
5345 return; // got a new target
5346
5347 // If the player is over 3072 fracunits away, then look for another player
5348 if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
5349 actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
5350 {
5351 return; // got a new target
5352 }
5353
5354 // chase towards player
5355 if (ultimatemode)
5356 P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/2, actor->scale));
5357 else
5358 P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/4, actor->scale));
5359
5360 // must adjust height
5361 if (ultimatemode)
5362 {
5363 if (actor->z < (actor->target->z + actor->target->height + FixedMul((64<<FRACBITS), actor->scale)))
5364 actor->momz += FixedMul(FRACUNIT/2, actor->scale);
5365 else
5366 actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
5367 }
5368 else
5369 {
5370 if (actor->z < (actor->target->z + actor->target->height + FixedMul((32<<FRACBITS), actor->scale)))
5371 actor->momz += FixedMul(FRACUNIT/2, actor->scale);
5372 else
5373 actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
5374 }
5375 }
5376
5377 // Function: A_JetbThink
5378 //
5379 // Description: Thinker for Jetty-Syn bombers
5380 //
5381 // var1 = unused
5382 // var2 = unused
5383 //
A_JetbThink(mobj_t * actor)5384 void A_JetbThink(mobj_t *actor)
5385 {
5386 sector_t *nextsector;
5387 fixed_t thefloor;
5388
5389 if (LUA_CallAction(A_JETBTHINK, actor))
5390 return;
5391
5392 if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
5393 && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
5394 thefloor = actor->watertop;
5395 else
5396 thefloor = actor->floorz;
5397
5398 if (actor->target)
5399 {
5400 A_JetChase(actor);
5401 // check for melee attack
5402 if (actor->info->raisestate
5403 && (actor->z > (actor->floorz + FixedMul((32<<FRACBITS), actor->scale)))
5404 && P_JetbCheckMeleeRange(actor) && !actor->reactiontime
5405 && (actor->target->z >= actor->floorz))
5406 {
5407 mobj_t *bomb;
5408 if (actor->info->attacksound)
5409 S_StartAttackSound(actor, actor->info->attacksound);
5410
5411 // use raisestate instead of MT_MINE
5412 bomb = P_SpawnMobj(actor->x, actor->y, actor->z - FixedMul((32<<FRACBITS), actor->scale), (mobjtype_t)actor->info->raisestate);
5413
5414 P_SetTarget(&bomb->target, actor);
5415 bomb->destscale = actor->scale;
5416 P_SetScale(bomb, actor->scale);
5417 actor->reactiontime = TICRATE; // one second
5418 S_StartSound(actor, actor->info->attacksound);
5419 }
5420 }
5421 else if (((actor->z - FixedMul((32<<FRACBITS), actor->scale)) < thefloor) && !((thefloor + FixedMul((32<<FRACBITS), actor->scale) + actor->height) > actor->ceilingz))
5422 actor->z = thefloor+FixedMul((32<<FRACBITS), actor->scale);
5423
5424 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
5425 {
5426 // look for a new target
5427 if (P_LookForPlayers(actor, true, false, 0))
5428 return; // got a new target
5429
5430 P_SetMobjState(actor, actor->info->spawnstate);
5431 return;
5432 }
5433
5434 nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
5435
5436 // Move downwards or upwards to go through a passageway.
5437 if (nextsector->ceilingheight < actor->z + actor->height)
5438 actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
5439 else if (nextsector->floorheight > actor->z)
5440 actor->momz += FixedMul(5*FRACUNIT, actor->scale);
5441 }
5442
5443 // Function: A_JetgShoot
5444 //
5445 // Description: Firing function for Jetty-Syn gunners.
5446 //
5447 // var1 = unused
5448 // var2 = unused
5449 //
A_JetgShoot(mobj_t * actor)5450 void A_JetgShoot(mobj_t *actor)
5451 {
5452 fixed_t dist;
5453
5454 if (LUA_CallAction(A_JETGSHOOT, actor))
5455 return;
5456
5457 if (!actor->target)
5458 return;
5459
5460 if (actor->reactiontime)
5461 return;
5462
5463 dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
5464
5465 if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
5466 return;
5467
5468 if (dist < FixedMul(64*FRACUNIT, actor->scale))
5469 return;
5470
5471 A_FaceTarget(actor);
5472 P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
5473
5474 if (ultimatemode)
5475 actor->reactiontime = actor->info->reactiontime*TICRATE;
5476 else
5477 actor->reactiontime = actor->info->reactiontime*TICRATE*2;
5478
5479 if (actor->info->attacksound)
5480 S_StartSound(actor, actor->info->attacksound);
5481 }
5482
5483 // Function: A_ShootBullet
5484 //
5485 // Description: Shoots a bullet. Raisestate defines object # to use as projectile.
5486 //
5487 // var1 = unused
5488 // var2 = unused
5489 //
A_ShootBullet(mobj_t * actor)5490 void A_ShootBullet(mobj_t *actor)
5491 {
5492 fixed_t dist;
5493
5494 if (LUA_CallAction(A_SHOOTBULLET, actor))
5495 return;
5496
5497 if (!actor->target)
5498 return;
5499
5500 dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
5501
5502 if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
5503 return;
5504
5505 A_FaceTarget(actor);
5506 P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
5507
5508 if (actor->info->attacksound)
5509 S_StartSound(actor, actor->info->attacksound);
5510 }
5511
5512 static mobj_t *minus;
5513
PIT_MinusCarry(mobj_t * thing)5514 static boolean PIT_MinusCarry(mobj_t *thing)
5515 {
5516 if (minus->tracer)
5517 return true;
5518
5519 if (minus->type == thing->type)
5520 return true;
5521
5522 if (!(thing->flags & (MF_PUSHABLE|MF_ENEMY)))
5523 return true;
5524
5525 if (P_AproxDistance(minus->x - thing->x, minus->y - thing->y) >= minus->radius*3)
5526 return true;
5527
5528 if (abs(thing->z - minus->z) > minus->height)
5529 return true;
5530
5531 P_SetTarget(&minus->tracer, thing);
5532
5533 return true;
5534 }
5535
5536 // Function: A_MinusDigging
5537 //
5538 // Description: Minus digging in the ground.
5539 //
5540 // var1 = If 1, play digging sound.
5541 // var2 = unused
5542 //
A_MinusDigging(mobj_t * actor)5543 void A_MinusDigging(mobj_t *actor)
5544 {
5545 INT32 locvar1 = var1;
5546 INT32 rad = 32;
5547 angle_t fa = (actor->angle >> ANGLETOFINESHIFT) & FINEMASK;
5548 fixed_t dis = actor->info->speed*4;
5549 fixed_t x = FINECOSINE(fa)*dis + actor->x + FRACUNIT*P_RandomRange(-rad, rad);
5550 fixed_t y = FINESINE(fa)*dis + actor->y + FRACUNIT*P_RandomRange(-rad, rad);
5551 fixed_t mz = (actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz : actor->floorz;
5552 mobj_t *par;
5553
5554 if (LUA_CallAction(A_MINUSDIGGING, actor))
5555 return;
5556
5557 if (!actor->target)
5558 {
5559 P_SetMobjState(actor, actor->info->spawnstate);
5560 return;
5561 }
5562
5563 par = P_SpawnMobj(actor->x, actor->y, mz, MT_MINUSDIRT);
5564 if (actor->eflags & MFE_VERTICALFLIP)
5565 par->eflags |= MFE_VERTICALFLIP;
5566 P_TryMove(par, x, y, false);
5567
5568 // If close enough, prepare to attack
5569 if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < actor->radius*2)
5570 {
5571 P_SetMobjState(actor, actor->info->meleestate);
5572 P_TryMove(actor, actor->target->x, actor->target->y, false);
5573 S_StartSound(actor, actor->info->attacksound);
5574
5575 // Spawn growing dirt pile.
5576 par = P_SpawnMobj(actor->x, actor->y, mz, MT_MINUSDIRT);
5577 P_SetMobjState(par, actor->info->raisestate);
5578 P_SetScale(par, actor->scale*2);
5579 if (actor->eflags & MFE_VERTICALFLIP)
5580 par->eflags |= MFE_VERTICALFLIP;
5581 return;
5582 }
5583
5584 // Play digging sound
5585 if (locvar1 == 1)
5586 A_PlayActiveSound(actor);
5587
5588 // Move
5589 var1 = 3;
5590 A_Chase(actor);
5591
5592 // Carry over shit, maybe
5593 if (P_MobjWasRemoved(actor->tracer) || !actor->tracer->health)
5594 P_SetTarget(&actor->tracer, NULL);
5595
5596 if (!actor->tracer)
5597 {
5598 fixed_t radius = 3*actor->radius;
5599 fixed_t yh = (unsigned)(actor->y + radius - bmaporgy) >> MAPBLOCKSHIFT;
5600 fixed_t yl = (unsigned)(actor->y - radius - bmaporgy) >> MAPBLOCKSHIFT;
5601 fixed_t xh = (unsigned)(actor->x + radius - bmaporgx) >> MAPBLOCKSHIFT;
5602 fixed_t xl = (unsigned)(actor->x - radius - bmaporgx) >> MAPBLOCKSHIFT;
5603 fixed_t bx, by;
5604
5605 BMBOUNDFIX(xl, xh, yl, yh);
5606
5607 minus = actor;
5608
5609 for (bx = xl; bx <= xh; bx++)
5610 for (by = yl; by <= yh; by++)
5611 P_BlockThingsIterator(bx, by, PIT_MinusCarry);
5612 }
5613 else
5614 {
5615 if (P_TryMove(actor->tracer, actor->x, actor->y, false))
5616 actor->tracer->z = mz;
5617 else
5618 P_SetTarget(&actor->tracer, NULL);
5619 }
5620 }
5621
5622 // Function: A_MinusPopup
5623 //
5624 // Description: Minus popping out of the ground.
5625 //
5626 // var1 = unused
5627 // var2 = unused
5628 //
A_MinusPopup(mobj_t * actor)5629 void A_MinusPopup(mobj_t *actor)
5630 {
5631 INT32 num = 6;
5632 angle_t ani = FixedAngle(FRACUNIT*360/num);
5633 INT32 i;
5634
5635 if (LUA_CallAction(A_MINUSPOPUP, actor))
5636 return;
5637
5638 if (actor->eflags & MFE_VERTICALFLIP)
5639 actor->momz = -10*FRACUNIT;
5640 else
5641 actor->momz = 10*FRACUNIT;
5642
5643 S_StartSound(actor, sfx_s3k82);
5644 for (i = 1; i <= num; i++)
5645 {
5646 mobj_t *rock = P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_ROCKCRUMBLE1);
5647 P_Thrust(rock, ani*i, FRACUNIT);
5648 P_SetObjectMomZ(rock, 3*FRACUNIT, false);
5649 P_SetScale(rock, rock->scale/3);
5650 }
5651 P_RadiusAttack(actor, actor, 2*actor->radius, 0, true);
5652 if (actor->tracer)
5653 P_DamageMobj(actor->tracer, actor, actor, 1, 0);
5654
5655 actor->flags = (actor->flags & ~MF_NOCLIPTHING)|MF_SPECIAL|MF_SHOOTABLE;
5656 }
5657
5658 // Function: A_MinusCheck
5659 //
5660 // Description: If the minus hits the floor, dig back into the ground.
5661 //
5662 // var1 = State to switch to (if 0, use seestate).
5663 // var2 = If not 0, spawn debris when hitting the floor.
5664 //
A_MinusCheck(mobj_t * actor)5665 void A_MinusCheck(mobj_t *actor)
5666 {
5667 INT32 locvar1 = var1;
5668 INT32 locvar2 = var2;
5669
5670 if (LUA_CallAction(A_MINUSCHECK, actor))
5671 return;
5672
5673 if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz) || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
5674 {
5675 P_SetMobjState(actor, locvar1 ? (statenum_t)locvar1 : actor->info->seestate);
5676 actor->flags = actor->info->flags;
5677 if (locvar2)
5678 {
5679 INT32 i, num = 6;
5680 angle_t ani = FixedAngle(FRACUNIT*360/num);
5681 for (i = 1; i <= num; i++)
5682 {
5683 mobj_t *rock = P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_ROCKCRUMBLE1);
5684 P_Thrust(rock, ani*i, FRACUNIT);
5685 P_SetObjectMomZ(rock, 3*FRACUNIT, false);
5686 P_SetScale(rock, rock->scale/3);
5687 }
5688 }
5689 }
5690 }
5691
5692 // Function: A_ChickenCheck
5693 //
5694 // Description: Resets the chicken once it hits the floor again.
5695 //
5696 // var1 = unused
5697 // var2 = unused
5698 //
A_ChickenCheck(mobj_t * actor)5699 void A_ChickenCheck(mobj_t *actor)
5700 {
5701 if (LUA_CallAction(A_CHICKENCHECK, actor))
5702 return;
5703
5704 if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
5705 || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz))
5706 {
5707 if (!(actor->momx || actor->momy || actor->momz)
5708 && actor->state > &states[actor->info->seestate])
5709 {
5710 A_Chase(actor);
5711 P_SetMobjState(actor, actor->info->seestate);
5712 }
5713
5714 actor->momx >>= 2;
5715 actor->momy >>= 2;
5716 }
5717 }
5718
5719 // Function: A_JetgThink
5720 //
5721 // Description: Thinker for Jetty-Syn Gunners
5722 //
5723 // var1 = unused
5724 // var2 = unused
5725 //
A_JetgThink(mobj_t * actor)5726 void A_JetgThink(mobj_t *actor)
5727 {
5728 sector_t *nextsector;
5729
5730 fixed_t thefloor;
5731
5732 if (LUA_CallAction(A_JETGTHINK, actor))
5733 return;
5734
5735 if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
5736 && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
5737 thefloor = actor->watertop;
5738 else
5739 thefloor = actor->floorz;
5740
5741 if (actor->target)
5742 {
5743 if (P_RandomChance(FRACUNIT/8) && !actor->reactiontime)
5744 P_SetMobjState(actor, actor->info->missilestate);
5745 else
5746 A_JetChase (actor);
5747 }
5748 else if (actor->z - FixedMul((32<<FRACBITS), actor->scale) < thefloor && !(thefloor + FixedMul((32<<FRACBITS), actor->scale)
5749 + actor->height > actor->ceilingz))
5750 {
5751 actor->z = thefloor + FixedMul((32<<FRACBITS), actor->scale);
5752 }
5753
5754 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
5755 {
5756 // look for a new target
5757 if (P_LookForPlayers(actor, true, false, 0))
5758 return; // got a new target
5759
5760 P_SetMobjState(actor, actor->info->spawnstate);
5761 return;
5762 }
5763
5764 nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
5765
5766 // Move downwards or upwards to go through a passageway.
5767 if (nextsector->ceilingheight < actor->z + actor->height)
5768 actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
5769 else if (nextsector->floorheight > actor->z)
5770 actor->momz += FixedMul(5*FRACUNIT, actor->scale);
5771 }
5772
5773 // Function: A_MouseThink
5774 //
5775 // Description: Thinker for scurrying mice.
5776 //
5777 // var1 = unused
5778 // var2 = unused
5779 //
A_MouseThink(mobj_t * actor)5780 void A_MouseThink(mobj_t *actor)
5781 {
5782 if (LUA_CallAction(A_MOUSETHINK, actor))
5783 return;
5784
5785 if (actor->reactiontime)
5786 actor->reactiontime--;
5787
5788 if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z == actor->floorz)
5789 || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height == actor->ceilingz))
5790 && !actor->reactiontime)
5791 {
5792 if (twodlevel || actor->flags2 & MF2_TWOD)
5793 {
5794 if (P_RandomChance(FRACUNIT/2))
5795 actor->angle += ANGLE_180;
5796 }
5797 else if (P_RandomChance(FRACUNIT/2))
5798 actor->angle += ANGLE_90;
5799 else
5800 actor->angle -= ANGLE_90;
5801
5802 P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
5803 actor->reactiontime = TICRATE/5;
5804 }
5805 }
5806
5807 // Function: A_DetonChase
5808 //
5809 // Description: Chases a Deton after a player.
5810 //
5811 // var1 = unused
5812 // var2 = unused
5813 //
A_DetonChase(mobj_t * actor)5814 void A_DetonChase(mobj_t *actor)
5815 {
5816 angle_t exact;
5817 fixed_t xydist, dist;
5818
5819 if (LUA_CallAction(A_DETONCHASE, actor))
5820 return;
5821
5822 // modify tracer threshold
5823 if (!actor->tracer || actor->tracer->health <= 0)
5824 actor->threshold = 0;
5825 else
5826 actor->threshold = 1;
5827
5828 if (!actor->tracer || !(actor->tracer->flags & MF_SHOOTABLE))
5829 {
5830 // look for a new target
5831 if (P_LookForPlayers(actor, true, true, 0))
5832 return; // got a new target
5833
5834 actor->momx = actor->momy = actor->momz = 0;
5835 P_SetMobjState(actor, actor->info->spawnstate);
5836 return;
5837 }
5838
5839 if (multiplayer && !actor->threshold && P_LookForPlayers(actor, true, true, 0))
5840 return; // got a new target
5841
5842 // Face movement direction if not doing so
5843 exact = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
5844 actor->angle = exact;
5845 /*if (exact != actor->angle)
5846 {
5847 if (exact - actor->angle > ANGLE_180)
5848 {
5849 actor->angle -= actor->info->raisestate;
5850 if (exact - actor->angle < ANGLE_180)
5851 actor->angle = exact;
5852 }
5853 else
5854 {
5855 actor->angle += actor->info->raisestate;
5856 if (exact - actor->angle > ANGLE_180)
5857 actor->angle = exact;
5858 }
5859 }*/
5860 // movedir is up/down angle: how much it has to go up as it goes over to the player
5861 xydist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
5862 exact = R_PointToAngle2(0, 0, xydist, actor->tracer->z - actor->z);
5863 actor->movedir = exact;
5864 /*if (exact != actor->movedir)
5865 {
5866 if (exact - actor->movedir > ANGLE_180)
5867 {
5868 actor->movedir -= actor->info->raisestate;
5869 if (exact - actor->movedir < ANGLE_180)
5870 actor->movedir = exact;
5871 }
5872 else
5873 {
5874 actor->movedir += actor->info->raisestate;
5875 if (exact - actor->movedir > ANGLE_180)
5876 actor->movedir = exact;
5877 }
5878 }*/
5879
5880 // check for melee attack
5881 if (actor->tracer)
5882 {
5883 if (P_AproxDistance(actor->tracer->x-actor->x, actor->tracer->y-actor->y) < actor->radius+actor->tracer->radius)
5884 {
5885 if (!((actor->tracer->z > actor->z + actor->height) || (actor->z > actor->tracer->z + actor->tracer->height)))
5886 {
5887 P_ExplodeMissile(actor);
5888 return;
5889 }
5890 }
5891 }
5892
5893 // chase towards player
5894 if ((dist = P_AproxDistance(xydist, actor->tracer->z-actor->z))
5895 > FixedMul((actor->info->painchance << FRACBITS), actor->scale))
5896 {
5897 P_SetTarget(&actor->tracer, NULL); // Too far away
5898 return;
5899 }
5900
5901 if (actor->reactiontime == 0)
5902 {
5903 actor->reactiontime = actor->info->reactiontime;
5904 return;
5905 }
5906
5907 if (actor->reactiontime > 1)
5908 {
5909 actor->reactiontime--;
5910 return;
5911 }
5912
5913 if (actor->reactiontime > 0)
5914 {
5915 actor->reactiontime = -42;
5916
5917 if (actor->info->seesound)
5918 S_StartScreamSound(actor, actor->info->seesound);
5919 }
5920
5921 if (actor->reactiontime == -42)
5922 {
5923 fixed_t xyspeed, speed;
5924
5925 if (actor->target->player)
5926 speed = actor->target->player->normalspeed;
5927 else
5928 speed = actor->target->info->speed;
5929
5930 actor->reactiontime = -42;
5931
5932 exact = actor->movedir>>ANGLETOFINESHIFT;
5933 xyspeed = FixedMul(FixedMul(speed,3*FRACUNIT/4), FINECOSINE(exact));
5934 actor->momz = FixedMul(FixedMul(speed,3*FRACUNIT/4), FINESINE(exact));
5935
5936 exact = actor->angle>>ANGLETOFINESHIFT;
5937 actor->momx = FixedMul(xyspeed, FINECOSINE(exact));
5938 actor->momy = FixedMul(xyspeed, FINESINE(exact));
5939
5940 // Variable re-use
5941 xyspeed = (P_AproxDistance(actor->tracer->x - actor->x, P_AproxDistance(actor->tracer->y - actor->y, actor->tracer->z - actor->z))>>(FRACBITS+6));
5942
5943 if (xyspeed < 1)
5944 xyspeed = 1;
5945
5946 if (leveltime % xyspeed == 0)
5947 S_StartSound(actor, sfx_deton);
5948 }
5949 }
5950
5951 // Function: A_CapeChase
5952 //
5953 // Description: Set an object's location to its target or tracer.
5954 //
5955 // var1:
5956 // 0 = Use target
5957 // 1 = Use tracer
5958 // upper 16 bits = Z offset
5959 // var2:
5960 // upper 16 bits = forward/backward offset
5961 // lower 16 bits = sideways offset
5962 //
A_CapeChase(mobj_t * actor)5963 void A_CapeChase(mobj_t *actor)
5964 {
5965 mobj_t *chaser;
5966 fixed_t foffsetx, foffsety, boffsetx, boffsety;
5967 INT32 locvar1 = var1;
5968 INT32 locvar2 = var2;
5969 angle_t angle;
5970
5971 if (LUA_CallAction(A_CAPECHASE, actor))
5972 return;
5973
5974 CONS_Debug(DBG_GAMELOGIC, "A_CapeChase called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
5975
5976 if (locvar1 & 65535)
5977 chaser = actor->tracer;
5978 else
5979 chaser = actor->target;
5980
5981 if (!chaser || (chaser->health <= 0))
5982 {
5983 if (chaser)
5984 CONS_Debug(DBG_GAMELOGIC, "Hmm, the guy I'm chasing (object type %d) has no health.. so I'll die too!\n", chaser->type);
5985
5986 P_RemoveMobj(actor);
5987 return;
5988 }
5989
5990 angle = (chaser->player ? chaser->player->drawangle : chaser->angle);
5991
5992 foffsetx = P_ReturnThrustX(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
5993 foffsety = P_ReturnThrustY(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
5994
5995 boffsetx = P_ReturnThrustX(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
5996 boffsety = P_ReturnThrustY(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
5997
5998 P_UnsetThingPosition(actor);
5999 actor->x = chaser->x + foffsetx + boffsetx;
6000 actor->y = chaser->y + foffsety + boffsety;
6001 if (chaser->eflags & MFE_VERTICALFLIP)
6002 {
6003 actor->eflags |= MFE_VERTICALFLIP;
6004 actor->flags2 |= MF2_OBJECTFLIP;
6005 actor->z = chaser->z + chaser->height - actor->height - FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
6006 }
6007 else
6008 {
6009 actor->eflags &= ~MFE_VERTICALFLIP;
6010 actor->flags2 &= ~MF2_OBJECTFLIP;
6011 actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
6012 }
6013 actor->angle = angle;
6014 P_SetThingPosition(actor);
6015 }
6016
6017 // Function: A_RotateSpikeBall
6018 //
6019 // Description: Rotates a spike ball around its target/tracer.
6020 //
6021 // var1:
6022 // 0 = Use target
6023 // 1 = Use tracer
6024 // var2 = unused
6025 //
A_RotateSpikeBall(mobj_t * actor)6026 void A_RotateSpikeBall(mobj_t *actor)
6027 {
6028 INT32 locvar1 = var1;
6029 const fixed_t radius = FixedMul(12*actor->info->speed, actor->scale);
6030
6031 if (LUA_CallAction(A_ROTATESPIKEBALL, actor))
6032 return;
6033
6034 if (!((!locvar1 && (actor->target)) || (locvar1 && (actor->tracer))))// This should NEVER happen.
6035 {
6036 CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Spikeball has no target\n");
6037 P_RemoveMobj(actor);
6038 return;
6039 }
6040
6041 if (!actor->info->speed)
6042 {
6043 CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Object has no speed.\n");
6044 return;
6045 }
6046
6047 actor->angle += FixedAngle(actor->info->speed);
6048 P_UnsetThingPosition(actor);
6049 {
6050 const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
6051 if (!locvar1)
6052 {
6053 actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
6054 actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
6055 actor->z = actor->target->z + actor->target->height/2;
6056 }
6057 else
6058 {
6059 actor->x = actor->tracer->x + FixedMul(FINECOSINE(fa),radius);
6060 actor->y = actor->tracer->y + FixedMul(FINESINE(fa),radius);
6061 actor->z = actor->tracer->z + actor->tracer->height/2;
6062 }
6063 P_SetThingPosition(actor);
6064 }
6065 }
6066
6067 // Function: A_UnidusBall
6068 //
6069 // Description: Rotates a spike ball around its target.
6070 //
6071 // var1:
6072 // 0 = Don't throw
6073 // 1 = Throw
6074 // 2 = Throw when target leaves MF2_SKULLFLY.
6075 // var2 = unused
6076 //
A_UnidusBall(mobj_t * actor)6077 void A_UnidusBall(mobj_t *actor)
6078 {
6079 INT32 locvar1 = var1;
6080 boolean canthrow = false;
6081
6082 if (LUA_CallAction(A_UNIDUSBALL, actor))
6083 return;
6084
6085 actor->angle += ANGLE_11hh;
6086
6087 if (actor->movecount)
6088 {
6089 if (P_AproxDistance(actor->momx, actor->momy) < FixedMul(actor->info->damage/2, actor->scale))
6090 P_ExplodeMissile(actor);
6091 return;
6092 }
6093
6094 if (!actor->target || !actor->target->health)
6095 {
6096 CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Removing unthrown spikeball from nonexistant Unidus\n");
6097 P_RemoveMobj(actor);
6098 return;
6099 }
6100
6101 P_UnsetThingPosition(actor);
6102 {
6103 const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*(leveltime%360));
6104 const UINT16 fa = angle>>ANGLETOFINESHIFT;
6105
6106 actor->x = actor->target->x + FixedMul(FINECOSINE(fa),actor->threshold);
6107 actor->y = actor->target->y + FixedMul( FINESINE(fa),actor->threshold);
6108 actor->z = actor->target->z + actor->target->height/2 - actor->height/2;
6109
6110 if (locvar1 == 1 && actor->target->target)
6111 {
6112 const angle_t tang = R_PointToAngle2(actor->target->x, actor->target->y, actor->target->target->x, actor->target->target->y);
6113 const angle_t mina = tang-ANGLE_11hh;
6114 canthrow = (angle-mina < FixedAngle(actor->info->speed*3));
6115 }
6116 }
6117 P_SetThingPosition(actor);
6118
6119 if (locvar1 == 1 && canthrow)
6120 {
6121 if (P_AproxDistance(actor->target->target->x - actor->target->x, actor->target->target->y - actor->target->y) > FixedMul(MISSILERANGE>>1, actor->scale)
6122 || !P_CheckSight(actor, actor->target->target))
6123 return;
6124
6125 actor->movecount = actor->info->damage>>FRACBITS;
6126 actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
6127 P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->target->x, actor->target->target->y), FixedMul(actor->info->damage, actor->scale));
6128 }
6129 else if (locvar1 == 2)
6130 {
6131 boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY;
6132 if (actor->target->state == &states[actor->target->info->painstate])
6133 {
6134 P_KillMobj(actor, NULL, NULL, 0);
6135 return;
6136 }
6137 switch(actor->extravalue2)
6138 {
6139 case 0: // at least one frame where not dashing
6140 if (!skull) ++actor->extravalue2;
6141 else break;
6142 /* FALLTHRU */
6143 case 1: // at least one frame where ARE dashing
6144 if (skull) ++actor->extravalue2;
6145 else break;
6146 /* FALLTHRU */
6147 case 2: // not dashing again?
6148 if (skull) break;
6149 // launch.
6150 {
6151 mobj_t *target = actor->target;
6152 if (actor->target->target)
6153 target = actor->target->target;
6154 actor->movecount = actor->info->damage>>FRACBITS;
6155 actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
6156 P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, target->x, target->y), FixedMul(actor->info->damage, actor->scale));
6157 }
6158 default: // from our compiler appeasement program (CAP).
6159 break;
6160 }
6161 }
6162 }
6163
6164 // Function: A_RockSpawn
6165 //
6166 // Spawns rocks at a specified interval
6167 //
6168 // var1 = unused
6169 // var2 = unused
A_RockSpawn(mobj_t * actor)6170 void A_RockSpawn(mobj_t *actor)
6171 {
6172 mobj_t *mo;
6173 mobjtype_t type;
6174 INT32 i = Tag_FindLineSpecial(12, (INT16)actor->threshold);
6175 line_t *line;
6176 fixed_t dist;
6177 fixed_t randomoomph;
6178
6179 if (LUA_CallAction(A_ROCKSPAWN, actor))
6180 return;
6181
6182 if (i == -1)
6183 {
6184 CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: Unable to find parameter line 12 (tag %d)!\n", actor->threshold);
6185 return;
6186 }
6187
6188 line = &lines[i];
6189
6190 if (!(sides[line->sidenum[0]].textureoffset >> FRACBITS))
6191 {
6192 CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: No X-offset detected! (tag %d)!\n", actor->threshold);
6193 return;
6194 }
6195
6196 dist = P_AproxDistance(line->dx, line->dy)/16;
6197
6198 if (dist < 1)
6199 dist = 1;
6200
6201 type = MT_ROCKCRUMBLE1 + (sides[line->sidenum[0]].rowoffset >> FRACBITS);
6202
6203 if (line->flags & ML_NOCLIMB)
6204 randomoomph = P_RandomByte() * (FRACUNIT/32);
6205 else
6206 randomoomph = 0;
6207
6208 mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK);
6209 P_SetMobjState(mo, mobjinfo[type].spawnstate);
6210 mo->angle = R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y);
6211
6212 P_InstaThrust(mo, mo->angle, dist + randomoomph);
6213 mo->momz = dist + randomoomph;
6214
6215 var1 = sides[line->sidenum[0]].textureoffset >> FRACBITS;
6216 A_SetTics(actor);
6217 }
6218
6219 //
6220 // Function: A_SlingAppear
6221 //
6222 // Appears a sling.
6223 //
6224 // var1 = unused
6225 // var2 = unused
6226 //
A_SlingAppear(mobj_t * actor)6227 void A_SlingAppear(mobj_t *actor)
6228 {
6229 UINT8 mlength = 4;
6230 mobj_t *spawnee, *hprev;
6231
6232 if (LUA_CallAction(A_SLINGAPPEAR, actor))
6233 return;
6234
6235 P_UnsetThingPosition(actor);
6236 actor->flags &= ~(MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
6237 P_SetThingPosition(actor);
6238 actor->lastlook = 128;
6239 actor->movecount = actor->lastlook;
6240 actor->threshold = 0;
6241 actor->movefactor = actor->threshold;
6242 actor->friction = 128;
6243
6244 hprev = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLGRABCHAIN);
6245 P_SetTarget(&hprev->tracer, actor);
6246 P_SetTarget(&hprev->hprev, actor);
6247 P_SetTarget(&actor->hnext, hprev);
6248 hprev->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
6249 hprev->movecount = mlength;
6250
6251 mlength--;
6252
6253 while (mlength > 0)
6254 {
6255 spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN);
6256 P_SetTarget(&spawnee->tracer, actor);
6257 P_SetTarget(&spawnee->hprev, hprev);
6258 P_SetTarget(&hprev->hnext, spawnee);
6259 hprev = spawnee;
6260
6261 spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
6262 spawnee->movecount = mlength;
6263
6264 mlength--;
6265 }
6266 }
6267
6268 // Function: A_SetFuse
6269 //
6270 // Description: Sets the actor's fuse timer if not set already. May also change state when fuse reaches the last tic, otherwise by default the actor will die or disappear. (Replaces A_SnowBall)
6271 //
6272 // var1 = fuse timer duration (in tics).
6273 // var2:
6274 // lower 16 bits = if > 0, state to change to when fuse = 1
6275 // upper 16 bits: 0 = (default) don't set fuse unless 0, 1 = force change, 2 = force no change
6276 //
A_SetFuse(mobj_t * actor)6277 void A_SetFuse(mobj_t *actor)
6278 {
6279 INT32 locvar1 = var1;
6280 INT32 locvar2 = var2;
6281
6282 if (LUA_CallAction(A_SETFUSE, actor))
6283 return;
6284
6285 if ((!actor->fuse || (locvar2 >> 16)) && (locvar2 >> 16) != 2) // set the actor's fuse value
6286 actor->fuse = locvar1;
6287
6288 if (actor->fuse == 1 && (locvar2 & 65535)) // change state on the very last tic (fuse is handled before actions in P_MobjThinker)
6289 {
6290 actor->fuse = 0; // don't die/disappear the next tic!
6291 P_SetMobjState(actor, locvar2 & 65535);
6292 }
6293 }
6294
6295 // Function: A_CrawlaCommanderThink
6296 //
6297 // Description: Thinker for Crawla Commander.
6298 //
6299 // var1 = shoot bullets?
6300 // var2 = "pogo mode" speed
6301 //
A_CrawlaCommanderThink(mobj_t * actor)6302 void A_CrawlaCommanderThink(mobj_t *actor)
6303 {
6304 fixed_t dist;
6305 sector_t *nextsector;
6306 fixed_t thefloor;
6307 INT32 locvar1 = var1;
6308 INT32 locvar2 = var2;
6309 boolean hovermode = (actor->health > 1 || actor->fuse);
6310
6311 if (LUA_CallAction(A_CRAWLACOMMANDERTHINK, actor))
6312 return;
6313
6314 if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
6315 && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
6316 thefloor = actor->watertop;
6317 else
6318 thefloor = actor->floorz;
6319
6320 if (!actor->fuse && actor->flags2 & MF2_FRET)
6321 {
6322 if (actor->info->painsound)
6323 S_StartSound(actor, actor->info->painsound);
6324
6325 actor->fuse = TICRATE/2;
6326 actor->momz = 0;
6327
6328 P_InstaThrust(actor, actor->angle-ANGLE_180, FixedMul(5*FRACUNIT, actor->scale));
6329 }
6330
6331 if (actor->reactiontime > 0)
6332 actor->reactiontime--;
6333
6334 if (actor->fuse < 2)
6335 {
6336 actor->fuse = 0;
6337 actor->flags2 &= ~MF2_FRET;
6338 }
6339
6340 // Hover mode
6341 if (hovermode)
6342 {
6343 if (actor->z < thefloor + FixedMul(16*FRACUNIT, actor->scale))
6344 actor->momz += FixedMul(FRACUNIT, actor->scale);
6345 else if (actor->z < thefloor + FixedMul(32*FRACUNIT, actor->scale))
6346 actor->momz += FixedMul(FRACUNIT/2, actor->scale);
6347 else
6348 actor->momz += FixedMul(16, actor->scale);
6349 }
6350
6351 if (!actor->target)
6352 {
6353 // look for a new target
6354 if (P_LookForPlayers(actor, true, false, 0))
6355 return; // got a new target
6356
6357 if (actor->state != &states[actor->info->spawnstate])
6358 P_SetMobjState(actor, actor->info->spawnstate);
6359 return;
6360 }
6361
6362 dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
6363
6364 if (actor->target->player && (!hovermode || actor->reactiontime <= 2*TICRATE))
6365 {
6366 if (dist < FixedMul(64<<(FRACBITS+(hovermode ? 1 : 0)), actor->scale)
6367 && ((actor->target->player->pflags & PF_JUMPED) || (actor->target->player->pflags & PF_SPINNING)))
6368 {
6369 // Auugh! She's trying to kill you! Strafe! STRAAAAFFEEE!!
6370 P_InstaThrust(actor, actor->angle - ANGLE_180, FixedMul(20*FRACUNIT, actor->scale));
6371 return;
6372 }
6373 }
6374
6375 if (locvar1)
6376 {
6377 if (actor->health < 2 && P_RandomChance(FRACUNIT/128))
6378 P_SpawnMissile(actor, actor->target, locvar1);
6379 }
6380
6381 // Face the player
6382 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
6383
6384 if (actor->threshold && dist > FixedMul(256*FRACUNIT, actor->scale))
6385 actor->momx = actor->momy = 0;
6386
6387 if (actor->reactiontime && actor->reactiontime <= 2*TICRATE && dist > actor->target->radius - FixedMul(FRACUNIT, actor->scale))
6388 {
6389 actor->threshold = 0;
6390
6391 // Roam around, somewhat in the player's direction.
6392 actor->angle += (P_RandomByte()<<10);
6393 actor->angle -= (P_RandomByte()<<10);
6394
6395 if (hovermode)
6396 {
6397 fixed_t mom;
6398 P_Thrust(actor, actor->angle, 2*actor->scale);
6399 mom = P_AproxDistance(actor->momx, actor->momy);
6400 if (mom > 20*actor->scale)
6401 {
6402 mom += 20*actor->scale;
6403 mom >>= 1;
6404 P_InstaThrust(actor, R_PointToAngle2(0, 0, actor->momx, actor->momy), mom);
6405 }
6406 }
6407 }
6408 else if (!actor->reactiontime)
6409 {
6410 if (hovermode && !(actor->flags2 & MF2_FRET)) // Hover Mode
6411 {
6412 if (dist < FixedMul(512*FRACUNIT, actor->scale))
6413 {
6414 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
6415 P_InstaThrust(actor, actor->angle, FixedMul(40*FRACUNIT, actor->scale));
6416 actor->threshold = 1;
6417 if (actor->info->attacksound)
6418 S_StartSound(actor, actor->info->attacksound);
6419 }
6420 }
6421 actor->reactiontime = 3*TICRATE + (P_RandomByte()>>2);
6422 }
6423
6424 if (actor->health == 1)
6425 P_Thrust(actor, actor->angle, 1);
6426
6427 // Pogo Mode
6428 if (!hovermode && actor->z <= actor->floorz)
6429 {
6430 if (actor->info->activesound)
6431 S_StartSound(actor, actor->info->activesound);
6432
6433 if (dist < FixedMul(256*FRACUNIT, actor->scale))
6434 {
6435 actor->momz = FixedMul(locvar2, actor->scale);
6436 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
6437 P_InstaThrust(actor, actor->angle, FixedMul(locvar2/8, actor->scale));
6438 // pogo on player
6439 }
6440 else
6441 {
6442 UINT8 prandom = P_RandomByte();
6443 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
6444 P_InstaThrust(actor, actor->angle, FixedDiv(FixedMul(locvar2, actor->scale), 3*FRACUNIT/2));
6445 actor->momz = FixedMul(locvar2, actor->scale); // Bounce up in air
6446 }
6447 }
6448
6449 nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
6450
6451 // Move downwards or upwards to go through a passageway.
6452 if (nextsector->floorheight > actor->z && nextsector->floorheight - actor->z < FixedMul(128*FRACUNIT, actor->scale))
6453 actor->momz += (nextsector->floorheight - actor->z) / 4;
6454 }
6455
6456 // Function: A_RingExplode
6457 //
6458 // Description: An explosion ring exploding
6459 //
6460 // var1 = unused
6461 // var2 = unused
6462 //
A_RingExplode(mobj_t * actor)6463 void A_RingExplode(mobj_t *actor)
6464 {
6465 mobj_t *mo2;
6466 thinker_t *th;
6467 angle_t d;
6468
6469 if (LUA_CallAction(A_RINGEXPLODE, actor))
6470 return;
6471
6472 for (d = 0; d < 16; d++)
6473 P_SpawnParaloop(actor->x, actor->y, actor->z + actor->height, FixedMul(actor->info->painchance, actor->scale), 16, MT_NIGHTSPARKLE, S_NULL, d*(ANGLE_22h), true);
6474
6475 S_StartSound(actor, sfx_prloop);
6476
6477 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
6478 {
6479 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
6480 continue;
6481
6482 mo2 = (mobj_t *)th;
6483
6484 if (mo2 == actor) // Don't explode yourself! Endless loop!
6485 continue;
6486
6487 if (P_AproxDistance(P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y), mo2->z - actor->z) > FixedMul(actor->info->painchance, actor->scale))
6488 continue;
6489
6490 if (mo2->flags & MF_SHOOTABLE)
6491 {
6492 actor->flags2 |= MF2_DEBRIS;
6493 P_DamageMobj(mo2, actor, actor->target, 1, 0);
6494 continue;
6495 }
6496 }
6497 return;
6498 }
6499
6500 // Function: A_OldRingExplode
6501 //
6502 // Description: An explosion ring exploding, 1.09.4 style
6503 //
6504 // var1 = object # to explode as debris
6505 // var2 = unused
6506 //
A_OldRingExplode(mobj_t * actor)6507 void A_OldRingExplode(mobj_t *actor) {
6508 UINT8 i;
6509 mobj_t *mo;
6510 const fixed_t ns = FixedMul(20 * FRACUNIT, actor->scale);
6511 INT32 locvar1 = var1;
6512 boolean changecolor = (actor->target && actor->target->player);
6513
6514 if (LUA_CallAction(A_OLDRINGEXPLODE, actor))
6515 return;
6516
6517 for (i = 0; i < 32; i++)
6518 {
6519 const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
6520
6521 mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
6522 P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points
6523
6524 mo->momx = FixedMul(FINECOSINE(fa),ns);
6525 mo->momy = FixedMul(FINESINE(fa),ns);
6526
6527 if (i > 15)
6528 {
6529 if (i & 1)
6530 mo->momz = ns;
6531 else
6532 mo->momz = -ns;
6533 }
6534
6535 mo->flags2 |= MF2_DEBRIS;
6536 mo->fuse = TICRATE/5;
6537
6538 if (changecolor)
6539 {
6540 if (!(gametyperules & GTR_TEAMS))
6541 mo->color = actor->target->color; //copy color
6542 else if (actor->target->player->ctfteam == 2)
6543 mo->color = skincolor_bluering;
6544 }
6545 }
6546
6547 mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
6548
6549 P_SetTarget(&mo->target, actor->target);
6550 mo->momz = ns;
6551 mo->flags2 |= MF2_DEBRIS;
6552 mo->fuse = TICRATE/5;
6553
6554 if (changecolor)
6555 {
6556 if (!(gametyperules & GTR_TEAMS))
6557 mo->color = actor->target->color; //copy color
6558 else if (actor->target->player->ctfteam == 2)
6559 mo->color = skincolor_bluering;
6560 }
6561
6562 mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
6563
6564 P_SetTarget(&mo->target, actor->target);
6565 mo->momz = -ns;
6566 mo->flags2 |= MF2_DEBRIS;
6567 mo->fuse = TICRATE/5;
6568
6569 if (changecolor)
6570 {
6571 if (!(gametyperules & GTR_TEAMS))
6572 mo->color = actor->target->color; //copy color
6573 else if (actor->target->player->ctfteam == 2)
6574 mo->color = skincolor_bluering;
6575 }
6576 }
6577
6578 // Function: A_MixUp
6579 //
6580 // Description: Mix up all of the player positions.
6581 //
6582 // var1 = unused
6583 // var2 = unused
6584 //
A_MixUp(mobj_t * actor)6585 void A_MixUp(mobj_t *actor)
6586 {
6587 boolean teleported[MAXPLAYERS];
6588 INT32 i, numplayers = 0, prandom = 0;
6589
6590 if (LUA_CallAction(A_MIXUP, actor))
6591 return;
6592
6593 if (!multiplayer)
6594 return;
6595
6596 // No mix-up monitors in hide and seek or time only race.
6597 // The random factor is okay for other game modes, but in these, it is cripplingly unfair.
6598 if (gametype == GT_HIDEANDSEEK || gametype == GT_RACE)
6599 {
6600 S_StartSound(actor, sfx_lose);
6601 return;
6602 }
6603
6604 numplayers = 0;
6605 memset(teleported, 0, sizeof (teleported));
6606
6607 // Count the number of players in the game
6608 // and grab their xyz coords
6609 for (i = 0; i < MAXPLAYERS; i++)
6610 if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
6611 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
6612 {
6613 if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
6614 continue;
6615
6616 numplayers++;
6617 }
6618
6619 if (numplayers <= 1) // Not enough players to mix up.
6620 {
6621 S_StartSound(actor, sfx_lose);
6622 return;
6623 }
6624 else if (numplayers == 2) // Special case -- simple swap
6625 {
6626 fixed_t x, y, z;
6627 angle_t angle, drawangle;
6628 INT32 one = -1, two = 0; // default value 0 to make the compiler shut up
6629
6630 // Zoom tube stuff
6631 mobj_t *tempthing = NULL; //tracer
6632 UINT16 carry1,carry2; //carry
6633 INT32 transspeed; //player speed
6634
6635 // Starpost stuff
6636 INT16 starpostx, starposty, starpostz;
6637 INT32 starpostnum;
6638 tic_t starposttime;
6639 angle_t starpostangle;
6640 fixed_t starpostscale;
6641
6642 INT32 mflags2;
6643
6644 for (i = 0; i < MAXPLAYERS; i++)
6645 if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
6646 && !players[i].exiting && !players[i].powers[pw_super])
6647 {
6648 if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
6649 continue;
6650
6651 if (one == -1)
6652 one = i;
6653 else
6654 {
6655 two = i;
6656 break;
6657 }
6658 }
6659
6660 //get this done first!
6661 if (players[one].powers[pw_carry] == CR_MINECART && players[one].mo->tracer && !(P_MobjWasRemoved(players[one].mo->tracer)))
6662 P_SetTarget(&players[one].mo->tracer->target, players[two].mo);
6663 if (players[two].powers[pw_carry] == CR_MINECART && players[two].mo->tracer && !(P_MobjWasRemoved(players[two].mo->tracer)))
6664 P_SetTarget(&players[two].mo->tracer->target, players[one].mo);
6665 tempthing = players[one].mo->tracer;
6666 P_SetTarget(&players[one].mo->tracer, players[two].mo->tracer);
6667 P_SetTarget(&players[two].mo->tracer, tempthing);
6668
6669 //zoom tubes use player->speed to determine direction and speed
6670 transspeed = players[one].speed;
6671 players[one].speed = players[two].speed;
6672 players[two].speed = transspeed;
6673
6674 //set flags variables now but DON'T set them.
6675 carry1 = (players[one].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[one].powers[pw_carry]);
6676 carry2 = (players[two].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[two].powers[pw_carry]);
6677
6678 x = players[one].mo->x;
6679 y = players[one].mo->y;
6680 z = players[one].mo->z;
6681 angle = players[one].mo->angle;
6682 drawangle = players[one].drawangle;
6683
6684 starpostx = players[one].starpostx;
6685 starposty = players[one].starposty;
6686 starpostz = players[one].starpostz;
6687 starpostangle = players[one].starpostangle;
6688 starpostscale = players[one].starpostscale;
6689 starpostnum = players[one].starpostnum;
6690 starposttime = players[one].starposttime;
6691
6692 mflags2 = players[one].mo->flags2;
6693
6694 P_MixUp(players[one].mo, players[two].mo->x, players[two].mo->y, players[two].mo->z, players[two].mo->angle,
6695 players[two].starpostx, players[two].starposty, players[two].starpostz,
6696 players[two].starpostnum, players[two].starposttime, players[two].starpostangle,
6697 players[two].starpostscale, players[two].drawangle, players[two].mo->flags2);
6698
6699 P_MixUp(players[two].mo, x, y, z, angle, starpostx, starposty, starpostz,
6700 starpostnum, starposttime, starpostangle,
6701 starpostscale, drawangle, mflags2);
6702
6703 //carry set after mixup. Stupid P_ResetPlayer() takes away some of the stuff we look for...
6704 //but not all of it! So we need to make sure they aren't set wrong or anything.
6705 players[one].powers[pw_carry] = carry2;
6706 players[two].powers[pw_carry] = carry1;
6707
6708 teleported[one] = true;
6709 teleported[two] = true;
6710 }
6711 else
6712 {
6713 fixed_t position[MAXPLAYERS][3];
6714 angle_t anglepos[MAXPLAYERS][2];
6715 INT32 pindex[MAXPLAYERS], counter = 0, teleportfrom = 0;
6716
6717 // Zoom tube stuff
6718 mobj_t *transtracer[MAXPLAYERS]; //tracer
6719 //pflags_t transflag[MAXPLAYERS]; //cyan pink white pink cyan
6720 UINT16 transcarry[MAXPLAYERS]; //player carry
6721 INT32 transspeed[MAXPLAYERS]; //player speed
6722
6723 // Star post stuff
6724 INT16 spposition[MAXPLAYERS][3];
6725 INT32 starpostnum[MAXPLAYERS];
6726 tic_t starposttime[MAXPLAYERS];
6727 angle_t starpostangle[MAXPLAYERS];
6728 fixed_t starpostscale[MAXPLAYERS];
6729
6730 INT32 flags2[MAXPLAYERS];
6731
6732 for (i = 0; i < MAXPLAYERS; i++)
6733 {
6734 position[i][0] = position[i][1] = position[i][2] = anglepos[i][0] = anglepos[i][1] = pindex[i] = -1;
6735 teleported[i] = false;
6736 }
6737
6738 for (i = 0; i < MAXPLAYERS; i++)
6739 {
6740 if (playeringame[i] && players[i].playerstate == PST_LIVE
6741 && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
6742 {
6743 if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
6744 continue;
6745
6746 position[counter][0] = players[i].mo->x;
6747 position[counter][1] = players[i].mo->y;
6748 position[counter][2] = players[i].mo->z;
6749 pindex[counter] = i;
6750 anglepos[counter][0] = players[i].mo->angle;
6751 anglepos[counter][1] = players[i].drawangle;
6752 players[i].mo->momx = players[i].mo->momy = players[i].mo->momz =
6753 players[i].rmomx = players[i].rmomy = 1;
6754 players[i].cmomx = players[i].cmomy = 0;
6755
6756 transcarry[counter] = (players[i].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[i].powers[pw_carry]);
6757 transspeed[counter] = players[i].speed;
6758 transtracer[counter] = players[i].mo->tracer;
6759
6760 spposition[counter][0] = players[i].starpostx;
6761 spposition[counter][1] = players[i].starposty;
6762 spposition[counter][2] = players[i].starpostz;
6763 starpostnum[counter] = players[i].starpostnum;
6764 starposttime[counter] = players[i].starposttime;
6765 starpostangle[counter] = players[i].starpostangle;
6766 starpostscale[counter] = players[i].starpostscale;
6767
6768 flags2[counter] = players[i].mo->flags2;
6769
6770 counter++;
6771 }
6772 }
6773
6774 counter = 0;
6775
6776 // Mix them up!
6777 for (;;)
6778 {
6779 if (counter > 255) // fail-safe to avoid endless loop
6780 break;
6781 prandom = P_RandomByte();
6782 prandom %= numplayers; // I love modular arithmetic, don't you?
6783 if (prandom) // Make sure it's not a useless mix
6784 break;
6785 counter++;
6786 }
6787
6788 counter = 0;
6789
6790 for (i = 0; i < MAXPLAYERS; i++)
6791 {
6792 if (playeringame[i] && players[i].playerstate == PST_LIVE
6793 && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
6794 {
6795 if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
6796 continue;
6797
6798 teleportfrom = (counter + prandom) % numplayers;
6799
6800 //speed and tracer come before...
6801 players[i].speed = transspeed[teleportfrom];
6802 P_SetTarget(&players[i].mo->tracer, transtracer[teleportfrom]);
6803
6804 P_MixUp(players[i].mo, position[teleportfrom][0], position[teleportfrom][1], position[teleportfrom][2], anglepos[teleportfrom][0],
6805 spposition[teleportfrom][0], spposition[teleportfrom][1], spposition[teleportfrom][2],
6806 starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom],
6807 starpostscale[teleportfrom], anglepos[teleportfrom][1], flags2[teleportfrom]);
6808
6809 //...carry after. same reasoning.
6810 players[i].powers[pw_carry] = transcarry[teleportfrom];
6811 if (transcarry[teleportfrom] == CR_MINECART && transtracer[teleportfrom] && !(P_MobjWasRemoved(transtracer[teleportfrom])))
6812 P_SetTarget(&transtracer[teleportfrom]->target, players[i].mo);
6813
6814 teleported[i] = true;
6815 counter++;
6816 }
6817 }
6818 }
6819
6820 for (i = 0; i < MAXPLAYERS; i++)
6821 {
6822 if (teleported[i])
6823 {
6824 if (playeringame[i] && players[i].playerstate == PST_LIVE
6825 && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
6826 {
6827 if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
6828 continue;
6829
6830 P_SetThingPosition(players[i].mo);
6831
6832 players[i].mo->floorz = P_GetFloorZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
6833 players[i].mo->ceilingz = P_GetCeilingZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
6834
6835 P_CheckPosition(players[i].mo, players[i].mo->x, players[i].mo->y);
6836 }
6837 }
6838 }
6839
6840 // Play the 'bowrwoosh!' sound
6841 S_StartSound(NULL, sfx_mixup);
6842 }
6843
6844 // Function: A_RecyclePowers
6845 //
6846 // Description: Take all player's powers, and swap 'em.
6847 //
6848 // var1 = unused
6849 // var2 = unused
6850 //
A_RecyclePowers(mobj_t * actor)6851 void A_RecyclePowers(mobj_t *actor)
6852 {
6853 INT32 i, j, k, numplayers = 0;
6854
6855 #ifdef WEIGHTEDRECYCLER
6856 UINT8 beneficiary = 255;
6857 #endif
6858 UINT8 playerslist[MAXPLAYERS];
6859 UINT8 postscramble[MAXPLAYERS];
6860
6861 UINT16 powers[MAXPLAYERS][NUMPOWERS];
6862 INT32 weapons[MAXPLAYERS];
6863 INT32 weaponheld[MAXPLAYERS];
6864
6865 if (LUA_CallAction(A_RECYCLEPOWERS, actor))
6866 return;
6867
6868 if (!multiplayer)
6869 {
6870 S_StartSound(actor, sfx_lose);
6871 return;
6872 }
6873
6874 numplayers = 0;
6875
6876 // Count the number of players in the game
6877 for (i = 0, j = 0; i < MAXPLAYERS; i++)
6878 {
6879 if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
6880 && !players[i].exiting && !((netgame || multiplayer) && players[i].spectator))
6881 {
6882 #ifndef WEIGHTEDRECYCLER
6883 if (players[i].powers[pw_super])
6884 continue; // Ignore super players
6885 #endif
6886
6887 numplayers++;
6888 postscramble[j] = playerslist[j] = (UINT8)i;
6889
6890 #ifdef WEIGHTEDRECYCLER
6891 // The guy who started the recycle gets the best result
6892 if (actor && actor->target && actor->target->player && &players[i] == actor->target->player)
6893 beneficiary = (UINT8)i;
6894 #endif
6895
6896 // Save powers
6897 for (k = 0; k < NUMPOWERS; k++)
6898 powers[i][k] = players[i].powers[k];
6899 //1.1: ring weapons too
6900 weapons[i] = players[i].ringweapons;
6901 weaponheld[i] = players[i].currentweapon;
6902
6903 j++;
6904 }
6905 }
6906
6907 if (numplayers <= 1)
6908 {
6909 S_StartSound(actor, sfx_lose);
6910 return; //nobody to touch!
6911 }
6912
6913 //shuffle the post scramble list, whee!
6914 // hardcoded 0-1 to 1-0 for two players
6915 if (numplayers == 2)
6916 {
6917 postscramble[0] = playerslist[1];
6918 postscramble[1] = playerslist[0];
6919 }
6920 else
6921 for (j = 0; j < numplayers; j++)
6922 {
6923 UINT8 tempint;
6924
6925 i = j + ((P_RandomByte() + leveltime) % (numplayers - j));
6926 tempint = postscramble[j];
6927 postscramble[j] = postscramble[i];
6928 postscramble[i] = tempint;
6929 }
6930
6931 #ifdef WEIGHTEDRECYCLER
6932 //the joys of qsort...
6933 if (beneficiary != 255) {
6934 qsort(playerslist, numplayers, sizeof(UINT8), P_RecycleCompare);
6935
6936 // now, make sure the benificiary is in the best slot
6937 // swap out whatever poor sap was going to get the best items
6938 for (i = 0; i < numplayers; i++)
6939 {
6940 if (postscramble[i] == beneficiary)
6941 {
6942 postscramble[i] = postscramble[0];
6943 postscramble[0] = beneficiary;
6944 break;
6945 }
6946 }
6947 }
6948 #endif
6949
6950 // now assign!
6951 for (i = 0; i < numplayers; i++)
6952 {
6953 UINT8 send_pl = playerslist[i];
6954 UINT8 recv_pl = postscramble[i];
6955
6956 // debugF
6957 CONS_Debug(DBG_GAMELOGIC, "sending player %hu's items to %hu\n", (UINT16)send_pl, (UINT16)recv_pl);
6958
6959 for (j = 0; j < NUMPOWERS; j++)
6960 {
6961 if (j == pw_flashing || j == pw_underwater || j == pw_spacetime || j == pw_carry
6962 || j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super
6963 || j == pw_pushing || j == pw_justsprung || j == pw_noautobrake || j == pw_justlaunched
6964 || j == pw_ignorelatch)
6965 continue;
6966 players[recv_pl].powers[j] = powers[send_pl][j];
6967 }
6968
6969 //1.1: weapon rings too
6970 players[recv_pl].ringweapons = weapons[send_pl];
6971 players[recv_pl].currentweapon = weaponheld[send_pl];
6972
6973 if (((players[recv_pl].powers[pw_shield] & SH_NOSTACK) == SH_PINK) && (players[recv_pl].revitem == MT_LHRT || players[recv_pl].spinitem == MT_LHRT || players[recv_pl].thokitem == MT_LHRT)) // Healers can't keep their buff.
6974 players[recv_pl].powers[pw_shield] &= SH_STACK;
6975
6976 P_SpawnShieldOrb(&players[recv_pl]);
6977 if (P_IsLocalPlayer(&players[recv_pl]))
6978 P_RestoreMusic(&players[recv_pl]);
6979 P_FlashPal(&players[recv_pl], PAL_RECYCLE, 10);
6980 }
6981
6982 S_StartSound(NULL, sfx_gravch); //heh, the sound effect I used is already in
6983 }
6984
6985 // Function: A_Boss1Chase
6986 //
6987 // Description: Like A_Chase, but for Boss 1.
6988 //
6989 // var1 = unused
6990 // var2 = unused
6991 //
A_Boss1Chase(mobj_t * actor)6992 void A_Boss1Chase(mobj_t *actor)
6993 {
6994 INT32 delta;
6995
6996 if (LUA_CallAction(A_BOSS1CHASE, actor))
6997 return;
6998
6999 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
7000 {
7001 // look for a new target
7002 if (P_LookForPlayers(actor, true, false, 0))
7003 return; // got a new target
7004
7005 P_SetMobjStateNF(actor, actor->info->spawnstate);
7006 return;
7007 }
7008
7009 if (actor->reactiontime)
7010 actor->reactiontime--;
7011
7012 // turn towards movement direction if not there yet
7013 if (actor->movedir < NUMDIRS)
7014 {
7015 actor->angle &= (7<<29);
7016 delta = actor->angle - (actor->movedir << 29);
7017
7018 if (delta > 0)
7019 actor->angle -= ANGLE_45;
7020 else if (delta < 0)
7021 actor->angle += ANGLE_45;
7022 }
7023
7024 // do not attack twice in a row
7025 if (actor->flags2 & MF2_JUSTATTACKED)
7026 {
7027 actor->flags2 &= ~MF2_JUSTATTACKED;
7028 P_NewChaseDir(actor);
7029 return;
7030 }
7031
7032 if (actor->movecount)
7033 goto nomissile;
7034
7035 if (!P_CheckMissileRange(actor))
7036 goto nomissile;
7037
7038 if (actor->reactiontime <= 0)
7039 {
7040 if (actor->health > actor->info->damage)
7041 {
7042 if (P_RandomChance(FRACUNIT/2))
7043 P_SetMobjState(actor, actor->info->missilestate);
7044 else
7045 P_SetMobjState(actor, actor->info->meleestate);
7046 }
7047 else
7048 {
7049 if (actor->spawnpoint && actor->spawnpoint->extrainfo)
7050 P_LinedefExecute(LE_PINCHPHASE+(actor->spawnpoint->extrainfo*LE_PARAMWIDTH), actor, NULL);
7051 else
7052 P_LinedefExecute(LE_PINCHPHASE, actor, NULL);
7053 P_SetMobjState(actor, actor->info->raisestate);
7054 }
7055
7056 actor->flags2 |= MF2_JUSTATTACKED;
7057 actor->reactiontime = actor->info->reactiontime;
7058 return;
7059 }
7060
7061 // ?
7062 nomissile:
7063 // possibly choose another target
7064 if (multiplayer && P_RandomChance(FRACUNIT/128))
7065 {
7066 if (P_LookForPlayers(actor, true, false, 0))
7067 return; // got a new target
7068 }
7069
7070 if (actor->flags & MF_FLOAT && !(actor->flags2 & MF2_SKULLFLY))
7071 { // Float up/down to your target's position. Stay above them, but not out of jump range.
7072 fixed_t target_min = actor->target->floorz+FixedMul(64*FRACUNIT, actor->scale);
7073 if (target_min < actor->target->z - actor->height)
7074 target_min = actor->target->z - actor->height;
7075 if (target_min < actor->floorz+FixedMul(33*FRACUNIT, actor->scale))
7076 target_min = actor->floorz+FixedMul(33*FRACUNIT, actor->scale);
7077 if (actor->z > target_min+FixedMul(16*FRACUNIT, actor->scale))
7078 actor->momz = FixedMul((-actor->info->speed<<(FRACBITS-1)), actor->scale);
7079 else if (actor->z < target_min)
7080 actor->momz = FixedMul(actor->info->speed<<(FRACBITS-1), actor->scale);
7081 else
7082 actor->momz = FixedMul(actor->momz,7*FRACUNIT/8);
7083 }
7084
7085 // chase towards player
7086 if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) > actor->radius+actor->target->radius)
7087 {
7088 if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
7089 P_NewChaseDir(actor);
7090 }
7091 // too close, don't want to chase.
7092 else if (--actor->movecount < 0)
7093 {
7094 // A mini-A_FaceTarget based on P_NewChaseDir.
7095 // Yes, it really is this simple when you get down to it.
7096 fixed_t deltax, deltay;
7097
7098 deltax = actor->target->x - actor->x;
7099 deltay = actor->target->y - actor->y;
7100
7101 actor->movedir = diags[((deltay < 0)<<1) + (deltax > 0)];
7102 actor->movecount = P_RandomByte() & 15;
7103 }
7104 }
7105
7106 // Function: A_Boss2Chase
7107 //
7108 // Description: Really doesn't 'chase', but rather goes in a circle.
7109 //
7110 // var1 = unused
7111 // var2 = unused
7112 //
A_Boss2Chase(mobj_t * actor)7113 void A_Boss2Chase(mobj_t *actor)
7114 {
7115 fixed_t radius;
7116 boolean reverse = false;
7117 INT32 speedvar;
7118
7119 if (LUA_CallAction(A_BOSS2CHASE, actor))
7120 return;
7121
7122 if (actor->health <= 0)
7123 return;
7124
7125 // Startup randomness
7126 if (actor->reactiontime <= -666)
7127 actor->reactiontime = 2*TICRATE + P_RandomByte();
7128
7129 // When reactiontime hits zero, he will go the other way
7130 if (--actor->reactiontime <= 0)
7131 {
7132 reverse = true;
7133 actor->reactiontime = 2*TICRATE + P_RandomByte();
7134 }
7135
7136 P_SetTarget(&actor->target, P_GetClosestAxis(actor));
7137
7138 if (!actor->target) // This should NEVER happen.
7139 {
7140 CONS_Debug(DBG_GAMELOGIC, "Boss2 has no target!\n");
7141 A_BossDeath(actor);
7142 return;
7143 }
7144
7145 radius = actor->target->radius;
7146
7147 if (reverse)
7148 {
7149 actor->watertop = -actor->watertop;
7150 actor->extravalue1 = 18;
7151 if (actor->flags2 & MF2_AMBUSH)
7152 actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2;
7153 actor->extravalue2 = actor->extravalue1;
7154 }
7155
7156 // Turnaround
7157 if (actor->extravalue1 > 0)
7158 {
7159 --actor->extravalue1;
7160
7161 // Set base angle
7162 {
7163 const angle_t fa = (actor->target->angle + FixedAngle(actor->watertop))>>ANGLETOFINESHIFT;
7164 const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
7165 const fixed_t fs = FixedMul(FINESINE(fa),radius);
7166 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
7167 }
7168
7169 // Now turn you around!
7170 // Note that the start position is the final position, we move it back around
7171 // to intermediary positions...
7172 actor->angle -= FixedAngle(FixedMul(FixedDiv(180<<FRACBITS, actor->extravalue2<<FRACBITS), actor->extravalue1<<FRACBITS));
7173 }
7174 else
7175 {
7176 // Only speed up if you have the 'Deaf' flag.
7177 if (actor->flags2 & MF2_AMBUSH)
7178 speedvar = actor->health;
7179 else
7180 speedvar = actor->info->spawnhealth;
7181
7182 actor->target->angle += // Don't use FixedAngleC!
7183 FixedAngle(FixedDiv(FixedMul(actor->watertop, (actor->info->spawnhealth*(FRACUNIT/4)*3)), speedvar*FRACUNIT));
7184
7185 P_UnsetThingPosition(actor);
7186 {
7187 const angle_t fa = actor->target->angle>>ANGLETOFINESHIFT;
7188 const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
7189 const fixed_t fs = FixedMul(FINESINE(fa),radius);
7190 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
7191 actor->x = actor->target->x + fc;
7192 actor->y = actor->target->y + fs;
7193 }
7194 P_SetThingPosition(actor);
7195
7196 // Spray goo once every second
7197 if (leveltime % (speedvar*15/10)-1 == 0)
7198 {
7199 const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
7200 mobj_t *goop;
7201 fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
7202 angle_t fa;
7203 // actor->movedir is used to determine the last
7204 // direction goo was sprayed in. There are 8 possible
7205 // directions to spray. (45-degree increments)
7206
7207 actor->movedir++;
7208 actor->movedir %= NUMDIRS;
7209 fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
7210
7211 goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
7212 goop->momx = FixedMul(FINECOSINE(fa),ns);
7213 goop->momy = FixedMul(FINESINE(fa),ns);
7214 goop->momz = FixedMul(4*FRACUNIT, actor->scale);
7215 goop->fuse = 10*TICRATE;
7216
7217 if (actor->info->attacksound)
7218 S_StartAttackSound(actor, actor->info->attacksound);
7219
7220 if (P_RandomChance(FRACUNIT/2))
7221 {
7222 goop->momx *= 2;
7223 goop->momy *= 2;
7224 }
7225 else if (P_RandomChance(129*FRACUNIT/256))
7226 {
7227 goop->momx *= 3;
7228 goop->momy *= 3;
7229 }
7230
7231 actor->flags2 |= MF2_JUSTATTACKED;
7232 }
7233 }
7234 }
7235
7236 // Function: A_Boss2Pogo
7237 //
7238 // Description: Pogo part of Boss 2 AI.
7239 //
7240 // var1 = unused
7241 // var2 = unused
7242 //
A_Boss2Pogo(mobj_t * actor)7243 void A_Boss2Pogo(mobj_t *actor)
7244 {
7245 if (LUA_CallAction(A_BOSS2POGO, actor))
7246 return;
7247
7248 if (actor->z <= actor->floorz + FixedMul(8*FRACUNIT, actor->scale) && actor->momz <= 0)
7249 {
7250 if (actor->state != &states[actor->info->raisestate])
7251 P_SetMobjState(actor, actor->info->raisestate);
7252 // Pogo Mode
7253 }
7254 else if (actor->momz < 0 && actor->reactiontime)
7255 {
7256 const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
7257 mobj_t *goop;
7258 fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
7259 angle_t fa;
7260 INT32 i;
7261 // spray in all 8 directions!
7262 for (i = 0; i < 8; i++)
7263 {
7264 actor->movedir++;
7265 actor->movedir %= NUMDIRS;
7266 fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
7267
7268 goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
7269 goop->momx = FixedMul(FINECOSINE(fa),ns);
7270 goop->momy = FixedMul(FINESINE(fa),ns);
7271 goop->momz = FixedMul(4*FRACUNIT, actor->scale);
7272
7273 goop->fuse = 10*TICRATE;
7274 }
7275 actor->reactiontime = 0; // we already shot goop, so don't do it again!
7276 if (actor->info->attacksound)
7277 S_StartAttackSound(actor, actor->info->attacksound);
7278 actor->flags2 |= MF2_JUSTATTACKED;
7279 }
7280 }
7281
7282 // Function: A_Boss2TakeDamage
7283 //
7284 // Description: Special function for Boss 2 so you can't just sit and destroy him.
7285 //
7286 // var1 = Invincibility duration
7287 // var2 = unused
7288 //
A_Boss2TakeDamage(mobj_t * actor)7289 void A_Boss2TakeDamage(mobj_t *actor)
7290 {
7291 INT32 locvar1 = var1;
7292
7293 if (LUA_CallAction(A_BOSS2TAKEDAMAGE, actor))
7294 return;
7295
7296 A_Pain(actor);
7297 actor->reactiontime = 1; // turn around
7298 if (locvar1 == 0) // old A_Invincibilerize behavior
7299 actor->movecount = TICRATE;
7300 else
7301 actor->movecount = locvar1; // become flashing invulnerable for this long.
7302 }
7303
7304 // Function: A_Boss7Chase
7305 //
7306 // Description: Like A_Chase, but for Black Eggman
7307 //
7308 // var1 = unused
7309 // var2 = unused
7310 //
A_Boss7Chase(mobj_t * actor)7311 void A_Boss7Chase(mobj_t *actor)
7312 {
7313 INT32 delta;
7314 INT32 i;
7315
7316 if (LUA_CallAction(A_BOSS7CHASE, actor))
7317 return;
7318
7319 if (actor->z != actor->floorz)
7320 return;
7321
7322 // Self-adjust if stuck on the edge
7323 if (actor->tracer)
7324 {
7325 if (P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y) > 128*FRACUNIT - actor->radius)
7326 P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), FRACUNIT);
7327 }
7328
7329 if (actor->flags2 & MF2_FRET)
7330 {
7331 P_SetMobjState(actor, S_BLACKEGG_DESTROYPLAT1);
7332 S_StartSound(0, sfx_s3k53);
7333 actor->flags2 &= ~MF2_FRET;
7334 return;
7335 }
7336
7337 // turn towards movement direction if not there yet
7338 if (actor->movedir < NUMDIRS)
7339 {
7340 actor->angle &= (7<<29);
7341 delta = actor->angle - (actor->movedir << 29);
7342
7343 if (delta > 0)
7344 actor->angle -= ANGLE_45;
7345 else if (delta < 0)
7346 actor->angle += ANGLE_45;
7347 }
7348
7349 // Is a player on top of us?
7350 for (i = 0; i < MAXPLAYERS; i++)
7351 {
7352 if (!playeringame[i] || players[i].spectator)
7353 continue;
7354
7355 if (!players[i].mo)
7356 continue;
7357
7358 if (players[i].mo->health <= 0)
7359 continue;
7360
7361 if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) > actor->radius)
7362 continue;
7363
7364 if (players[i].mo->z > actor->z + actor->height - 2*FRACUNIT
7365 && players[i].mo->z < actor->z + actor->height + 32*FRACUNIT)
7366 {
7367 // Punch him!
7368 P_SetMobjState(actor, actor->info->meleestate);
7369 S_StartSound(0, sfx_begrnd); // warning sound
7370 return;
7371 }
7372 }
7373
7374 if (actor->health <= actor->info->damage
7375 && actor->target
7376 && actor->target->player
7377 && (actor->target->player->powers[pw_carry] == CR_GENERIC))
7378 {
7379 A_FaceTarget(actor);
7380 P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
7381 actor->movecount = TICRATE + P_RandomByte()/2;
7382 return;
7383 }
7384
7385 if (actor->reactiontime)
7386 actor->reactiontime--;
7387
7388 if (actor->reactiontime <= 0 && actor->z == actor->floorz)
7389 {
7390 // Here, we'll call P_RandomByte() and decide what kind of attack to do
7391 switch(actor->threshold)
7392 {
7393 case 0: // Lob cannon balls
7394 if (actor->z < 1056*FRACUNIT)
7395 {
7396 A_FaceTarget(actor);
7397 P_SetMobjState(actor, actor->info->xdeathstate);
7398 actor->movecount = 7*TICRATE + P_RandomByte();
7399 break;
7400 }
7401 actor->threshold++;
7402 /* FALLTHRU */
7403 case 1: // Chaingun Goop
7404 A_FaceTarget(actor);
7405 P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
7406
7407 if (actor->health > actor->info->damage)
7408 actor->movecount = TICRATE + P_RandomByte()/3;
7409 else
7410 actor->movecount = TICRATE + P_RandomByte()/2;
7411 break;
7412 case 2: // Homing Missile
7413 A_FaceTarget(actor);
7414 P_SetMobjState(actor, actor->info->missilestate);
7415 S_StartSound(0, sfx_beflap);
7416 break;
7417 }
7418
7419 actor->threshold++;
7420 actor->threshold %= 3;
7421 return;
7422 }
7423
7424 // possibly choose another target
7425 if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
7426 && P_BossTargetPlayer(actor, false))
7427 return; // got a new target
7428
7429 if (leveltime & 1)
7430 {
7431 // chase towards player
7432 if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
7433 P_NewChaseDir(actor);
7434 }
7435 }
7436
7437 // Function: A_GoopSplat
7438 //
7439 // Description: Black Eggman goop hits a target and sticks around for awhile.
7440 //
7441 // var1 = unused
7442 // var2 = unused
7443 //
A_GoopSplat(mobj_t * actor)7444 void A_GoopSplat(mobj_t *actor)
7445 {
7446 if (LUA_CallAction(A_GOOPSPLAT, actor))
7447 return;
7448
7449 P_UnsetThingPosition(actor);
7450 if (sector_list)
7451 {
7452 P_DelSeclist(sector_list);
7453 sector_list = NULL;
7454 }
7455 actor->flags = MF_SPECIAL; // Not a typo
7456 P_SetThingPosition(actor);
7457 }
7458
7459 // Function: A_Boss2PogoSFX
7460 //
7461 // Description: Pogoing for Boss 2
7462 //
7463 // var1 = pogo jump strength
7464 // var2 = idle pogo speed
7465 //
A_Boss2PogoSFX(mobj_t * actor)7466 void A_Boss2PogoSFX(mobj_t *actor)
7467 {
7468 INT32 locvar1 = var1;
7469 INT32 locvar2 = var2;
7470
7471 if (LUA_CallAction(A_BOSS2POGOSFX, actor))
7472 return;
7473
7474 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
7475 {
7476 // look for a new target
7477 if (P_LookForPlayers(actor, true, false, 0))
7478 return; // got a new target
7479
7480 return;
7481 }
7482
7483 // Boing!
7484 if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(256*FRACUNIT, actor->scale))
7485 {
7486 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
7487 P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
7488 // pogo on player
7489 }
7490 else
7491 {
7492 UINT8 prandom = P_RandomByte();
7493 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
7494 P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale));
7495 }
7496 if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
7497 actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
7498 actor->reactiontime = 1;
7499 }
7500
7501 // Function: A_Boss2PogoTarget
7502 //
7503 // Description: Pogoing for Boss 2, tries to actually land on the player directly.
7504 //
7505 // var1 = pogo jump strength
7506 // var2 = idle pogo speed
7507 //
A_Boss2PogoTarget(mobj_t * actor)7508 void A_Boss2PogoTarget(mobj_t *actor)
7509 {
7510 INT32 locvar1 = var1;
7511 INT32 locvar2 = var2;
7512
7513 if (LUA_CallAction(A_BOSS2POGOTARGET, actor))
7514 return;
7515
7516 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing])
7517 || P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) >= FixedMul(512*FRACUNIT, actor->scale))
7518 {
7519 // look for a new target
7520 if (P_LookForPlayers(actor, true, false, 512*FRACUNIT))
7521 ; // got a new target
7522 else if (P_LookForPlayers(actor, true, false, 0))
7523 ; // got a new target
7524 else
7525 return;
7526 }
7527
7528 // Target hit, retreat!
7529 if ((actor->target->player && actor->target->player->powers[pw_flashing] > TICRATE) || actor->flags2 & MF2_FRET)
7530 {
7531 UINT8 prandom = P_RandomByte();
7532 actor->z++; // unstick from the floor
7533 actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
7534 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
7535 P_InstaThrust(actor, actor->angle+ANGLE_180, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
7536 }
7537 // Try to land on top of the player.
7538 else if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(512*FRACUNIT, actor->scale))
7539 {
7540 fixed_t airtime, gravityadd, zoffs, height;
7541
7542 // check gravity in the sector (for later math)
7543 P_CheckGravity(actor, true);
7544 gravityadd = actor->momz;
7545
7546 actor->z++; // unstick from the floor
7547 actor->momz = FixedMul(locvar1 + (locvar1>>2), actor->scale); // Bounce up in air
7548
7549 /*badmath = 0;
7550 airtime = 0;
7551 do {
7552 badmath += momz;
7553 momz += gravityadd;
7554 airtime++;
7555 } while(badmath > 0);
7556 airtime = 2*airtime<<FRACBITS;
7557 */
7558
7559 // Remember, kids!
7560 // Reduced down Calculus lets you avoid bad 'logic math' loops!
7561 //airtime = FixedDiv(-actor->momz<<1, gravityadd)<<1; // going from 0 to 0 is much simpler
7562
7563 if (actor->target->player)
7564 height = P_GetPlayerHeight(actor->target->player) >> 1;
7565 else
7566 height = actor->target->height >> 1;
7567
7568 zoffs = height + (actor->target->floorz - actor->floorz); // offset by the difference in floor height plus half the player height,
7569 airtime = FixedDiv((-actor->momz - FixedSqrt(FixedMul(actor->momz,actor->momz)+zoffs)), gravityadd)<<1; // to try and land on their head rather than on their feet
7570
7571 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
7572 P_InstaThrust(actor, actor->angle, FixedDiv(P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y), airtime));
7573 }
7574 // Wander semi-randomly towards the player to get closer.
7575 else
7576 {
7577 UINT8 prandom = P_RandomByte();
7578 actor->z++; // unstick from the floor
7579 actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
7580 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
7581 P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
7582 }
7583 // Boing!
7584 if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
7585
7586 if (actor->info->missilestate) // spawn the pogo stick collision box
7587 {
7588 mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate);
7589 P_SetTarget(&pogo->target, actor);
7590 }
7591
7592 actor->reactiontime = 1;
7593 }
7594
7595 // Function: A_EggmanBox
7596 //
7597 // Description: Harms the player
7598 //
7599 // var1 = unused
7600 // var2 = unused
7601 //
A_EggmanBox(mobj_t * actor)7602 void A_EggmanBox(mobj_t *actor)
7603 {
7604 if (LUA_CallAction(A_EGGMANBOX, actor))
7605 return;
7606
7607 if (!actor->target || !actor->target->player)
7608 {
7609 CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
7610 return;
7611 }
7612
7613 P_DamageMobj(actor->target, actor, actor, 1, 0); // Ow!
7614 }
7615
7616 // Function: A_TurretFire
7617 //
7618 // Description: Initiates turret fire.
7619 //
7620 // var1 = object # to repeatedly fire
7621 // var2 = distance threshold
7622 //
A_TurretFire(mobj_t * actor)7623 void A_TurretFire(mobj_t *actor)
7624 {
7625 INT32 count = 0;
7626 fixed_t dist;
7627 INT32 locvar1 = var1;
7628 INT32 locvar2 = var2;
7629
7630 if (LUA_CallAction(A_TURRETFIRE, actor))
7631 return;
7632
7633 if (locvar2)
7634 dist = FixedMul(locvar2*FRACUNIT, actor->scale);
7635 else
7636 dist = FixedMul(2048*FRACUNIT, actor->scale);
7637
7638 if (!locvar1)
7639 locvar1 = MT_TURRETLASER;
7640
7641 while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
7642 {
7643 if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
7644 {
7645 actor->flags2 |= MF2_FIRING;
7646 actor->extravalue1 = locvar1;
7647 break;
7648 }
7649
7650 count++;
7651 }
7652 }
7653
7654 // Function: A_SuperTurretFire
7655 //
7656 // Description: Initiates turret fire that even stops Super Sonic.
7657 //
7658 // var1 = object # to repeatedly fire
7659 // var2 = distance threshold
7660 //
A_SuperTurretFire(mobj_t * actor)7661 void A_SuperTurretFire(mobj_t *actor)
7662 {
7663 INT32 count = 0;
7664 fixed_t dist;
7665 INT32 locvar1 = var1;
7666 INT32 locvar2 = var2;
7667
7668 if (LUA_CallAction(A_SUPERTURRETFIRE, actor))
7669 return;
7670
7671 if (locvar2)
7672 dist = FixedMul(locvar2*FRACUNIT, actor->scale);
7673 else
7674 dist = FixedMul(2048*FRACUNIT, actor->scale);
7675
7676 if (!locvar1)
7677 locvar1 = MT_TURRETLASER;
7678
7679 while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
7680 {
7681 if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
7682 {
7683 actor->flags2 |= MF2_FIRING;
7684 actor->flags2 |= MF2_SUPERFIRE;
7685 actor->extravalue1 = locvar1;
7686 break;
7687 }
7688
7689 count++;
7690 }
7691 }
7692
7693 // Function: A_TurretStop
7694 //
7695 // Description: Stops the turret fire.
7696 //
7697 // var1 = Don't play activesound?
7698 // var2 = unused
7699 //
A_TurretStop(mobj_t * actor)7700 void A_TurretStop(mobj_t *actor)
7701 {
7702 INT32 locvar1 = var1;
7703
7704 if (LUA_CallAction(A_TURRETSTOP, actor))
7705 return;
7706
7707 actor->flags2 &= ~MF2_FIRING;
7708 actor->flags2 &= ~MF2_SUPERFIRE;
7709
7710 if (actor->target && actor->info->activesound && !locvar1)
7711 S_StartSound(actor, actor->info->activesound);
7712 }
7713
7714 // Function: A_SparkFollow
7715 //
7716 // Description: Used by the hyper sparks to rotate around their target.
7717 //
7718 // var1 = unused
7719 // var2 = unused
7720 //
A_SparkFollow(mobj_t * actor)7721 void A_SparkFollow(mobj_t *actor)
7722 {
7723 if (LUA_CallAction(A_SPARKFOLLOW, actor))
7724 return;
7725
7726 if ((!actor->target || (actor->target->health <= 0))
7727 || (actor->target->player && !actor->target->player->powers[pw_super]))
7728 {
7729 P_RemoveMobj(actor);
7730 return;
7731 }
7732
7733 actor->angle += FixedAngle(actor->info->damage*FRACUNIT);
7734 P_UnsetThingPosition(actor);
7735 {
7736 const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
7737 actor->x = actor->target->x + FixedMul(FINECOSINE(fa),FixedMul(actor->info->speed, actor->scale));
7738 actor->y = actor->target->y + FixedMul(FINESINE(fa),FixedMul(actor->info->speed, actor->scale));
7739 if (actor->target->eflags & MFE_VERTICALFLIP)
7740 actor->z = actor->target->z + actor->target->height - FixedDiv(actor->target->height,3*FRACUNIT);
7741 else
7742 actor->z = actor->target->z + FixedDiv(actor->target->height,3*FRACUNIT) - actor->height;
7743 }
7744 P_SetThingPosition(actor);
7745 }
7746
7747 // Function: A_BuzzFly
7748 //
7749 // Description: Makes an object slowly fly after a player, in the manner of a Buzz.
7750 //
7751 // var1 = sfx to play
7752 // var2 = length of sfx, set to threshold if played
7753 //
A_BuzzFly(mobj_t * actor)7754 void A_BuzzFly(mobj_t *actor)
7755 {
7756 INT32 locvar1 = var1;
7757 INT32 locvar2 = var2;
7758
7759 if (LUA_CallAction(A_BUZZFLY, actor))
7760 return;
7761
7762 if (actor->flags2 & MF2_AMBUSH)
7763 return;
7764
7765 if (actor->reactiontime)
7766 actor->reactiontime--;
7767
7768 // modify target threshold
7769 if (actor->threshold)
7770 {
7771 if (!actor->target || actor->target->health <= 0)
7772 actor->threshold = 0;
7773 else
7774 actor->threshold--;
7775 }
7776
7777 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
7778 {
7779 // look for a new target
7780 if (P_LookForPlayers(actor, true, false, 0))
7781 return; // got a new target
7782
7783 actor->momz = actor->momy = actor->momx = 0;
7784 P_SetMobjState(actor, actor->info->spawnstate);
7785 return;
7786 }
7787
7788 // turn towards movement direction if not there yet
7789 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
7790
7791 if (actor->target->health <= 0 || (!actor->threshold && !P_CheckSight(actor, actor->target)))
7792 {
7793 if ((multiplayer || netgame) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
7794 return; // got a new target
7795
7796 actor->momx = actor->momy = actor->momz = 0;
7797 P_SetMobjState(actor, actor->info->spawnstate); // Go back to looking around
7798 return;
7799 }
7800
7801 // If the player is over 3072 fracunits away, then look for another player
7802 if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
7803 actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale))
7804 {
7805 if (multiplayer || netgame)
7806 P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)); // maybe get a new target
7807
7808 return;
7809 }
7810
7811 // chase towards player
7812 {
7813 INT32 dist, realspeed;
7814 const fixed_t mf = 5*(FRACUNIT/4);
7815
7816 if (ultimatemode)
7817 realspeed = FixedMul(FixedMul(actor->info->speed,mf), actor->scale);
7818 else
7819 realspeed = FixedMul(actor->info->speed, actor->scale);
7820
7821 dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x,
7822 actor->target->y - actor->y), actor->target->z - actor->z);
7823
7824 if (dist < 1)
7825 dist = 1;
7826
7827 actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), realspeed);
7828 actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), realspeed);
7829 actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), realspeed);
7830
7831 if (actor->z+actor->momz >= actor->waterbottom && actor->watertop > actor->floorz
7832 && actor->z+actor->momz > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)
7833 && actor->z+actor->momz <= actor->watertop)
7834 {
7835 actor->momz = 0;
7836 actor->z = actor->watertop;
7837 }
7838 }
7839
7840 if (locvar1 != sfx_None && !actor->threshold)
7841 {
7842 S_StartSound(actor, locvar1);
7843 actor->threshold = locvar2;
7844 }
7845 }
7846
7847 // Function: A_GuardChase
7848 //
7849 // Description: Modified A_Chase for Egg Guard
7850 //
7851 // var1 = unused
7852 // var2 = unused
7853 //
A_GuardChase(mobj_t * actor)7854 void A_GuardChase(mobj_t *actor)
7855 {
7856 INT32 delta;
7857
7858 if (LUA_CallAction(A_GUARDCHASE, actor))
7859 return;
7860
7861 if (actor->reactiontime)
7862 actor->reactiontime--;
7863
7864 if (actor->threshold != 42) // In formation...
7865 {
7866 fixed_t speed;
7867
7868 if (!actor->tracer || !actor->tracer->health)
7869 {
7870 P_SetTarget(&actor->tracer, NULL);
7871 actor->threshold = 42;
7872 P_SetMobjState(actor, actor->info->painstate);
7873 actor->flags |= MF_SPECIAL|MF_SHOOTABLE;
7874 return;
7875 }
7876
7877 speed = actor->extravalue1*actor->scale;
7878
7879 if (actor->flags2 & MF2_AMBUSH)
7880 speed <<= 1;
7881
7882 if (speed
7883 && !P_TryMove(actor,
7884 actor->x + P_ReturnThrustX(actor, actor->angle, speed),
7885 actor->y + P_ReturnThrustY(actor, actor->angle, speed),
7886 false)
7887 && speed > 0) // can't be the same check as previous so that P_TryMove gets to happen.
7888 {
7889 if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_OBJECTSPECIAL))
7890 actor->angle += ANGLE_90;
7891 else if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_EXTRA))
7892 actor->angle -= ANGLE_90;
7893 else
7894 actor->angle += ANGLE_180;
7895 }
7896
7897 if (actor->extravalue1 < actor->info->speed)
7898 actor->extravalue1++;
7899 }
7900 else // Break ranks!
7901 {
7902 // turn towards movement direction if not there yet
7903 if (actor->movedir < NUMDIRS)
7904 {
7905 actor->angle &= (7<<29);
7906 delta = actor->angle - (actor->movedir << 29);
7907
7908 if (delta > 0)
7909 actor->angle -= ANGLE_45;
7910 else if (delta < 0)
7911 actor->angle += ANGLE_45;
7912 }
7913
7914 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
7915 {
7916 // look for a new target
7917 if (P_LookForPlayers(actor, true, false, 0))
7918 return; // got a new target
7919
7920 P_SetMobjStateNF(actor, actor->info->spawnstate);
7921 return;
7922 }
7923
7924 // possibly choose another target
7925 if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
7926 && P_LookForPlayers(actor, true, false, 0))
7927 return; // got a new target
7928
7929 // chase towards player
7930 if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
7931 {
7932 P_NewChaseDir(actor);
7933 actor->movecount += 5; // Increase tics before change in direction allowed.
7934 }
7935 }
7936
7937 // Now that we've moved, its time for our shield to move!
7938 // Otherwise it'll never act as a proper overlay.
7939 if (actor->tracer && actor->tracer->state
7940 && actor->tracer->state->action.acp1)
7941 {
7942 var1 = actor->tracer->state->var1, var2 = actor->tracer->state->var2;
7943 actor->tracer->state->action.acp1(actor->tracer);
7944 }
7945 }
7946
7947 // Function: A_EggShield
7948 //
7949 // Description: Modified A_Chase for Egg Guard's shield
7950 //
7951 // var1 = unused
7952 // var2 = unused
7953 //
A_EggShield(mobj_t * actor)7954 void A_EggShield(mobj_t *actor)
7955 {
7956 INT32 i;
7957 player_t *player;
7958 fixed_t blockdist;
7959 fixed_t newx, newy;
7960 fixed_t movex, movey;
7961 angle_t angle;
7962
7963 if (LUA_CallAction(A_EGGSHIELD, actor))
7964 return;
7965
7966 if (!actor->target || !actor->target->health)
7967 {
7968 P_RemoveMobj(actor);
7969 return;
7970 }
7971
7972 newx = actor->target->x + P_ReturnThrustX(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
7973 newy = actor->target->y + P_ReturnThrustY(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
7974
7975 movex = newx - actor->x;
7976 movey = newy - actor->y;
7977
7978 actor->angle = actor->target->angle;
7979 if (actor->target->eflags & MFE_VERTICALFLIP)
7980 {
7981 actor->eflags |= MFE_VERTICALFLIP;
7982 actor->z = actor->target->z + actor->target->height - actor->height;
7983 }
7984 else
7985 actor->z = actor->target->z;
7986
7987 actor->destscale = actor->target->destscale;
7988 P_SetScale(actor, actor->target->scale);
7989
7990 actor->floorz = actor->target->floorz;
7991 actor->ceilingz = actor->target->ceilingz;
7992
7993 if (!movex && !movey)
7994 return;
7995
7996 P_UnsetThingPosition(actor);
7997 actor->x = newx;
7998 actor->y = newy;
7999 P_SetThingPosition(actor);
8000
8001 // Search for players to push
8002 for (i = 0; i < MAXPLAYERS; i++)
8003 {
8004 if (!playeringame[i] || players[i].spectator)
8005 continue;
8006
8007 player = &players[i];
8008
8009 if (!player->mo)
8010 continue;
8011
8012 if (player->mo->z > actor->z + actor->height)
8013 continue;
8014
8015 if (player->mo->z + player->mo->height < actor->z)
8016 continue;
8017
8018 blockdist = actor->radius + player->mo->radius;
8019
8020 if (abs(actor->x - player->mo->x) >= blockdist || abs(actor->y - player->mo->y) >= blockdist)
8021 continue; // didn't hit it
8022
8023 angle = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
8024
8025 if (angle > ANGLE_90 && angle < ANGLE_270)
8026 continue;
8027
8028 // Blocked by the shield
8029 player->mo->momx += movex;
8030 player->mo->momy += movey;
8031 return;
8032 }
8033 }
8034
8035
8036 // Function: A_SetReactionTime
8037 //
8038 // Description: Sets the object's reaction time.
8039 //
8040 // var1 = 1 (use value in var2); 0 (use info table value)
8041 // var2 = if var1 = 1, then value to set
8042 //
A_SetReactionTime(mobj_t * actor)8043 void A_SetReactionTime(mobj_t *actor)
8044 {
8045 if (LUA_CallAction(A_SETREACTIONTIME, actor))
8046 return;
8047
8048 if (var1)
8049 actor->reactiontime = var2;
8050 else
8051 actor->reactiontime = actor->info->reactiontime;
8052 }
8053
8054 // Function: A_Boss1Spikeballs
8055 //
8056 // Description: Boss 1 spikeball spawning loop.
8057 //
8058 // var1 = ball number
8059 // var2 = total balls
8060 //
A_Boss1Spikeballs(mobj_t * actor)8061 void A_Boss1Spikeballs(mobj_t *actor)
8062 {
8063 INT32 locvar1 = var1;
8064 INT32 locvar2 = var2;
8065 mobj_t *ball;
8066
8067 if (LUA_CallAction(A_BOSS1SPIKEBALLS, actor))
8068 return;
8069
8070 ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL);
8071 P_SetTarget(&ball->target, actor);
8072 ball->movedir = FixedAngle(FixedMul(FixedDiv(locvar1<<FRACBITS, locvar2<<FRACBITS), 360<<FRACBITS));
8073 ball->threshold = ball->radius + actor->radius + ball->info->painchance;
8074
8075 S_StartSound(ball, ball->info->seesound);
8076 var1 = ball->state->var1, var2 = ball->state->var2;
8077 ball->state->action.acp1(ball);
8078 }
8079
8080 // Function: A_Boss3TakeDamage
8081 //
8082 // Description: Called when Boss 3 takes damage.
8083 //
8084 // var1 = movecount value
8085 // var2 = unused
8086 //
A_Boss3TakeDamage(mobj_t * actor)8087 void A_Boss3TakeDamage(mobj_t *actor)
8088 {
8089 if (LUA_CallAction(A_BOSS3TAKEDAMAGE, actor))
8090 return;
8091
8092 actor->movecount = var1;
8093 actor->movefactor = -512*FRACUNIT;
8094
8095 /*if (actor->target && actor->target->spawnpoint)
8096 actor->threshold = actor->target->spawnpoint->extrainfo;*/
8097
8098 }
8099
8100 // Function: A_Boss3Path
8101 //
8102 // Description: Does pathfinding along Boss 3's nodes.
8103 //
8104 // var1 = unused
8105 // var2 = unused
8106 //
A_Boss3Path(mobj_t * actor)8107 void A_Boss3Path(mobj_t *actor)
8108 {
8109 if (LUA_CallAction(A_BOSS3PATH, actor))
8110 return;
8111
8112 if (actor->tracer && actor->tracer->health && actor->tracer->movecount)
8113 actor->movecount |= 1;
8114 else if (actor->movecount & 1)
8115 actor->movecount = 0;
8116
8117 if (actor->movecount & 2) // We've reached a firing point?
8118 {
8119 // Wait here and pretend to be angry or something.
8120 actor->momx = 0;
8121 actor->momy = 0;
8122 actor->momz = 0;
8123 P_SetTarget(&actor->target, actor->tracer->target);
8124 var1 = 0, var2 = 0;
8125 A_FaceTarget(actor);
8126 if (actor->tracer->state == &states[actor->tracer->info->missilestate])
8127 P_SetMobjState(actor, actor->info->missilestate);
8128 return;
8129 }
8130 else if (actor->threshold >= 0) // Traveling mode
8131 {
8132 fixed_t dist = 0;
8133 fixed_t speed;
8134
8135 if (!(actor->flags2 & MF2_STRONGBOX))
8136 {
8137 thinker_t *th;
8138 mobj_t *mo2;
8139
8140 P_SetTarget(&actor->target, NULL);
8141
8142 // scan the thinkers
8143 // to find a point that matches
8144 // the number
8145 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
8146 {
8147 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
8148 continue;
8149
8150 mo2 = (mobj_t *)th;
8151 if (mo2->type != MT_BOSS3WAYPOINT)
8152 continue;
8153 if (!mo2->spawnpoint)
8154 continue;
8155 if (mo2->spawnpoint->angle != actor->threshold)
8156 continue;
8157 if (mo2->spawnpoint->extrainfo != actor->cusval)
8158 continue;
8159
8160 P_SetTarget(&actor->target, mo2);
8161 break;
8162 }
8163 }
8164
8165 if (!actor->target) // Should NEVER happen
8166 {
8167 CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy was unable to find specified waypoint: %d, %d\n", actor->threshold, actor->cusval);
8168 return;
8169 }
8170
8171 if (actor->tracer && ((actor->tracer->movedir)
8172 || (actor->tracer->health <= actor->tracer->info->damage)))
8173 speed = actor->info->speed * 2;
8174 else
8175 speed = actor->info->speed;
8176
8177 if (actor->target->x == actor->x && actor->target->y == actor->y)
8178 {
8179 dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z + actor->movefactor - actor->z);
8180
8181 if (dist < 1)
8182 dist = 1;
8183
8184 actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), speed);
8185 actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), speed);
8186 actor->momz = FixedMul(FixedDiv(actor->target->z + actor->movefactor - actor->z, dist), speed);
8187
8188 if (actor->momx != 0 || actor->momy != 0)
8189 actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy);
8190 }
8191
8192 if (dist <= speed)
8193 {
8194 // If further away, set XYZ of mobj to waypoint location
8195 P_UnsetThingPosition(actor);
8196 actor->x = actor->target->x;
8197 actor->y = actor->target->y;
8198 actor->z = actor->target->z + actor->movefactor;
8199 actor->momx = actor->momy = actor->momz = 0;
8200 P_SetThingPosition(actor);
8201
8202 if (!actor->movefactor) // firing mode
8203 {
8204 actor->movecount |= 2;
8205 actor->movefactor = -512*FRACUNIT;
8206 actor->flags2 &= ~MF2_STRONGBOX;
8207 }
8208 else if (!(actor->flags2 & MF2_STRONGBOX)) // just spawned or going down
8209 {
8210 actor->flags2 |= MF2_STRONGBOX;
8211 actor->movefactor = -512*FRACUNIT;
8212 }
8213 else if (!(actor->flags2 & MF2_AMBUSH)) // just shifted tube
8214 {
8215 actor->flags2 |= MF2_AMBUSH;
8216 actor->movefactor = 0;
8217 }
8218 else // just hit the bottom of your tube
8219 {
8220 P_RemoveMobj(actor); // Cycle completed. Dummy removed.
8221 return;
8222 }
8223 }
8224 }
8225 }
8226
8227 // Function: A_Boss3ShockThink
8228 //
8229 // Description: Inserts new interstitial shockwave objects when the space between others spreads too much.
8230 //
8231 // var1 = unused
8232 // var2 = unused
8233 //
A_Boss3ShockThink(mobj_t * actor)8234 void A_Boss3ShockThink(mobj_t *actor)
8235 {
8236 if (LUA_CallAction(A_BOSS3SHOCKTHINK, actor))
8237 return;
8238
8239 if (actor->momx || actor->momy)
8240 actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy) + ANGLE_90;
8241
8242 if (actor->hnext && !P_MobjWasRemoved(actor->hnext))
8243 {
8244 mobj_t *snext = actor->hnext;
8245 mobj_t *snew;
8246 fixed_t x0, y0, x1, y1;
8247
8248 // Break the link if movements are too different
8249 if (FixedHypot(snext->momx - actor->momx, snext->momy - actor->momy) > 12*actor->scale)
8250 {
8251 P_SetTarget(&actor->hnext, NULL);
8252 return;
8253 }
8254
8255 // Check distance between shockwave objects to determine whether interstitial ones should be spawned
8256 x0 = actor->x;
8257 y0 = actor->y;
8258 x1 = snext->x;
8259 y1 = snext->y;
8260 if (FixedHypot(x1 - x0, y1 - y0) > 2*actor->radius)
8261 {
8262 snew = P_SpawnMobj((x0 + x1) >> 1, (y0 + y1) >> 1, (actor->z + snext->z) >> 1, actor->type);
8263 snew->momx = (actor->momx + snext->momx) >> 1;
8264 snew->momy = (actor->momy + snext->momy) >> 1;
8265 snew->momz = (actor->momz + snext->momz) >> 1; // is this really needed?
8266 snew->angle = (actor->angle + snext->angle) >> 1;
8267 P_SetTarget(&snew->target, actor->target);
8268 snew->fuse = actor->fuse;
8269
8270 P_SetTarget(&actor->hnext, snew);
8271 P_SetTarget(&snew->hnext, snext);
8272 }
8273 }
8274 }
8275
8276 // Function: A_LinedefExecute
8277 //
8278 // Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well.
8279 //
8280 // var1 = tag
8281 // var2 = add angle to tag (optional)
8282 //
A_LinedefExecute(mobj_t * actor)8283 void A_LinedefExecute(mobj_t *actor)
8284 {
8285 INT32 tagnum;
8286 INT32 locvar1 = var1;
8287 INT32 locvar2 = var2;
8288
8289 if (LUA_CallAction(A_LINEDEFEXECUTE, actor))
8290 return;
8291
8292 tagnum = locvar1;
8293 // state numbers option is no more, custom states cannot be guaranteed to stay the same number anymore, now that they can be defined by names instead
8294
8295 if (locvar2)
8296 tagnum += locvar2*(AngleFixed(actor->angle)>>FRACBITS);
8297 else if (actor->spawnpoint && actor->spawnpoint->extrainfo)
8298 tagnum += (actor->spawnpoint->extrainfo*LE_PARAMWIDTH);
8299
8300 CONS_Debug(DBG_GAMELOGIC, "A_LinedefExecute: Running mobjtype %d's sector with tag %d\n", actor->type, tagnum);
8301
8302 // tag 32768 displayed in map editors is actually tag -32768, tag 32769 is -32767, 65535 is -1 etc.
8303 P_LinedefExecute((INT16)tagnum, actor, actor->subsector->sector);
8304 }
8305
8306 // Function: A_PlaySeeSound
8307 //
8308 // Description: Plays the object's seesound.
8309 //
8310 // var1 = unused
8311 // var2 = unused
8312 //
A_PlaySeeSound(mobj_t * actor)8313 void A_PlaySeeSound(mobj_t *actor)
8314 {
8315 if (LUA_CallAction(A_PLAYSEESOUND, actor))
8316 return;
8317
8318 if (actor->info->seesound)
8319 S_StartScreamSound(actor, actor->info->seesound);
8320 }
8321
8322 // Function: A_PlayAttackSound
8323 //
8324 // Description: Plays the object's attacksound.
8325 //
8326 // var1 = unused
8327 // var2 = unused
8328 //
A_PlayAttackSound(mobj_t * actor)8329 void A_PlayAttackSound(mobj_t *actor)
8330 {
8331 if (LUA_CallAction(A_PLAYATTACKSOUND, actor))
8332 return;
8333
8334 if (actor->info->attacksound)
8335 S_StartAttackSound(actor, actor->info->attacksound);
8336 }
8337
8338 // Function: A_PlayActiveSound
8339 //
8340 // Description: Plays the object's activesound.
8341 //
8342 // var1 = unused
8343 // var2 = unused
8344 //
A_PlayActiveSound(mobj_t * actor)8345 void A_PlayActiveSound(mobj_t *actor)
8346 {
8347 if (LUA_CallAction(A_PLAYACTIVESOUND, actor))
8348 return;
8349
8350 if (actor->info->activesound)
8351 S_StartSound(actor, actor->info->activesound);
8352 }
8353
8354 // Function: A_SmokeTrailer
8355 //
8356 // Description: Adds smoke trails to an object.
8357 //
8358 // var1 = object # to spawn as smoke
8359 // var2 = unused
8360 //
A_SmokeTrailer(mobj_t * actor)8361 void A_SmokeTrailer(mobj_t *actor)
8362 {
8363 mobj_t *th;
8364 INT32 locvar1 = var1;
8365
8366 if (LUA_CallAction(A_SMOKETRAILER, actor))
8367 return;
8368
8369 if (leveltime % 4)
8370 return;
8371
8372 // add the smoke behind the rocket
8373 if (actor->eflags & MFE_VERTICALFLIP)
8374 {
8375 th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z + actor->height - FixedMul(mobjinfo[locvar1].height, actor->scale), locvar1);
8376 th->flags2 |= MF2_OBJECTFLIP;
8377 }
8378 else
8379 th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z, locvar1);
8380 P_SetObjectMomZ(th, FRACUNIT, false);
8381 th->destscale = actor->scale;
8382 P_SetScale(th, actor->scale);
8383 th->tics -= P_RandomByte() & 3;
8384 if (th->tics < 1)
8385 th->tics = 1;
8386 }
8387
8388 // Function: A_SpawnObjectAbsolute
8389 //
8390 // Description: Spawns an object at an absolute position
8391 //
8392 // var1:
8393 // var1 >> 16 = x
8394 // var1 & 65535 = y
8395 // var2:
8396 // var2 >> 16 = z
8397 // var2 & 65535 = type
8398 //
A_SpawnObjectAbsolute(mobj_t * actor)8399 void A_SpawnObjectAbsolute(mobj_t *actor)
8400 {
8401 INT16 x, y, z; // Want to be sure we can use negative values
8402 mobjtype_t type;
8403 mobj_t *mo;
8404 INT32 locvar1 = var1;
8405 INT32 locvar2 = var2;
8406
8407 if (LUA_CallAction(A_SPAWNOBJECTABSOLUTE, actor))
8408 return;
8409
8410 x = (INT16)(locvar1>>16);
8411 y = (INT16)(locvar1&65535);
8412 z = (INT16)(locvar2>>16);
8413 type = (mobjtype_t)(locvar2&65535);
8414
8415 mo = P_SpawnMobj(x<<FRACBITS, y<<FRACBITS, z<<FRACBITS, type);
8416
8417 // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
8418 mo->angle = actor->angle;
8419
8420 if (actor->eflags & MFE_VERTICALFLIP)
8421 mo->flags2 |= MF2_OBJECTFLIP;
8422 }
8423
8424 // Function: A_SpawnObjectRelative
8425 //
8426 // Description: Spawns an object relative to the location of the actor
8427 //
8428 // var1:
8429 // var1 >> 16 = x
8430 // var1 & 65535 = y
8431 // var2:
8432 // var2 >> 16 = z
8433 // var2 & 65535 = type
8434 //
A_SpawnObjectRelative(mobj_t * actor)8435 void A_SpawnObjectRelative(mobj_t *actor)
8436 {
8437 INT16 x, y, z; // Want to be sure we can use negative values
8438 mobjtype_t type;
8439 mobj_t *mo;
8440 INT32 locvar1 = var1;
8441 INT32 locvar2 = var2;
8442
8443 if (LUA_CallAction(A_SPAWNOBJECTRELATIVE, actor))
8444 return;
8445
8446 CONS_Debug(DBG_GAMELOGIC, "A_SpawnObjectRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
8447
8448 x = (INT16)(locvar1>>16);
8449 y = (INT16)(locvar1&65535);
8450 z = (INT16)(locvar2>>16);
8451 type = (mobjtype_t)(locvar2&65535);
8452
8453 // Spawn objects correctly in reverse gravity.
8454 // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
8455 mo = P_SpawnMobj(actor->x + FixedMul(x<<FRACBITS, actor->scale),
8456 actor->y + FixedMul(y<<FRACBITS, actor->scale),
8457 (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<<FRACBITS, actor->scale)) : (actor->z + FixedMul(z<<FRACBITS, actor->scale)), type);
8458
8459 // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
8460 mo->angle = actor->angle;
8461
8462 if (actor->eflags & MFE_VERTICALFLIP)
8463 mo->flags2 |= MF2_OBJECTFLIP;
8464
8465 }
8466
8467 // Function: A_ChangeAngleRelative
8468 //
8469 // Description: Changes the angle to a random relative value between the min and max. Set min and max to the same value to eliminate randomness
8470 //
8471 // var1 = min
8472 // var2 = max
8473 //
A_ChangeAngleRelative(mobj_t * actor)8474 void A_ChangeAngleRelative(mobj_t *actor)
8475 {
8476 // Oh god, the old code /sucked/. Changed this and the absolute version to get a random range using amin and amax instead of
8477 // getting a random angle from the _entire_ spectrum and then clipping. While we're at it, do the angle conversion to the result
8478 // rather than the ranges, so <0 and >360 work as possible values. -Red
8479 INT32 locvar1 = var1;
8480 INT32 locvar2 = var2;
8481 //angle_t angle = (P_RandomByte()+1)<<24;
8482 const fixed_t amin = locvar1*FRACUNIT;
8483 const fixed_t amax = locvar2*FRACUNIT;
8484 //const angle_t amin = FixedAngle(locvar1*FRACUNIT);
8485 //const angle_t amax = FixedAngle(locvar2*FRACUNIT);
8486
8487 if (LUA_CallAction(A_CHANGEANGLERELATIVE, actor))
8488 return;
8489
8490 #ifdef PARANOIA
8491 if (amin > amax)
8492 I_Error("A_ChangeAngleRelative: var1 is greater than var2");
8493 #endif
8494 /*
8495 if (angle < amin)
8496 angle = amin;
8497 if (angle > amax)
8498 angle = amax;*/
8499
8500 actor->angle += FixedAngle(P_RandomRange(amin, amax));
8501 }
8502
8503 // Function: A_ChangeAngleAbsolute
8504 //
8505 // Description: Changes the angle to a random absolute value between the min and max. Set min and max to the same value to eliminate randomness
8506 //
8507 // var1 = min
8508 // var2 = max
8509 //
A_ChangeAngleAbsolute(mobj_t * actor)8510 void A_ChangeAngleAbsolute(mobj_t *actor)
8511 {
8512 INT32 locvar1 = var1;
8513 INT32 locvar2 = var2;
8514 //angle_t angle = (P_RandomByte()+1)<<24;
8515 const fixed_t amin = locvar1*FRACUNIT;
8516 const fixed_t amax = locvar2*FRACUNIT;
8517 //const angle_t amin = FixedAngle(locvar1*FRACUNIT);
8518 //const angle_t amax = FixedAngle(locvar2*FRACUNIT);
8519
8520 if (LUA_CallAction(A_CHANGEANGLEABSOLUTE, actor))
8521 return;
8522
8523 #ifdef PARANOIA
8524 if (amin > amax)
8525 I_Error("A_ChangeAngleAbsolute: var1 is greater than var2");
8526 #endif
8527 /*
8528 if (angle < amin)
8529 angle = amin;
8530 if (angle > amax)
8531 angle = amax;*/
8532
8533 actor->angle = FixedAngle(P_RandomRange(amin, amax));
8534 }
8535
8536 // Function: A_RollAngle
8537 //
8538 // Description: Changes the roll angle.
8539 //
8540 // var1 = angle
8541 // var2 = relative? (default)
8542 //
A_RollAngle(mobj_t * actor)8543 void A_RollAngle(mobj_t *actor)
8544 {
8545 INT32 locvar1 = var1;
8546 INT32 locvar2 = var2;
8547 const angle_t angle = FixedAngle(locvar1*FRACUNIT);
8548
8549 if (LUA_CallAction(A_ROLLANGLE, actor))
8550 return;
8551
8552 // relative (default)
8553 if (!locvar2)
8554 actor->rollangle += angle;
8555 // absolute
8556 else
8557 actor->rollangle = angle;
8558 }
8559
8560 // Function: A_ChangeRollAngleRelative
8561 //
8562 // Description: Changes the roll angle to a random relative value between the min and max. Set min and max to the same value to eliminate randomness
8563 //
8564 // var1 = min
8565 // var2 = max
8566 //
A_ChangeRollAngleRelative(mobj_t * actor)8567 void A_ChangeRollAngleRelative(mobj_t *actor)
8568 {
8569 INT32 locvar1 = var1;
8570 INT32 locvar2 = var2;
8571 const fixed_t amin = locvar1*FRACUNIT;
8572 const fixed_t amax = locvar2*FRACUNIT;
8573
8574 if (LUA_CallAction(A_CHANGEROLLANGLERELATIVE, actor))
8575 return;
8576
8577 #ifdef PARANOIA
8578 if (amin > amax)
8579 I_Error("A_ChangeRollAngleRelative: var1 is greater than var2");
8580 #endif
8581
8582 actor->rollangle += FixedAngle(P_RandomRange(amin, amax));
8583 }
8584
8585 // Function: A_ChangeRollAngleAbsolute
8586 //
8587 // Description: Changes the roll angle to a random absolute value between the min and max. Set min and max to the same value to eliminate randomness
8588 //
8589 // var1 = min
8590 // var2 = max
8591 //
A_ChangeRollAngleAbsolute(mobj_t * actor)8592 void A_ChangeRollAngleAbsolute(mobj_t *actor)
8593 {
8594 INT32 locvar1 = var1;
8595 INT32 locvar2 = var2;
8596 const fixed_t amin = locvar1*FRACUNIT;
8597 const fixed_t amax = locvar2*FRACUNIT;
8598
8599 if (LUA_CallAction(A_CHANGEROLLANGLEABSOLUTE, actor))
8600 return;
8601
8602 #ifdef PARANOIA
8603 if (amin > amax)
8604 I_Error("A_ChangeRollAngleAbsolute: var1 is greater than var2");
8605 #endif
8606
8607 actor->rollangle = FixedAngle(P_RandomRange(amin, amax));
8608 }
8609
8610 // Function: A_PlaySound
8611 //
8612 // Description: Plays a sound
8613 //
8614 // var1 = sound # to play
8615 // var2:
8616 // lower 16 bits = If 1, play sound using calling object as origin. If 0, play sound without an origin
8617 // upper 16 bits = If 1, do not play sound during preticker.
8618 //
A_PlaySound(mobj_t * actor)8619 void A_PlaySound(mobj_t *actor)
8620 {
8621 INT32 locvar1 = var1;
8622 INT32 locvar2 = var2;
8623
8624 if (LUA_CallAction(A_PLAYSOUND, actor))
8625 return;
8626
8627 if (leveltime < 2 && (locvar2 >> 16))
8628 return;
8629
8630 S_StartSound((locvar2 & 65535) ? actor : NULL, locvar1);
8631 }
8632
8633 // Function: A_FindTarget
8634 //
8635 // Description: Finds the nearest/furthest mobj of the specified type and sets actor->target to it.
8636 //
8637 // var1 = mobj type
8638 // var2 = if (0) nearest; else furthest;
8639 //
A_FindTarget(mobj_t * actor)8640 void A_FindTarget(mobj_t *actor)
8641 {
8642 INT32 locvar1 = var1;
8643 INT32 locvar2 = var2;
8644 mobj_t *targetedmobj = NULL;
8645 thinker_t *th;
8646 mobj_t *mo2;
8647 fixed_t dist1 = 0, dist2 = 0;
8648
8649 if (LUA_CallAction(A_FINDTARGET, actor))
8650 return;
8651
8652 CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
8653
8654 // scan the thinkers
8655 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
8656 {
8657 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
8658 continue;
8659
8660 mo2 = (mobj_t *)th;
8661
8662 if (mo2->type == (mobjtype_t)locvar1)
8663 {
8664 if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
8665 continue; // Ignore spectators
8666 if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
8667 continue; // Ignore dead things
8668 if (targetedmobj == NULL)
8669 {
8670 targetedmobj = mo2;
8671 dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
8672 }
8673 else
8674 {
8675 dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
8676
8677 if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
8678 {
8679 targetedmobj = mo2;
8680 dist2 = dist1;
8681 }
8682 }
8683 }
8684 }
8685
8686 if (!targetedmobj)
8687 {
8688 CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Unable to find the specified object to target.\n");
8689 return; // Oops, nothing found..
8690 }
8691
8692 CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Found a target.\n");
8693
8694 P_SetTarget(&actor->target, targetedmobj);
8695 }
8696
8697 // Function: A_FindTracer
8698 //
8699 // Description: Finds the nearest/furthest mobj of the specified type and sets actor->tracer to it.
8700 //
8701 // var1 = mobj type
8702 // var2 = if (0) nearest; else furthest;
8703 //
A_FindTracer(mobj_t * actor)8704 void A_FindTracer(mobj_t *actor)
8705 {
8706 INT32 locvar1 = var1;
8707 INT32 locvar2 = var2;
8708 mobj_t *targetedmobj = NULL;
8709 thinker_t *th;
8710 mobj_t *mo2;
8711 fixed_t dist1 = 0, dist2 = 0;
8712
8713 if (LUA_CallAction(A_FINDTRACER, actor))
8714 return;
8715
8716 CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
8717
8718 // scan the thinkers
8719 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
8720 {
8721 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
8722 continue;
8723
8724 mo2 = (mobj_t *)th;
8725
8726 if (mo2->type == (mobjtype_t)locvar1)
8727 {
8728 if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
8729 continue; // Ignore spectators
8730 if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
8731 continue; // Ignore dead things
8732 if (targetedmobj == NULL)
8733 {
8734 targetedmobj = mo2;
8735 dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
8736 }
8737 else
8738 {
8739 dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
8740
8741 if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
8742 {
8743 targetedmobj = mo2;
8744 dist2 = dist1;
8745 }
8746 }
8747 }
8748 }
8749
8750 if (!targetedmobj)
8751 {
8752 CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Unable to find the specified object to target.\n");
8753 return; // Oops, nothing found..
8754 }
8755
8756 CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Found a target.\n");
8757
8758 P_SetTarget(&actor->tracer, targetedmobj);
8759 }
8760
8761 // Function: A_SetTics
8762 //
8763 // Description: Sets the animation tics of an object
8764 //
8765 // var1 = tics to set to
8766 // var2 = if this is set, and no var1 is supplied, the mobj's threshold value will be used.
8767 //
A_SetTics(mobj_t * actor)8768 void A_SetTics(mobj_t *actor)
8769 {
8770 INT32 locvar1 = var1;
8771 INT32 locvar2 = var2;
8772
8773 if (LUA_CallAction(A_SETTICS, actor))
8774 return;
8775
8776 if (locvar1)
8777 actor->tics = locvar1;
8778 else if (locvar2)
8779 actor->tics = actor->threshold;
8780 }
8781
8782 // Function: A_SetRandomTics
8783 //
8784 // Description: Sets the animation tics of an object to a random value
8785 //
8786 // var1 = lower bound
8787 // var2 = upper bound
8788 //
A_SetRandomTics(mobj_t * actor)8789 void A_SetRandomTics(mobj_t *actor)
8790 {
8791 INT32 locvar1 = var1;
8792 INT32 locvar2 = var2;
8793
8794 if (LUA_CallAction(A_SETRANDOMTICS, actor))
8795 return;
8796
8797 actor->tics = P_RandomRange(locvar1, locvar2);
8798 }
8799
8800 // Function: A_ChangeColorRelative
8801 //
8802 // Description: Changes the color of an object
8803 //
8804 // var1 = if (var1 > 0), find target and add its color value to yours
8805 // var2 = if (var1 = 0), color value to add
8806 //
A_ChangeColorRelative(mobj_t * actor)8807 void A_ChangeColorRelative(mobj_t *actor)
8808 {
8809 INT32 locvar1 = var1;
8810 INT32 locvar2 = var2;
8811
8812 if (LUA_CallAction(A_CHANGECOLORRELATIVE, actor))
8813 return;
8814
8815 if (locvar1)
8816 {
8817 // Have you ever seen anything so hideous?
8818 if (actor->target)
8819 actor->color = (UINT16)(actor->color + actor->target->color);
8820 }
8821 else
8822 actor->color = (UINT16)(actor->color + locvar2);
8823 }
8824
8825 // Function: A_ChangeColorAbsolute
8826 //
8827 // Description: Changes the color of an object by an absolute value. Note: 0 is default colormap.
8828 //
8829 // var1 = if (var1 > 0), set your color to your target's color
8830 // var2 = if (var1 = 0), color value to set to
8831 //
A_ChangeColorAbsolute(mobj_t * actor)8832 void A_ChangeColorAbsolute(mobj_t *actor)
8833 {
8834 INT32 locvar1 = var1;
8835 INT32 locvar2 = var2;
8836
8837 if (LUA_CallAction(A_CHANGECOLORABSOLUTE, actor))
8838 return;
8839
8840 if (locvar1)
8841 {
8842 if (actor->target)
8843 actor->color = actor->target->color;
8844 }
8845 else
8846 actor->color = (UINT16)locvar2;
8847 }
8848
8849 // Function: A_Dye
8850 //
8851 // Description: Colorizes an object.
8852 //
8853 // var1 = if (var1 != 0), dye your target instead of yourself
8854 // var2 = color value to dye
8855 //
A_Dye(mobj_t * actor)8856 void A_Dye(mobj_t *actor)
8857 {
8858 INT32 locvar1 = var1;
8859 INT32 locvar2 = var2;
8860
8861 mobj_t *target = ((locvar1 && actor->target) ? actor->target : actor);
8862 UINT16 color = (UINT16)locvar2;
8863 if (LUA_CallAction(A_DYE, actor))
8864 return;
8865 if (color >= numskincolors)
8866 return;
8867
8868 // What if it's a player?
8869 if (target->player)
8870 target->player->powers[pw_dye] = color;
8871
8872 if (!color)
8873 {
8874 target->colorized = false;
8875 target->color = target->player ? target->player->skincolor : SKINCOLOR_NONE;
8876 }
8877 else if (!(target->player))
8878 {
8879 target->colorized = true;
8880 target->color = color;
8881 }
8882 }
8883
8884 // Function: A_MoveRelative
8885 //
8886 // Description: Moves an object (wrapper for P_Thrust)
8887 //
8888 // var1 = angle
8889 // var2 = force
8890 //
A_MoveRelative(mobj_t * actor)8891 void A_MoveRelative(mobj_t *actor)
8892 {
8893 INT32 locvar1 = var1;
8894 INT32 locvar2 = var2;
8895
8896 if (LUA_CallAction(A_MOVERELATIVE, actor))
8897 return;
8898
8899 P_Thrust(actor, actor->angle+FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
8900 }
8901
8902 // Function: A_MoveAbsolute
8903 //
8904 // Description: Moves an object (wrapper for P_InstaThrust)
8905 //
8906 // var1 = angle
8907 // var2 = force
8908 //
A_MoveAbsolute(mobj_t * actor)8909 void A_MoveAbsolute(mobj_t *actor)
8910 {
8911 INT32 locvar1 = var1;
8912 INT32 locvar2 = var2;
8913
8914 if (LUA_CallAction(A_MOVEABSOLUTE, actor))
8915 return;
8916
8917 P_InstaThrust(actor, FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
8918 }
8919
8920 // Function: A_Thrust
8921 //
8922 // Description: Pushes the object horizontally at its current angle.
8923 //
8924 // var1 = amount of force
8925 // var2 = If 1, xy momentum is lost. If 0, xy momentum is kept
8926 //
A_Thrust(mobj_t * actor)8927 void A_Thrust(mobj_t *actor)
8928 {
8929 INT32 locvar1 = var1;
8930 INT32 locvar2 = var2;
8931
8932 if (LUA_CallAction(A_THRUST, actor))
8933 return;
8934
8935 if (!locvar1)
8936 CONS_Debug(DBG_GAMELOGIC, "A_Thrust: Var1 not specified!\n");
8937
8938 if (locvar2)
8939 P_InstaThrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
8940 else
8941 P_Thrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
8942 }
8943
8944 // Function: A_ZThrust
8945 //
8946 // Description: Pushes the object up or down.
8947 //
8948 // var1 = amount of force
8949 // var2:
8950 // lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
8951 // upper 16 bits = If 1, z momentum is lost. If 0, z momentum is kept
8952 //
A_ZThrust(mobj_t * actor)8953 void A_ZThrust(mobj_t *actor)
8954 {
8955 INT32 locvar1 = var1;
8956 INT32 locvar2 = var2;
8957
8958 if (LUA_CallAction(A_ZTHRUST, actor))
8959 return;
8960
8961 if (!locvar1)
8962 CONS_Debug(DBG_GAMELOGIC, "A_ZThrust: Var1 not specified!\n");
8963
8964 if (locvar2 & 65535)
8965 actor->momx = actor->momy = 0;
8966
8967 if (actor->eflags & MFE_VERTICALFLIP)
8968 actor->z--;
8969 else
8970 actor->z++;
8971
8972 P_SetObjectMomZ(actor, locvar1*FRACUNIT, !(locvar2 >> 16));
8973 }
8974
8975 // Function: A_SetTargetsTarget
8976 //
8977 // Description: Sets your target to the object who your target is targeting. Yikes! If it happens to be NULL, you're just out of luck.
8978 //
8979 // var1: (Your target)
8980 // 0 = target
8981 // 1 = tracer
8982 // var2: (Your target's target)
8983 // 0 = target/tracer's target
8984 // 1 = target/tracer's tracer
8985 //
A_SetTargetsTarget(mobj_t * actor)8986 void A_SetTargetsTarget(mobj_t *actor)
8987 {
8988 INT32 locvar1 = var1;
8989 INT32 locvar2 = var2;
8990 mobj_t *oldtarg = NULL, *newtarg = NULL;
8991
8992 if (LUA_CallAction(A_SETTARGETSTARGET, actor))
8993 return;
8994
8995 // actor's target
8996 if (locvar1) // or tracer
8997 oldtarg = actor->tracer;
8998 else
8999 oldtarg = actor->target;
9000
9001 if (P_MobjWasRemoved(oldtarg))
9002 return;
9003
9004 // actor's target's target!
9005 if (locvar2) // or tracer
9006 newtarg = oldtarg->tracer;
9007 else
9008 newtarg = oldtarg->target;
9009
9010 if (P_MobjWasRemoved(newtarg))
9011 return;
9012
9013 // set actor's new target
9014 if (locvar1) // or tracer
9015 P_SetTarget(&actor->tracer, newtarg);
9016 else
9017 P_SetTarget(&actor->target, newtarg);
9018 }
9019
9020 // Function: A_SetObjectFlags
9021 //
9022 // Description: Sets the flags of an object
9023 //
9024 // var1 = flag value to set
9025 // var2:
9026 // if var2 == 2, add the flag to the current flags
9027 // else if var2 == 1, remove the flag from the current flags
9028 // else if var2 == 0, set the flags to the exact value
9029 //
A_SetObjectFlags(mobj_t * actor)9030 void A_SetObjectFlags(mobj_t *actor)
9031 {
9032 INT32 locvar1 = var1;
9033 INT32 locvar2 = var2;
9034 boolean unlinkthings = false;
9035
9036 if (LUA_CallAction(A_SETOBJECTFLAGS, actor))
9037 return;
9038
9039 if (locvar2 == 2)
9040 locvar1 = actor->flags | locvar1;
9041 else if (locvar2 == 1)
9042 locvar1 = actor->flags & ~locvar1;
9043
9044 if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
9045 unlinkthings = true;
9046
9047 if (unlinkthings) {
9048 P_UnsetThingPosition(actor);
9049 if (sector_list)
9050 {
9051 P_DelSeclist(sector_list);
9052 sector_list = NULL;
9053 }
9054 }
9055
9056 actor->flags = locvar1;
9057
9058 if (unlinkthings)
9059 P_SetThingPosition(actor);
9060 }
9061
9062 // Function: A_SetObjectFlags2
9063 //
9064 // Description: Sets the flags2 of an object
9065 //
9066 // var1 = flag value to set
9067 // var2:
9068 // if var2 == 2, add the flag to the current flags
9069 // else if var2 == 1, remove the flag from the current flags
9070 // else if var2 == 0, set the flags to the exact value
9071 //
A_SetObjectFlags2(mobj_t * actor)9072 void A_SetObjectFlags2(mobj_t *actor)
9073 {
9074 INT32 locvar1 = var1;
9075 INT32 locvar2 = var2;
9076
9077 if (LUA_CallAction(A_SETOBJECTFLAGS2, actor))
9078 return;
9079
9080 if (locvar2 == 2)
9081 actor->flags2 |= locvar1;
9082 else if (locvar2 == 1)
9083 actor->flags2 &= ~locvar1;
9084 else
9085 actor->flags2 = locvar1;
9086 }
9087
9088 // Function: A_BossJetFume
9089 //
9090 // Description: Spawns jet fumes/other attachment miscellany for the boss. To only be used when he is spawned.
9091 //
9092 // var1:
9093 // 0 - Triple jet fume pattern
9094 // 1 - Unused (formerly Boss 3's propeller)
9095 // 2 - Metal Sonic jet fume
9096 // 3 - Boss 4 jet flame
9097 // var2 = unused
9098 //
A_BossJetFume(mobj_t * actor)9099 void A_BossJetFume(mobj_t *actor)
9100 {
9101 mobj_t *filler;
9102 INT32 locvar1 = var1;
9103
9104 if (LUA_CallAction(A_BOSSJETFUME, actor))
9105 return;
9106
9107 if (locvar1 == 0) // Boss1 jet fumes
9108 {
9109 fixed_t jetx, jety, jetz;
9110
9111 jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
9112 jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
9113 if (actor->eflags & MFE_VERTICALFLIP)
9114 jetz = actor->z + actor->height - FixedMul(38*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
9115 else
9116 jetz = actor->z + FixedMul(38*FRACUNIT, actor->scale);
9117
9118 filler = P_SpawnMobj(jetx, jety, jetz, MT_JETFUME1);
9119 P_SetTarget(&filler->target, actor);
9120 filler->destscale = actor->scale;
9121 P_SetScale(filler, filler->destscale);
9122 if (actor->eflags & MFE_VERTICALFLIP)
9123 filler->flags2 |= MF2_OBJECTFLIP;
9124 filler->fuse = 56;
9125
9126 if (actor->eflags & MFE_VERTICALFLIP)
9127 jetz = actor->z + actor->height - FixedMul(12*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
9128 else
9129 jetz = actor->z + FixedMul(12*FRACUNIT, actor->scale);
9130
9131 filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
9132 jety + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
9133 jetz, MT_JETFUME1);
9134 P_SetTarget(&filler->target, actor);
9135 filler->destscale = actor->scale;
9136 P_SetScale(filler, filler->destscale);
9137 if (actor->eflags & MFE_VERTICALFLIP)
9138 filler->flags2 |= MF2_OBJECTFLIP;
9139 filler->fuse = 57;
9140
9141 filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
9142 jety + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
9143 jetz, MT_JETFUME1);
9144 P_SetTarget(&filler->target, actor);
9145 filler->destscale = actor->scale;
9146 P_SetScale(filler, filler->destscale);
9147 if (actor->eflags & MFE_VERTICALFLIP)
9148 filler->flags2 |= MF2_OBJECTFLIP;
9149 filler->fuse = 58;
9150
9151 P_SetTarget(&actor->tracer, filler);
9152 }
9153 /*else if (locvar1 == 1) // Boss 3 propeller
9154 {
9155 fixed_t jetx, jety, jetz;
9156
9157 jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -60*actor->scale);
9158 jety = actor->y + P_ReturnThrustY(actor, actor->angle, -60*actor->scale);
9159 if (actor->eflags & MFE_VERTICALFLIP)
9160 jetz = actor->z + actor->height - FixedMul(17*FRACUNIT + mobjinfo[MT_PROPELLER].height, actor->scale);
9161 else
9162 jetz = actor->z + FixedMul(17*FRACUNIT, actor->scale);
9163
9164 filler = P_SpawnMobj(jetx, jety, jetz, MT_PROPELLER);
9165 P_SetTarget(&filler->target, actor);
9166 filler->destscale = actor->scale;
9167 P_SetScale(filler, filler->destscale);
9168 if (actor->eflags & MFE_VERTICALFLIP)
9169 filler->flags2 |= MF2_OBJECTFLIP;
9170 filler->angle = actor->angle - ANGLE_180;
9171
9172 P_SetTarget(&actor->tracer, filler);
9173 }*/
9174 else if (locvar1 == 2) // Metal Sonic jet fumes
9175 {
9176 filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1);
9177 P_SetTarget(&filler->target, actor);
9178 filler->fuse = 59;
9179 P_SetTarget(&actor->tracer, filler);
9180 P_SetScale(filler, (filler->destscale = actor->scale/3));
9181 if (actor->eflags & MFE_VERTICALFLIP)
9182 filler->flags2 |= MF2_OBJECTFLIP;
9183 filler->color = SKINCOLOR_ICY;
9184 filler->colorized = true;
9185 }
9186 else if (locvar1 == 3) // Boss 4 jet flame
9187 {
9188 fixed_t jetz;
9189 if (actor->eflags & MFE_VERTICALFLIP)
9190 jetz = actor->z + actor->height + FixedMul(50*FRACUNIT - mobjinfo[MT_JETFLAME].height, actor->scale);
9191 else
9192 jetz = actor->z - 50*actor->scale;
9193 filler = P_SpawnMobj(actor->x, actor->y, jetz, MT_JETFLAME);
9194 P_SetTarget(&filler->target, actor);
9195 // Boss 4 already uses its tracer for other things
9196 filler->destscale = actor->scale;
9197 P_SetScale(filler, filler->destscale);
9198 if (actor->eflags & MFE_VERTICALFLIP)
9199 filler->flags2 |= MF2_OBJECTFLIP;
9200 }
9201 else if (locvar1 == 4) // Boss 4 Spectator Eggrobo jet flame
9202 {
9203 fixed_t jetx, jety, jetz, movefactor = 12;
9204
9205 jetz = actor->z;
9206 if (actor->eflags & MFE_VERTICALFLIP)
9207 jetz += (actor->height - FixedMul(mobjinfo[MT_EGGROBO1JET].height, actor->scale));
9208
9209 while (true)
9210 {
9211 jetx = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, movefactor*actor->scale) - P_ReturnThrustX(actor, actor->angle, 19*actor->scale);
9212 jety = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, movefactor*actor->scale) - P_ReturnThrustY(actor, actor->angle, 19*actor->scale);
9213 filler = P_SpawnMobj(jetx, jety, jetz, MT_EGGROBO1JET);
9214 filler->movefactor = movefactor;
9215 P_SetTarget(&filler->target, actor);
9216 filler->destscale = actor->scale;
9217 P_SetScale(filler, filler->destscale);
9218 if (actor->eflags & MFE_VERTICALFLIP)
9219 filler->flags2 |= MF2_OBJECTFLIP;
9220 if (movefactor <= 0)
9221 break;
9222 movefactor = -movefactor;
9223 }
9224 }
9225 }
9226
9227 // Function: A_RandomState
9228 //
9229 // Description: Chooses one of the two state numbers supplied randomly.
9230 //
9231 // var1 = state number 1
9232 // var2 = state number 2
9233 //
A_RandomState(mobj_t * actor)9234 void A_RandomState(mobj_t *actor)
9235 {
9236 INT32 locvar1 = var1;
9237 INT32 locvar2 = var2;
9238
9239 if (LUA_CallAction(A_RANDOMSTATE, actor))
9240 return;
9241
9242 P_SetMobjState(actor, P_RandomChance(FRACUNIT/2) ? locvar1 : locvar2);
9243 }
9244
9245 // Function: A_RandomStateRange
9246 //
9247 // Description: Chooses a random state within the range supplied.
9248 //
9249 // var1 = Minimum state number to choose.
9250 // var2 = Maximum state number to use.
9251 //
A_RandomStateRange(mobj_t * actor)9252 void A_RandomStateRange(mobj_t *actor)
9253 {
9254 INT32 locvar1 = var1;
9255 INT32 locvar2 = var2;
9256
9257 if (LUA_CallAction(A_RANDOMSTATERANGE, actor))
9258 return;
9259
9260 P_SetMobjState(actor, P_RandomRange(locvar1, locvar2));
9261 }
9262
9263 // Function: A_DualAction
9264 //
9265 // Description: Calls two actions. Be careful, if you reference the same state this action is called from, you can create an infinite loop.
9266 //
9267 // var1 = state # to use 1st action from
9268 // var2 = state # to use 2nd action from
9269 //
A_DualAction(mobj_t * actor)9270 void A_DualAction(mobj_t *actor)
9271 {
9272 INT32 locvar1 = var1;
9273 INT32 locvar2 = var2;
9274
9275 if (LUA_CallAction(A_DUALACTION, actor))
9276 return;
9277
9278 CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
9279
9280 var1 = states[locvar1].var1;
9281 var2 = states[locvar1].var2;
9282 astate = &states[locvar1];
9283
9284 CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling First Action (state %d)...\n", locvar1);
9285 states[locvar1].action.acp1(actor);
9286
9287 var1 = states[locvar2].var1;
9288 var2 = states[locvar2].var2;
9289 astate = &states[locvar2];
9290
9291 CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling Second Action (state %d)...\n", locvar2);
9292 states[locvar2].action.acp1(actor);
9293 }
9294
9295 // Function: A_RemoteAction
9296 //
9297 // Description: var1 is the remote object. var2 is the state reference for calling the action called on var1. var1 becomes the actor's target, the action (var2) is called on var1. actor's target is restored
9298 //
9299 // var1 = remote object (-2 uses tracer, -1 uses target)
9300 // var2 = state reference for calling an action
9301 //
A_RemoteAction(mobj_t * actor)9302 void A_RemoteAction(mobj_t *actor)
9303 {
9304 INT32 locvar1 = var1;
9305 INT32 locvar2 = var2;
9306 mobj_t *originaltarget = actor->target; // Hold on to the target for later.
9307
9308 if (LUA_CallAction(A_REMOTEACTION, actor))
9309 return;
9310
9311 // If >=0, find the closest target.
9312 if (locvar1 >= 0)
9313 {
9314 ///* DO A_FINDTARGET STUFF *///
9315 mobj_t *targetedmobj = NULL;
9316 thinker_t *th;
9317 mobj_t *mo2;
9318 fixed_t dist1 = 0, dist2 = 0;
9319
9320 // scan the thinkers
9321 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
9322 {
9323 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
9324 continue;
9325
9326 mo2 = (mobj_t *)th;
9327
9328 if (mo2->type == (mobjtype_t)locvar1)
9329 {
9330 if (targetedmobj == NULL)
9331 {
9332 targetedmobj = mo2;
9333 dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
9334 }
9335 else
9336 {
9337 dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
9338
9339 if ((locvar2 && dist1 < dist2) || (!locvar2 && dist1 > dist2))
9340 {
9341 targetedmobj = mo2;
9342 dist2 = dist1;
9343 }
9344 }
9345 }
9346 }
9347
9348 if (!targetedmobj)
9349 {
9350 CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Unable to find the specified object to target.\n");
9351 return; // Oops, nothing found..
9352 }
9353
9354 CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Found a target.\n");
9355
9356 P_SetTarget(&actor->target, targetedmobj);
9357
9358 ///* END A_FINDTARGET STUFF *///
9359 }
9360
9361 // If -2, use the tracer as the target
9362 else if (locvar1 == -2)
9363 P_SetTarget(&actor->target, actor->tracer);
9364 // if -1 or anything else, just use the target.
9365
9366 if (actor->target)
9367 {
9368 // Steal the var1 and var2 from "locvar2"
9369 var1 = states[locvar2].var1;
9370 var2 = states[locvar2].var2;
9371 astate = &states[locvar2];
9372
9373 CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Calling action on %p\n"
9374 "var1 is %d\nvar2 is %d\n", actor->target, var1, var2);
9375 states[locvar2].action.acp1(actor->target);
9376 }
9377
9378 P_SetTarget(&actor->target, originaltarget); // Restore the original target.
9379 }
9380
9381 // Function: A_ToggleFlameJet
9382 //
9383 // Description: Turns a flame jet on and off.
9384 //
9385 // var1 = unused
9386 // var2 = unused
9387 //
A_ToggleFlameJet(mobj_t * actor)9388 void A_ToggleFlameJet(mobj_t* actor)
9389 {
9390 if (LUA_CallAction(A_TOGGLEFLAMEJET, actor))
9391 return;
9392
9393 // threshold - off delay
9394 // movecount - on timer
9395
9396 if (actor->flags2 & MF2_FIRING)
9397 {
9398 actor->flags2 &= ~MF2_FIRING;
9399
9400 if (actor->threshold)
9401 actor->tics = actor->threshold;
9402 }
9403 else
9404 {
9405 actor->flags2 |= MF2_FIRING;
9406
9407 if (actor->movecount)
9408 actor->tics = actor->movecount;
9409 }
9410 }
9411
9412 // Function: A_OrbitNights
9413 //
9414 // Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.)
9415 //
9416 // var1 = Angle adjustment (aka orbit speed)
9417 // var2:
9418 // Bits 1-10: height offset, max 1023
9419 // Bits 11-16: X radius factor (max 63, default 20)
9420 // Bit 17: set if object is Nightopian Helper
9421 // Bit 18: set to define X/Y/Z rotation factor
9422 // Bits 19-20: Unused
9423 // Bits 21-26: Y radius factor (max 63, default 32)
9424 // Bits 27-32: Z radius factor (max 63, default 32)
9425 //
9426 // If MF_GRENADEBOUNCE is flagged on mobj, use actor->threshold to define X/Y/Z radius factor, max 1023 each:
9427 // Bits 1-10: X factor
9428 // Bits 11-20: Y factor
9429 // Bits 21-30: Z factor
A_OrbitNights(mobj_t * actor)9430 void A_OrbitNights(mobj_t* actor)
9431 {
9432 INT32 ofs = (var2 & 0x3FF);
9433 boolean ishelper = (var2 & 0x10000);
9434 boolean donotrescale = (var2 & 0x40000);
9435 INT32 xfactor = 32, yfactor = 32, zfactor = 20;
9436
9437 if (LUA_CallAction(A_ORBITNIGHTS, actor))
9438 return;
9439
9440 if (actor->flags & MF_GRENADEBOUNCE)
9441 {
9442 xfactor = (actor->threshold & 0x3FF);
9443 yfactor = (actor->threshold & 0xFFC00) >> 10;
9444 zfactor = (actor->threshold & 0x3FF00000) >> 20;
9445 }
9446 else if (var2 & 0x20000)
9447 {
9448 xfactor = (var2 & 0xFC00) >> 10;
9449 yfactor = (var2 & 0x3F00000) >> 20;
9450 zfactor = (var2 & 0xFC000000) >> 26;
9451 }
9452
9453 if (!actor->target
9454 || (actor->target->player &&
9455 // if NiGHTS special stage and not NiGHTSmode.
9456 (((maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && !(actor->target->player->powers[pw_carry] == CR_NIGHTSMODE))
9457 // Also remove this object if they no longer have a NiGHTS helper
9458 || (ishelper && !actor->target->player->powers[pw_nights_helper]))))
9459 {
9460 P_RemoveMobj(actor);
9461 return;
9462 }
9463 else
9464 {
9465 actor->extravalue1 += var1;
9466 P_UnsetThingPosition(actor);
9467 {
9468 const angle_t fa = (angle_t)actor->extravalue1 >> ANGLETOFINESHIFT;
9469 const angle_t ofa = ((angle_t)actor->extravalue1 + (ofs*ANG1)) >> ANGLETOFINESHIFT;
9470
9471 const fixed_t fc = FixedMul(FINECOSINE(fa),FixedMul(xfactor*FRACUNIT, actor->scale));
9472 const fixed_t fh = FixedMul(FINECOSINE(ofa),FixedMul(zfactor*FRACUNIT, actor->scale));
9473 const fixed_t fs = FixedMul(FINESINE(fa),FixedMul(yfactor*FRACUNIT, actor->scale));
9474
9475 actor->x = actor->target->x + fc;
9476 actor->y = actor->target->y + fs;
9477 actor->z = actor->target->z + fh + FixedMul(16*FRACUNIT, actor->scale);
9478
9479 // Semi-lazy hack
9480 actor->angle = (angle_t)actor->extravalue1 + ANGLE_90;
9481 }
9482 P_SetThingPosition(actor);
9483
9484 if (ishelper && actor->target->player) // Flash a helper that's about to be removed.
9485 {
9486 if ((actor->target->player->powers[pw_nights_helper] < TICRATE)
9487 && (actor->target->player->powers[pw_nights_helper] & 1))
9488 actor->flags2 |= MF2_DONTDRAW;
9489 else
9490 actor->flags2 &= ~MF2_DONTDRAW;
9491 }
9492
9493 if (!donotrescale && actor->destscale != actor->target->destscale)
9494 actor->destscale = actor->target->destscale;
9495 }
9496 }
9497
9498 // Function: A_GhostMe
9499 //
9500 // Description: Spawns a "ghost" mobj of this actor, ala spindash trails and the minus's digging "trails"
9501 //
9502 // var1 = duration in tics
9503 // var2 = unused
9504 //
A_GhostMe(mobj_t * actor)9505 void A_GhostMe(mobj_t *actor)
9506 {
9507 INT32 locvar1 = var1;
9508 mobj_t *ghost;
9509
9510 if (LUA_CallAction(A_GHOSTME, actor))
9511 return;
9512
9513 ghost = P_SpawnGhostMobj(actor);
9514 if (ghost && locvar1 > 0)
9515 ghost->fuse = locvar1;
9516 }
9517
9518 // Function: A_SetObjectState
9519 //
9520 // Description: Changes the state of an object's target/tracer.
9521 //
9522 // var1 = state number
9523 // var2:
9524 // 0 = target
9525 // 1 = tracer
9526 //
A_SetObjectState(mobj_t * actor)9527 void A_SetObjectState(mobj_t *actor)
9528 {
9529 INT32 locvar1 = var1;
9530 INT32 locvar2 = var2;
9531 mobj_t *target;
9532
9533 if (LUA_CallAction(A_SETOBJECTSTATE, actor))
9534 return;
9535
9536 if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer))
9537 {
9538 if (cv_debug)
9539 CONS_Printf("A_SetObjectState: No target to change state!\n");
9540 return;
9541 }
9542
9543 if (!locvar2) // target
9544 target = actor->target;
9545 else // tracer
9546 target = actor->tracer;
9547
9548 if (target->health > 0)
9549 {
9550 if (!target->player)
9551 P_SetMobjState(target, locvar1);
9552 else
9553 P_SetPlayerMobjState(target, locvar1);
9554 }
9555 }
9556
9557 // Function: A_SetObjectTypeState
9558 //
9559 // Description: Changes the state of all active objects of a certain type in a certain range of the actor.
9560 //
9561 // var1 = state number
9562 // var2:
9563 // lower 16 bits = type
9564 // upper 16 bits = range (if == 0, across whole map)
9565 //
A_SetObjectTypeState(mobj_t * actor)9566 void A_SetObjectTypeState(mobj_t *actor)
9567 {
9568 INT32 locvar1 = var1;
9569 INT32 locvar2 = var2;
9570 const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
9571 const UINT16 loc2up = (UINT16)(locvar2 >> 16);
9572
9573 thinker_t *th;
9574 mobj_t *mo2;
9575 fixed_t dist = 0;
9576
9577 if (LUA_CallAction(A_SETOBJECTTYPESTATE, actor))
9578 return;
9579
9580 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
9581 {
9582 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
9583 continue;
9584
9585 mo2 = (mobj_t *)th;
9586
9587 if (mo2->type == (mobjtype_t)loc2lw)
9588 {
9589 dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
9590
9591 if (mo2->health > 0)
9592 {
9593 if (loc2up == 0)
9594 P_SetMobjState(mo2, locvar1);
9595 else
9596 {
9597 if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
9598 P_SetMobjState(mo2, locvar1);
9599 }
9600 }
9601 }
9602 }
9603 }
9604
9605 // Function: A_KnockBack
9606 //
9607 // Description: Knocks back the object's target at its current speed.
9608 //
9609 // var1:
9610 // 0 = target
9611 // 1 = tracer
9612 // var2 = unused
9613 //
A_KnockBack(mobj_t * actor)9614 void A_KnockBack(mobj_t *actor)
9615 {
9616 INT32 locvar1 = var1;
9617 mobj_t *target;
9618
9619 if (LUA_CallAction(A_KNOCKBACK, actor))
9620 return;
9621
9622 if (!locvar1)
9623 target = actor->target;
9624 else
9625 target = actor->tracer;
9626
9627 if (!target)
9628 {
9629 if(cv_debug)
9630 CONS_Printf("A_KnockBack: No target!\n");
9631 return;
9632 }
9633
9634 target->momx *= -1;
9635 target->momy *= -1;
9636 }
9637
9638 // Function: A_PushAway
9639 //
9640 // Description: Pushes an object's target away from the calling object.
9641 //
9642 // var1 = amount of force
9643 // var2:
9644 // lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
9645 // upper 16 bits = 0 - target, 1 - tracer
9646 //
A_PushAway(mobj_t * actor)9647 void A_PushAway(mobj_t *actor)
9648 {
9649 INT32 locvar1 = var1;
9650 INT32 locvar2 = var2;
9651 mobj_t *target; // target
9652 angle_t an; // actor to target angle
9653
9654 if (LUA_CallAction(A_PUSHAWAY, actor))
9655 return;
9656
9657 if ((!(locvar2 >> 16) && !actor->target) || ((locvar2 >> 16) && !actor->tracer))
9658 return;
9659
9660 if (!locvar1)
9661 CONS_Printf("A_Thrust: Var1 not specified!\n");
9662
9663 if (!(locvar2 >> 16)) // target
9664 target = actor->target;
9665 else // tracer
9666 target = actor->tracer;
9667
9668 an = R_PointToAngle2(actor->x, actor->y, target->x, target->y);
9669
9670 if (locvar2 & 65535)
9671 P_InstaThrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
9672 else
9673 P_Thrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
9674 }
9675
9676 // Function: A_RingDrain
9677 //
9678 // Description: Drain targeted player's rings.
9679 //
9680 // var1 = ammount of drained rings
9681 // var2 = unused
9682 //
A_RingDrain(mobj_t * actor)9683 void A_RingDrain(mobj_t *actor)
9684 {
9685 INT32 locvar1 = var1;
9686 player_t *player;
9687
9688 if (LUA_CallAction(A_RINGDRAIN, actor))
9689 return;
9690
9691 if (!actor->target || !actor->target->player)
9692 {
9693 if(cv_debug)
9694 CONS_Printf("A_RingDrain: No player targeted!\n");
9695 return;
9696 }
9697
9698 player = actor->target->player;
9699 P_GivePlayerRings(player, -min(locvar1, player->rings));
9700 }
9701
9702 // Function: A_SplitShot
9703 //
9704 // Description: Shoots 2 missiles that hit next to the player.
9705 //
9706 // var1 = target x-y-offset
9707 // var2:
9708 // lower 16 bits = missile type
9709 // upper 16 bits = height offset
9710 //
A_SplitShot(mobj_t * actor)9711 void A_SplitShot(mobj_t *actor)
9712 {
9713 INT32 locvar1 = var1;
9714 INT32 locvar2 = var2;
9715 const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
9716 const UINT16 loc2up = (UINT16)(locvar2 >> 16);
9717 const fixed_t offs = (fixed_t)(locvar1*FRACUNIT);
9718 const fixed_t hoffs = (fixed_t)(loc2up*FRACUNIT);
9719
9720 if (LUA_CallAction(A_SPLITSHOT, actor))
9721 return;
9722
9723 if (!actor->target)
9724 return;
9725
9726 A_FaceTarget(actor);
9727 {
9728 const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT;
9729 const fixed_t fasin = FINESINE(an);
9730 const fixed_t facos = FINECOSINE(an);
9731 fixed_t xs = FixedMul(facos,FixedMul(offs, actor->scale));
9732 fixed_t ys = FixedMul(fasin,FixedMul(offs, actor->scale));
9733 fixed_t z;
9734
9735 if (actor->eflags & MFE_VERTICALFLIP)
9736 z = actor->z + actor->height - FixedMul(hoffs, actor->scale);
9737 else
9738 z = actor->z + FixedMul(hoffs, actor->scale);
9739
9740 P_SpawnPointMissile(actor, actor->target->x+xs, actor->target->y+ys, actor->target->z, loc2lw, actor->x, actor->y, z);
9741 P_SpawnPointMissile(actor, actor->target->x-xs, actor->target->y-ys, actor->target->z, loc2lw, actor->x, actor->y, z);
9742 }
9743 }
9744
9745 // Function: A_MissileSplit
9746 //
9747 // Description: If the object is a missile it will create a new missile with an alternate flight path owned by the one who shot the former missile.
9748 //
9749 // var1 = splitting missile type
9750 // var2 = splitting angle
9751 //
A_MissileSplit(mobj_t * actor)9752 void A_MissileSplit(mobj_t *actor)
9753 {
9754 INT32 locvar1 = var1;
9755 INT32 locvar2 = var2;
9756
9757 if (LUA_CallAction(A_MISSILESPLIT, actor))
9758 return;
9759
9760 if (actor->eflags & MFE_VERTICALFLIP)
9761 P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z+actor->height, locvar2);
9762 else
9763 P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z, locvar2);
9764 }
9765
9766 // Function: A_MultiShot
9767 //
9768 // Description: Shoots objects horizontally that spread evenly in all directions.
9769 //
9770 // var1:
9771 // lower 16 bits = number of missiles
9772 // upper 16 bits = missile type #
9773 // var2 = height offset
9774 //
A_MultiShot(mobj_t * actor)9775 void A_MultiShot(mobj_t *actor)
9776 {
9777 fixed_t z, xr, yr;
9778 INT32 locvar1 = var1;
9779 INT32 locvar2 = var2;
9780 const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
9781 const UINT16 loc1up = (UINT16)(locvar1 >> 16);
9782 INT32 count = 0;
9783 fixed_t ad;
9784
9785 if (LUA_CallAction(A_MULTISHOT, actor))
9786 return;
9787
9788 if (actor->target)
9789 A_FaceTarget(actor);
9790
9791 if(loc1lw > 90)
9792 ad = FixedMul(90*FRACUNIT, actor->scale);
9793 else
9794 ad = FixedMul(loc1lw*FRACUNIT, actor->scale);
9795
9796 if (actor->eflags & MFE_VERTICALFLIP)
9797 z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
9798 else
9799 z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
9800 xr = FixedMul((P_SignedRandom()/3)<<FRACBITS, actor->scale); // please note p_mobj.c's P_Rand() abuse
9801 yr = FixedMul((P_SignedRandom()/3)<<FRACBITS, actor->scale); // of rand(), RAND_MAX and signness mess
9802
9803 while(count <= loc1lw && loc1lw >= 1)
9804 {
9805 const angle_t fa = FixedAngleC(count*FRACUNIT*360, ad)>>ANGLETOFINESHIFT;
9806 const fixed_t rc = FINECOSINE(fa);
9807 const fixed_t rs = FINESINE(fa);
9808 const fixed_t xrc = FixedMul(xr, rc);
9809 const fixed_t yrs = FixedMul(yr, rs);
9810 const fixed_t xrs = FixedMul(xr, rs);
9811 const fixed_t yrc = FixedMul(yr, rc);
9812
9813 P_SpawnPointMissile(actor, xrc-yrs+actor->x, xrs+yrc+actor->y, z, loc1up, actor->x, actor->y, z);
9814 count++;
9815 }
9816
9817 if (!(actor->flags & MF_BOSS))
9818 {
9819 if (ultimatemode)
9820 actor->reactiontime = actor->info->reactiontime*TICRATE;
9821 else
9822 actor->reactiontime = actor->info->reactiontime*TICRATE*2;
9823 }
9824 }
9825
9826 // Function: A_InstaLoop
9827 //
9828 // Description: Makes the object move along a 2d (view angle, z) polygon.
9829 //
9830 // var1:
9831 // lower 16 bits = current step
9832 // upper 16 bits = maximum step #
9833 // var2 = force
9834 //
A_InstaLoop(mobj_t * actor)9835 void A_InstaLoop(mobj_t *actor)
9836 {
9837 INT32 locvar1 = var1;
9838 INT32 locvar2 = var2;
9839 fixed_t force = max(locvar2, 1)*FRACUNIT; // defaults to 1 if var2 < 1
9840 const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
9841 const UINT16 loc1up = (UINT16)(locvar1 >> 16);
9842 const angle_t fa = FixedAngleC(loc1lw*FRACUNIT*360, loc1up*FRACUNIT)>>ANGLETOFINESHIFT;
9843 const fixed_t ac = FINECOSINE(fa);
9844 const fixed_t as = FINESINE(fa);
9845
9846 if (LUA_CallAction(A_INSTALOOP, actor))
9847 return;
9848
9849 P_InstaThrust(actor, actor->angle, FixedMul(ac, FixedMul(force, actor->scale)));
9850 P_SetObjectMomZ(actor, FixedMul(as, force), false);
9851 }
9852
9853 // Function: A_Custom3DRotate
9854 //
9855 // Description: Rotates the actor around its target in 3 dimensions.
9856 //
9857 // var1:
9858 // lower 16 bits = radius in fracunits
9859 // upper 16 bits = vertical offset
9860 // var2:
9861 // lower 16 bits = vertical rotation speed in 1/10 fracunits per tic
9862 // upper 16 bits = horizontal rotation speed in 1/10 fracunits per tic
9863 //
A_Custom3DRotate(mobj_t * actor)9864 void A_Custom3DRotate(mobj_t *actor)
9865 {
9866 INT32 locvar1 = var1;
9867 INT32 locvar2 = var2;
9868
9869 const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
9870 const UINT16 loc1up = (UINT16)(locvar1 >> 16);
9871 const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
9872 const UINT16 loc2up = (UINT16)(locvar2 >> 16);
9873
9874 const fixed_t radius = FixedMul(loc1lw*FRACUNIT, actor->scale);
9875 const fixed_t hOff = FixedMul(loc1up*FRACUNIT, actor->scale);
9876 const fixed_t hspeed = FixedMul(loc2up*FRACUNIT/10, actor->scale);
9877 const fixed_t vspeed = FixedMul(loc2lw*FRACUNIT/10, actor->scale);
9878
9879 if (LUA_CallAction(A_CUSTOM3DROTATE, actor))
9880 return;
9881
9882 if (!actor->target) // Ensure we actually have a target first.
9883 {
9884 CONS_Printf("Error: A_Custom3DRotate: Object has no target.\n");
9885 P_RemoveMobj(actor);
9886 return;
9887 }
9888
9889 if (actor->target->health == 0)
9890 {
9891 P_RemoveMobj(actor);
9892 return;
9893 }
9894
9895 if (hspeed==0 && vspeed==0)
9896 {
9897 if (cv_debug)
9898 CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n");
9899 return;
9900 }
9901
9902 actor->angle += FixedAngle(hspeed);
9903 actor->movedir += FixedAngle(vspeed);
9904 P_UnsetThingPosition(actor);
9905 {
9906 const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
9907
9908 if (vspeed == 0 && hspeed != 0)
9909 {
9910 actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
9911 actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
9912 actor->z = actor->target->z + actor->target->height/2 - actor->height/2 + hOff;
9913 }
9914 else
9915 {
9916 const angle_t md = actor->movedir>>ANGLETOFINESHIFT;
9917 actor->x = actor->target->x + FixedMul(FixedMul(FINESINE(md),FINECOSINE(fa)),radius);
9918 actor->y = actor->target->y + FixedMul(FixedMul(FINESINE(md),FINESINE(fa)),radius);
9919 actor->z = actor->target->z + FixedMul(FINECOSINE(md),radius) + actor->target->height/2 - actor->height/2 + hOff;
9920 }
9921 }
9922 P_SetThingPosition(actor);
9923 }
9924
9925 // Function: A_SearchForPlayers
9926 //
9927 // Description: Checks if the actor has targeted a vulnerable player. If not a new player will be searched all around. If no players are available the object can call a specific state. (Useful for not moving enemies)
9928 //
9929 // var1:
9930 // if var1 == 0, if necessary call state with same state number as var2
9931 // else, do not call a specific state if no players are available
9932 // var2 = state number
9933 //
A_SearchForPlayers(mobj_t * actor)9934 void A_SearchForPlayers(mobj_t *actor)
9935 {
9936 INT32 locvar1 = var1;
9937 INT32 locvar2 = var2;
9938
9939 if (LUA_CallAction(A_SEARCHFORPLAYERS, actor))
9940 return;
9941
9942 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
9943 {
9944 // look for a new target
9945 if (P_LookForPlayers(actor, true, false, 0))
9946 return; // got a new target
9947
9948 if(locvar1==0)
9949 {
9950 P_SetMobjStateNF(actor, locvar2);
9951 return;
9952 }
9953 }
9954 }
9955
9956 // Function: A_CheckRandom
9957 //
9958 // Description: Calls a state by chance.
9959 //
9960 // var1:
9961 // lower 16 bits = denominator
9962 // upper 16 bits = numerator (defaults to 1 if zero)
9963 // var2 = state number
9964 //
A_CheckRandom(mobj_t * actor)9965 void A_CheckRandom(mobj_t *actor)
9966 {
9967 INT32 locvar1 = var1;
9968 INT32 locvar2 = var2;
9969 fixed_t chance = FRACUNIT;
9970
9971 if (LUA_CallAction(A_CHECKRANDOM, actor))
9972 return;
9973
9974 if ((locvar1 & 0xFFFF) == 0)
9975 return;
9976
9977 // The PRNG doesn't suck anymore, OK?
9978 if (locvar1 >> 16)
9979 chance *= (locvar1 >> 16);
9980 chance /= (locvar1 & 0xFFFF);
9981
9982 if (P_RandomChance(chance))
9983 P_SetMobjState(actor, locvar2);
9984 }
9985
9986 // Function: A_CheckTargetRings
9987 //
9988 // Description: Calls a state depending on the ammount of rings currently owned by targeted players.
9989 //
9990 // var1 = if player rings >= var1 call state
9991 // var2 = state number
9992 //
A_CheckTargetRings(mobj_t * actor)9993 void A_CheckTargetRings(mobj_t *actor)
9994 {
9995 INT32 locvar1 = var1;
9996 INT32 locvar2 = var2;
9997
9998 if (LUA_CallAction(A_CHECKTARGETRINGS, actor))
9999 return;
10000
10001 if (!(actor->target) || !(actor->target->player))
10002 return;
10003
10004 if (actor->target->player->rings >= locvar1)
10005 P_SetMobjState(actor, locvar2);
10006 }
10007
10008 // Function: A_CheckRings
10009 //
10010 // Description: Calls a state depending on the ammount of rings currently owned by all players.
10011 //
10012 // var1 = if player rings >= var1 call state
10013 // var2 = state number
10014 //
A_CheckRings(mobj_t * actor)10015 void A_CheckRings(mobj_t *actor)
10016 {
10017 INT32 locvar1 = var1;
10018 INT32 locvar2 = var2;
10019 INT32 i, cntr = 0;
10020
10021 if (LUA_CallAction(A_CHECKRINGS, actor))
10022 return;
10023
10024 for (i = 0; i < MAXPLAYERS; i++)
10025 cntr += players[i].rings;
10026
10027 if (cntr >= locvar1)
10028 P_SetMobjState(actor, locvar2);
10029 }
10030
10031 // Function: A_CheckTotalRings
10032 //
10033 // Description: Calls a state depending on the maximum ammount of rings owned by all players during this try.
10034 //
10035 // var1 = if total player rings >= var1 call state
10036 // var2 = state number
10037 //
A_CheckTotalRings(mobj_t * actor)10038 void A_CheckTotalRings(mobj_t *actor)
10039 {
10040 INT32 locvar1 = var1;
10041 INT32 locvar2 = var2;
10042
10043 INT32 i, cntr = 0;
10044
10045 if (LUA_CallAction(A_CHECKTOTALRINGS, actor))
10046 return;
10047
10048 for (i = 0; i < MAXPLAYERS; i++)
10049 cntr += players[i].totalring;
10050
10051 if (cntr >= locvar1)
10052 P_SetMobjState(actor, locvar2);
10053 }
10054
10055 // Function: A_CheckHealth
10056 //
10057 // Description: Calls a state depending on the object's current health.
10058 //
10059 // var1 = if health <= var1 call state
10060 // var2 = state number
10061 //
A_CheckHealth(mobj_t * actor)10062 void A_CheckHealth(mobj_t *actor)
10063 {
10064 INT32 locvar1 = var1;
10065 INT32 locvar2 = var2;
10066
10067 if (LUA_CallAction(A_CHECKHEALTH, actor))
10068 return;
10069
10070 if (actor->health <= locvar1)
10071 P_SetMobjState(actor, locvar2);
10072 }
10073
10074 // Function: A_CheckRange
10075 //
10076 // Description: Calls a state if the object's target is in range.
10077 //
10078 // var1:
10079 // lower 16 bits = range
10080 // upper 16 bits = 0 - target, 1 - tracer
10081 // var2 = state number
10082 //
A_CheckRange(mobj_t * actor)10083 void A_CheckRange(mobj_t *actor)
10084 {
10085 INT32 locvar1 = var1;
10086 INT32 locvar2 = var2;
10087 fixed_t dist;
10088
10089 if (LUA_CallAction(A_CHECKRANGE, actor))
10090 return;
10091
10092 if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
10093 return;
10094
10095 if (!(locvar1 >> 16)) //target
10096 dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
10097 else //tracer
10098 dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
10099
10100 if (dist <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
10101 P_SetMobjState(actor, locvar2);
10102 }
10103
10104 // Function: A_CheckHeight
10105 //
10106 // Description: Calls a state if the object and it's target have a height offset <= var1 compared to each other.
10107 //
10108 // var1:
10109 // lower 16 bits = height offset
10110 // upper 16 bits = 0 - target, 1 - tracer
10111 // var2 = state number
10112 //
A_CheckHeight(mobj_t * actor)10113 void A_CheckHeight(mobj_t *actor)
10114 {
10115 INT32 locvar1 = var1;
10116 INT32 locvar2 = var2;
10117 fixed_t height;
10118
10119 if (LUA_CallAction(A_CHECKHEIGHT, actor))
10120 return;
10121
10122 if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
10123 return;
10124
10125 if (!(locvar1 >> 16)) // target
10126 height = abs(actor->target->z - actor->z);
10127 else // tracer
10128 height = abs(actor->tracer->z - actor->z);
10129
10130 if (height <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
10131 P_SetMobjState(actor, locvar2);
10132 }
10133
10134 // Function: A_CheckTrueRange
10135 //
10136 // Description: Calls a state if the object's target is in true range. (Checks height, too.)
10137 //
10138 // var1:
10139 // lower 16 bits = range
10140 // upper 16 bits = 0 - target, 1 - tracer
10141 // var2 = state number
10142 //
A_CheckTrueRange(mobj_t * actor)10143 void A_CheckTrueRange(mobj_t *actor)
10144 {
10145 INT32 locvar1 = var1;
10146 INT32 locvar2 = var2;
10147 fixed_t height; // vertical range
10148 fixed_t dist; // horizontal range
10149 fixed_t l; // true range
10150
10151 if (LUA_CallAction(A_CHECKTRUERANGE, actor))
10152 return;
10153
10154 if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
10155 return;
10156
10157 if (!(locvar1 >> 16)) // target
10158 {
10159 height = actor->target->z - actor->z;
10160 dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
10161
10162 }
10163 else // tracer
10164 {
10165 height = actor->tracer->z - actor->z;
10166 dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
10167 }
10168
10169 l = P_AproxDistance(dist, height);
10170
10171 if (l <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
10172 P_SetMobjState(actor, locvar2);
10173
10174 }
10175
10176 // Function: A_CheckThingCount
10177 //
10178 // Description: Calls a state depending on the number of active things in range.
10179 //
10180 // var1:
10181 // lower 16 bits = number of things
10182 // upper 16 bits = thing type
10183 // var2:
10184 // lower 16 bits = state to call
10185 // upper 16 bits = range (if == 0, check whole map)
10186 //
A_CheckThingCount(mobj_t * actor)10187 void A_CheckThingCount(mobj_t *actor)
10188 {
10189 INT32 locvar1 = var1;
10190 INT32 locvar2 = var2;
10191
10192 const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
10193 const UINT16 loc1up = (UINT16)(locvar1 >> 16);
10194 const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
10195 const UINT16 loc2up = (UINT16)(locvar2 >> 16);
10196
10197 INT32 count = 0;
10198 thinker_t *th;
10199 mobj_t *mo2;
10200 fixed_t dist = 0;
10201
10202 if (LUA_CallAction(A_CHECKTHINGCOUNT, actor))
10203 return;
10204
10205 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
10206 {
10207 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
10208 continue;
10209
10210 mo2 = (mobj_t *)th;
10211
10212 if (mo2->type == (mobjtype_t)loc1up)
10213 {
10214 dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
10215
10216 if (loc2up == 0)
10217 count++;
10218 else
10219 {
10220 if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
10221 count++;
10222 }
10223 }
10224 }
10225
10226 if(loc1lw <= count)
10227 P_SetMobjState(actor, loc2lw);
10228 }
10229
10230 // Function: A_CheckAmbush
10231 //
10232 // Description: Calls a state if the actor is behind its targeted player.
10233 //
10234 // var1:
10235 // 0 = target
10236 // 1 = tracer
10237 // var2 = state number
10238 //
A_CheckAmbush(mobj_t * actor)10239 void A_CheckAmbush(mobj_t *actor)
10240 {
10241 INT32 locvar1 = var1;
10242 INT32 locvar2 = var2;
10243 angle_t at; // angle target is currently facing
10244 angle_t atp; // actor to target angle
10245 angle_t an; // angle between at and atp
10246
10247 if (LUA_CallAction(A_CHECKAMBUSH, actor))
10248 return;
10249
10250 if ((!locvar1 && !actor->target) || (locvar1 && !actor->tracer))
10251 return;
10252
10253 if (!locvar1) // target
10254 {
10255 at = actor->target->angle;
10256 atp = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
10257 }
10258 else // tracer
10259 {
10260 at = actor->tracer->angle;
10261 atp = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
10262 }
10263
10264 an = atp - at;
10265
10266 if (an > ANGLE_180) // flip angle if bigger than 180
10267 an = InvAngle(an);
10268
10269 if (an < ANGLE_90+ANGLE_22h) // within an angle of 112.5 from each other?
10270 P_SetMobjState(actor, locvar2);
10271 }
10272
10273 // Function: A_CheckCustomValue
10274 //
10275 // Description: Calls a state depending on the object's custom value.
10276 //
10277 // var1 = if custom value >= var1, call state
10278 // var2 = state number
10279 //
A_CheckCustomValue(mobj_t * actor)10280 void A_CheckCustomValue(mobj_t *actor)
10281 {
10282 INT32 locvar1 = var1;
10283 INT32 locvar2 = var2;
10284
10285 if (LUA_CallAction(A_CHECKCUSTOMVALUE, actor))
10286 return;
10287
10288 if (actor->cusval >= locvar1)
10289 P_SetMobjState(actor, locvar2);
10290 }
10291
10292 // Function: A_CheckCusValMemo
10293 //
10294 // Description: Calls a state depending on the object's custom memory value.
10295 //
10296 // var1 = if memory value >= var1, call state
10297 // var2 = state number
10298 //
A_CheckCusValMemo(mobj_t * actor)10299 void A_CheckCusValMemo(mobj_t *actor)
10300 {
10301 INT32 locvar1 = var1;
10302 INT32 locvar2 = var2;
10303
10304 if (LUA_CallAction(A_CHECKCUSVALMEMO, actor))
10305 return;
10306
10307 if (actor->cvmem >= locvar1)
10308 P_SetMobjState(actor, locvar2);
10309 }
10310
10311 // Function: A_SetCustomValue
10312 //
10313 // Description: Changes the custom value of an object.
10314 //
10315 // var1 = manipulating value
10316 // var2:
10317 // if var2 == 5, multiply the custom value by var1
10318 // else if var2 == 4, divide the custom value by var1
10319 // else if var2 == 3, apply modulo var1 to the custom value
10320 // else if var2 == 2, add var1 to the custom value
10321 // else if var2 == 1, substract var1 from the custom value
10322 // else if var2 == 0, replace the custom value with var1
10323 //
A_SetCustomValue(mobj_t * actor)10324 void A_SetCustomValue(mobj_t *actor)
10325 {
10326 INT32 locvar1 = var1;
10327 INT32 locvar2 = var2;
10328
10329 if (LUA_CallAction(A_SETCUSTOMVALUE, actor))
10330 return;
10331
10332 if (cv_debug)
10333 CONS_Printf("Init custom value is %d\n", actor->cusval);
10334
10335 if (locvar1 == 0 && locvar2 == 4)
10336 return; // DON'T DIVIDE BY ZERO
10337
10338 // no need for a "temp" value here, just modify the cusval directly
10339 if (locvar2 == 5) // multiply
10340 actor->cusval *= locvar1;
10341 else if (locvar2 == 4) // divide
10342 actor->cusval /= locvar1;
10343 else if (locvar2 == 3) // modulo
10344 actor->cusval %= locvar1;
10345 else if (locvar2 == 2) // add
10346 actor->cusval += locvar1;
10347 else if (locvar2 == 1) // subtract
10348 actor->cusval -= locvar1;
10349 else // replace
10350 actor->cusval = locvar1;
10351
10352 if(cv_debug)
10353 CONS_Printf("New custom value is %d\n", actor->cusval);
10354 }
10355
10356 // Function: A_UseCusValMemo
10357 //
10358 // Description: Memorizes or recalls a current custom value.
10359 //
10360 // var1:
10361 // if var1 == 1, manipulate memory value
10362 // else, recall memory value replacing the custom value
10363 // var2:
10364 // if var2 == 5, mem = mem*cv || cv = cv*mem
10365 // else if var2 == 4, mem = mem/cv || cv = cv/mem
10366 // else if var2 == 3, mem = mem%cv || cv = cv%mem
10367 // else if var2 == 2, mem += cv || cv += mem
10368 // else if var2 == 1, mem -= cv || cv -= mem
10369 // else mem = cv || cv = mem
10370 //
A_UseCusValMemo(mobj_t * actor)10371 void A_UseCusValMemo(mobj_t *actor)
10372 {
10373 INT32 locvar1 = var1;
10374 INT32 locvar2 = var2;
10375
10376 INT32 temp = actor->cusval; // value being manipulated
10377 INT32 tempM = actor->cvmem; // value used to manipulate temp with
10378
10379 if (LUA_CallAction(A_USECUSVALMEMO, actor))
10380 return;
10381
10382 if (locvar1 == 1) // cvmem being changed using cusval
10383 {
10384 temp = actor->cvmem;
10385 tempM = actor->cusval;
10386 }
10387 else // cusval being changed with cvmem
10388 {
10389 temp = actor->cusval;
10390 tempM = actor->cvmem;
10391 }
10392
10393 if (tempM == 0 && locvar2 == 4)
10394 return; // DON'T DIVIDE BY ZERO
10395
10396 // now get new value for cusval/cvmem using the other
10397 if (locvar2 == 5) // multiply
10398 temp *= tempM;
10399 else if (locvar2 == 4) // divide
10400 temp /= tempM;
10401 else if (locvar2 == 3) // modulo
10402 temp %= tempM;
10403 else if (locvar2 == 2) // add
10404 temp += tempM;
10405 else if (locvar2 == 1) // subtract
10406 temp -= tempM;
10407 else // replace
10408 temp = tempM;
10409
10410 // finally, give cusval/cvmem the new value!
10411 if (locvar1 == 1)
10412 actor->cvmem = temp;
10413 else
10414 actor->cusval = temp;
10415 }
10416
10417 // Function: A_RelayCustomValue
10418 //
10419 // Description: Manipulates the custom value of the object's target/tracer.
10420 //
10421 // var1:
10422 // lower 16 bits:
10423 // if var1 == 0, use own custom value
10424 // else, use var1 value
10425 // upper 16 bits = 0 - target, 1 - tracer
10426 // var2:
10427 // if var2 == 5, multiply the target's custom value by var1
10428 // else if var2 == 4, divide the target's custom value by var1
10429 // else if var2 == 3, apply modulo var1 to the target's custom value
10430 // else if var2 == 2, add var1 to the target's custom value
10431 // else if var2 == 1, substract var1 from the target's custom value
10432 // else if var2 == 0, replace the target's custom value with var1
10433 //
A_RelayCustomValue(mobj_t * actor)10434 void A_RelayCustomValue(mobj_t *actor)
10435 {
10436 INT32 locvar1 = var1;
10437 INT32 locvar2 = var2;
10438
10439 INT32 temp; // reference value - var1 lower 16 bits changes this
10440 INT32 tempT; // target's value - changed to tracer if var1 upper 16 bits set, then modified to become final value
10441
10442 if (LUA_CallAction(A_RELAYCUSTOMVALUE, actor))
10443 return;
10444
10445 if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
10446 return;
10447
10448 // reference custom value
10449 if ((locvar1 & 65535) == 0)
10450 temp = actor->cusval; // your own custom value
10451 else
10452 temp = (locvar1 & 65535); // var1 value
10453
10454 if (!(locvar1 >> 16)) // target's custom value
10455 tempT = actor->target->cusval;
10456 else // tracer's custom value
10457 tempT = actor->tracer->cusval;
10458
10459 if (temp == 0 && locvar2 == 4)
10460 return; // DON'T DIVIDE BY ZERO
10461
10462 // now get new cusval using target's and the reference
10463 if (locvar2 == 5) // multiply
10464 tempT *= temp;
10465 else if (locvar2 == 4) // divide
10466 tempT /= temp;
10467 else if (locvar2 == 3) // modulo
10468 tempT %= temp;
10469 else if (locvar2 == 2) // add
10470 tempT += temp;
10471 else if (locvar2 == 1) // subtract
10472 tempT -= temp;
10473 else // replace
10474 tempT = temp;
10475
10476 // finally, give target/tracer the new cusval!
10477 if (!(locvar1 >> 16)) // target
10478 actor->target->cusval = tempT;
10479 else // tracer
10480 actor->tracer->cusval = tempT;
10481 }
10482
10483 // Function: A_CusValAction
10484 //
10485 // Description: Calls an action from a reference state applying custom value parameters.
10486 //
10487 // var1 = state # to use action from
10488 // var2:
10489 // if var2 == 5, only replace new action's var2 with memory value
10490 // else if var2 == 4, only replace new action's var1 with memory value
10491 // else if var2 == 3, replace new action's var2 with custom value and var1 with memory value
10492 // else if var2 == 2, replace new action's var1 with custom value and var2 with memory value
10493 // else if var2 == 1, only replace new action's var2 with custom value
10494 // else if var2 == 0, only replace new action's var1 with custom value
10495 //
A_CusValAction(mobj_t * actor)10496 void A_CusValAction(mobj_t *actor)
10497 {
10498 INT32 locvar1 = var1;
10499 INT32 locvar2 = var2;
10500
10501 if (LUA_CallAction(A_CUSVALACTION, actor))
10502 return;
10503
10504 if (locvar2 == 5)
10505 {
10506 var1 = states[locvar1].var1;
10507 var2 = (INT32)actor->cvmem;
10508 }
10509 else if (locvar2 == 4)
10510 {
10511 var1 = (INT32)actor->cvmem;
10512 var2 = states[locvar1].var2;
10513 }
10514 else if (locvar2 == 3)
10515 {
10516 var1 = (INT32)actor->cvmem;
10517 var2 = (INT32)actor->cusval;
10518 }
10519 else if (locvar2 == 2)
10520 {
10521 var1 = (INT32)actor->cusval;
10522 var2 = (INT32)actor->cvmem;
10523 }
10524 else if (locvar2 == 1)
10525 {
10526 var1 = states[locvar1].var1;
10527 var2 = (INT32)actor->cusval;
10528 }
10529 else
10530 {
10531 var1 = (INT32)actor->cusval;
10532 var2 = states[locvar1].var2;
10533 }
10534
10535 astate = &states[locvar1];
10536 states[locvar1].action.acp1(actor);
10537 }
10538
10539 // Function: A_ForceStop
10540 //
10541 // Description: Actor immediately stops its current movement.
10542 //
10543 // var1:
10544 // if var1 == 0, stop x-y-z-movement
10545 // else, stop x-y-movement only
10546 // var2 = unused
10547 //
A_ForceStop(mobj_t * actor)10548 void A_ForceStop(mobj_t *actor)
10549 {
10550 INT32 locvar1 = var1;
10551
10552 if (LUA_CallAction(A_FORCESTOP, actor))
10553 return;
10554
10555 actor->momx = actor->momy = 0;
10556 if (locvar1 == 0)
10557 actor->momz = 0;
10558 }
10559
10560 // Function: A_ForceWin
10561 //
10562 // Description: Makes all players win the level.
10563 //
10564 // var1 = unused
10565 // var2 = unused
10566 //
A_ForceWin(mobj_t * actor)10567 void A_ForceWin(mobj_t *actor)
10568 {
10569 INT32 i;
10570
10571 if (LUA_CallAction(A_FORCEWIN, actor))
10572 return;
10573
10574 for (i = 0; i < MAXPLAYERS; i++)
10575 {
10576 if (playeringame[i] && ((players[i].mo && players[i].mo->health)
10577 || ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
10578 break;
10579 }
10580
10581 if (i == MAXPLAYERS)
10582 return;
10583
10584 for (i = 0; i < MAXPLAYERS; i++)
10585 {
10586 if (!playeringame[i])
10587 continue;
10588 P_DoPlayerExit(&players[i]);
10589 }
10590 }
10591
10592 // Function: A_SpikeRetract
10593 //
10594 // Description: Toggles actor solid flag.
10595 //
10596 // var1:
10597 // if var1 == 0, actor no collide
10598 // else, actor solid
10599 // var2 = unused
10600 //
A_SpikeRetract(mobj_t * actor)10601 void A_SpikeRetract(mobj_t *actor)
10602 {
10603 INT32 locvar1 = var1;
10604
10605 if (LUA_CallAction(A_SPIKERETRACT, actor))
10606 return;
10607
10608 if (actor->flags & MF_NOBLOCKMAP)
10609 return;
10610
10611 if (locvar1 == 0)
10612 {
10613 actor->flags &= ~MF_SOLID;
10614 actor->flags |= MF_NOCLIPTHING;
10615 }
10616 else
10617 {
10618 actor->flags |= MF_SOLID;
10619 actor->flags &= ~MF_NOCLIPTHING;
10620 }
10621 if (actor->flags & MF_SOLID)
10622 P_CheckPosition(actor, actor->x, actor->y);
10623 }
10624
10625 // Function: A_InfoState
10626 //
10627 // Description: Set mobj state to one predefined in mobjinfo.
10628 //
10629 // var1:
10630 // if var1 == 0, set actor to spawnstate
10631 // else if var1 == 1, set actor to seestate
10632 // else if var1 == 2, set actor to meleestate
10633 // else if var1 == 3, set actor to missilestate
10634 // else if var1 == 4, set actor to deathstate
10635 // else if var1 == 5, set actor to xdeathstate
10636 // else if var1 == 6, set actor to raisestate
10637 // var2 = unused
10638 //
A_InfoState(mobj_t * actor)10639 void A_InfoState(mobj_t *actor)
10640 {
10641 INT32 locvar1 = var1;
10642 switch (locvar1)
10643 {
10644 case 0:
10645 if (actor->state != &states[actor->info->spawnstate])
10646 P_SetMobjState(actor, actor->info->spawnstate);
10647 break;
10648 case 1:
10649 if (actor->state != &states[actor->info->seestate])
10650 P_SetMobjState(actor, actor->info->seestate);
10651 break;
10652 case 2:
10653 if (actor->state != &states[actor->info->meleestate])
10654 P_SetMobjState(actor, actor->info->meleestate);
10655 break;
10656 case 3:
10657 if (actor->state != &states[actor->info->missilestate])
10658 P_SetMobjState(actor, actor->info->missilestate);
10659 break;
10660 case 4:
10661 if (actor->state != &states[actor->info->deathstate])
10662 P_SetMobjState(actor, actor->info->deathstate);
10663 break;
10664 case 5:
10665 if (actor->state != &states[actor->info->xdeathstate])
10666 P_SetMobjState(actor, actor->info->xdeathstate);
10667 break;
10668 case 6:
10669 if (actor->state != &states[actor->info->raisestate])
10670 P_SetMobjState(actor, actor->info->raisestate);
10671 break;
10672 default:
10673 break;
10674 }
10675 }
10676
10677 // Function: A_Repeat
10678 //
10679 // Description: Returns to state var2 until animation has been used var1 times, then continues to nextstate.
10680 //
10681 // var1 = repeat count
10682 // var2 = state to return to if extravalue2 > 0
10683 //
A_Repeat(mobj_t * actor)10684 void A_Repeat(mobj_t *actor)
10685 {
10686 INT32 locvar1 = var1;
10687 INT32 locvar2 = var2;
10688
10689 if (LUA_CallAction(A_REPEAT, actor))
10690 return;
10691
10692 if (locvar1 && (!actor->extravalue2 || actor->extravalue2 > locvar1))
10693 actor->extravalue2 = locvar1;
10694
10695 if (--actor->extravalue2 > 0)
10696 P_SetMobjState(actor, locvar2);
10697 }
10698
10699 // Function: A_SetScale
10700 //
10701 // Description: Changes the scale of the actor or its target/tracer
10702 //
10703 // var1 = new scale (1*FRACUNIT = 100%)
10704 // var2:
10705 // upper 16 bits: 0 = actor, 1 = target, 2 = tracer
10706 // lower 16 bits: 0 = instant change, 1 = smooth change
10707 //
A_SetScale(mobj_t * actor)10708 void A_SetScale(mobj_t *actor)
10709 {
10710 INT32 locvar1 = var1;
10711 INT32 locvar2 = var2;
10712 mobj_t *target;
10713
10714 if (LUA_CallAction(A_SETSCALE, actor))
10715 return;
10716
10717 if (locvar1 <= 0)
10718 {
10719 if(cv_debug)
10720 CONS_Printf("A_SetScale: Valid scale not specified!\n");
10721 return;
10722 }
10723
10724 if ((locvar2>>16) == 1)
10725 target = actor->target;
10726 else if ((locvar2>>16) == 2)
10727 target = actor->tracer;
10728 else // default to yourself!
10729 target = actor;
10730
10731 if (!target)
10732 {
10733 if(cv_debug)
10734 CONS_Printf("A_SetScale: No target!\n");
10735 return;
10736 }
10737
10738 target->destscale = locvar1; // destination scale
10739 if (!(locvar2 & 65535))
10740 P_SetScale(target, locvar1); // this instantly changes current scale to var1 if used, if not destscale will alter scale to var1 anyway
10741 }
10742
10743 // Function: A_RemoteDamage
10744 //
10745 // Description: Damages, kills or even removes either the actor or its target/tracer. Actor acts as the inflictor/source unless harming itself
10746 //
10747 // var1 = Mobj affected: 0 - actor, 1 - target, 2 - tracer
10748 // var2 = Action: 0 - Damage, 1 - Kill, 2 - Remove
10749 //
A_RemoteDamage(mobj_t * actor)10750 void A_RemoteDamage(mobj_t *actor)
10751 {
10752 INT32 locvar1 = var1;
10753 INT32 locvar2 = var2;
10754 mobj_t *target; // we MUST have a target
10755 mobj_t *source = NULL; // on the other hand we don't necessarily need a source
10756
10757 if (LUA_CallAction(A_REMOTEDAMAGE, actor))
10758 return;
10759
10760 if (locvar1 == 1)
10761 target = actor->target;
10762 else if (locvar1 == 2)
10763 target = actor->tracer;
10764 else // default to yourself!
10765 target = actor;
10766
10767 if (locvar1 == 1 || locvar1 == 2)
10768 source = actor;
10769
10770 if (!target)
10771 {
10772 if(cv_debug)
10773 CONS_Printf("A_RemoteDamage: No target!\n");
10774 return;
10775 }
10776
10777 if (locvar2 == 1) // Kill mobj!
10778 {
10779 if (target->player) // players die using P_DamageMobj instead for some reason
10780 P_DamageMobj(target, source, source, 1, DMG_INSTAKILL);
10781 else
10782 P_KillMobj(target, source, source, 0);
10783 }
10784 else if (locvar2 == 2) // Remove mobj!
10785 {
10786 if (target->player) //don't remove players!
10787 return;
10788
10789 P_RemoveMobj(target);
10790 }
10791 else // default: Damage mobj!
10792 P_DamageMobj(target, source, source, 1, 0);
10793 }
10794
10795 // Function: A_HomingChase
10796 //
10797 // Description: Actor chases directly towards its destination object
10798 //
10799 // var1 = speed multiple
10800 // var2 = destination: 0 = target, 1 = tracer
10801 //
A_HomingChase(mobj_t * actor)10802 void A_HomingChase(mobj_t *actor)
10803 {
10804 INT32 locvar1 = var1;
10805 INT32 locvar2 = var2;
10806 mobj_t *dest;
10807 fixed_t dist;
10808 fixed_t speedmul;
10809
10810 if (LUA_CallAction(A_HOMINGCHASE, actor))
10811 return;
10812
10813 if (locvar2 == 1)
10814 dest = actor->tracer;
10815 else //default
10816 dest = actor->target;
10817
10818 if (!dest || !dest->health)
10819 return;
10820
10821 actor->angle = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y);
10822
10823 dist = P_AproxDistance(P_AproxDistance(dest->x - actor->x, dest->y - actor->y), dest->z - actor->z);
10824
10825 if (dist < 1)
10826 dist = 1;
10827
10828 speedmul = FixedMul(locvar1, actor->scale);
10829
10830 actor->momx = FixedMul(FixedDiv(dest->x - actor->x, dist), speedmul);
10831 actor->momy = FixedMul(FixedDiv(dest->y - actor->y, dist), speedmul);
10832 actor->momz = FixedMul(FixedDiv(dest->z - actor->z, dist), speedmul);
10833 }
10834
10835 // Function: A_TrapShot
10836 //
10837 // Description: Fires a missile in a particular direction and angle rather than AT something, Trapgoyle-style!
10838 //
10839 // var1:
10840 // lower 16 bits = object # to fire
10841 // upper 16 bits = front offset
10842 // var2:
10843 // lower 15 bits = vertical angle variable
10844 // 16th bit:
10845 // - 0: use vertical angle variable as vertical angle in degrees
10846 // - 1: mimic P_SpawnXYZMissile
10847 // use z of actor minus z of missile as vertical distance to cover during momz calculation
10848 // use vertical angle variable as horizontal distance to cover during momz calculation
10849 // upper 16 bits = height offset
10850 //
A_TrapShot(mobj_t * actor)10851 void A_TrapShot(mobj_t *actor)
10852 {
10853 INT32 locvar1 = var1;
10854 INT32 locvar2 = var2;
10855 boolean oldstyle = (locvar2 & 32768) ? true : false;
10856 mobjtype_t type = (mobjtype_t)(locvar1 & 65535);
10857 mobj_t *missile;
10858 INT16 frontoff = (INT16)(locvar1 >> 16);
10859 INT16 vertoff = (INT16)(locvar2 >> 16);
10860 fixed_t x, y, z;
10861 fixed_t speed;
10862
10863 if (LUA_CallAction(A_TRAPSHOT, actor))
10864 return;
10865
10866 x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
10867 y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
10868
10869 if (actor->eflags & MFE_VERTICALFLIP)
10870 z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale);
10871 else
10872 z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale);
10873
10874 CONS_Debug(DBG_GAMELOGIC, "A_TrapShot: missile no. = %d, front offset = %d, vertical angle = %d, z offset = %d\n",
10875 type, frontoff, (INT16)(locvar2 & 65535), vertoff);
10876
10877 missile = P_SpawnMobj(x, y, z, type);
10878
10879 if (actor->eflags & MFE_VERTICALFLIP)
10880 missile->flags2 |= MF2_OBJECTFLIP;
10881
10882 missile->destscale = actor->scale;
10883 P_SetScale(missile, actor->scale);
10884
10885 if (missile->info->seesound)
10886 S_StartSound(missile, missile->info->seesound);
10887
10888 P_SetTarget(&missile->target, actor);
10889 missile->angle = actor->angle;
10890
10891 speed = FixedMul(missile->info->speed, missile->scale);
10892
10893 if (oldstyle)
10894 {
10895 missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed);
10896 missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed);
10897 // The below line basically mimics P_SpawnXYZMissile's momz calculation.
10898 missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed);
10899 P_CheckMissileSpawn(missile);
10900 }
10901 else
10902 {
10903 angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT);
10904 if (actor->eflags & MFE_VERTICALFLIP)
10905 vertang = InvAngle(vertang); // flip firing angle
10906 missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed));
10907 missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed));
10908 missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed);
10909 }
10910 }
10911
10912 // Function: A_VileTarget
10913 //
10914 // Description: Spawns an object directly on the target, and sets this object as the actor's tracer.
10915 // Originally used by Archviles to summon a pillar of hellfire, hence the name.
10916 //
10917 // var1 = mobj to spawn
10918 // var2 = If 0, target only the actor's target. Else, target every player, period.
10919 //
A_VileTarget(mobj_t * actor)10920 void A_VileTarget(mobj_t *actor)
10921 {
10922 INT32 locvar1 = var1;
10923 INT32 locvar2 = var2;
10924 mobj_t *fog;
10925 mobjtype_t fogtype;
10926 INT32 i;
10927
10928 if (LUA_CallAction(A_VILETARGET, actor))
10929 return;
10930
10931 if (!actor->target)
10932 return;
10933
10934 A_FaceTarget(actor);
10935
10936 // Determine object to spawn
10937 if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
10938 fogtype = MT_CYBRAKDEMON_TARGET_RETICULE;
10939 else
10940 fogtype = (mobjtype_t)locvar1;
10941
10942 if (!locvar2)
10943 {
10944 fog = P_SpawnMobj(actor->target->x,
10945 actor->target->y,
10946 actor->target->z + ((actor->target->eflags & MFE_VERTICALFLIP) ? actor->target->height - mobjinfo[fogtype].height : 0),
10947 fogtype);
10948 if (actor->target->eflags & MFE_VERTICALFLIP)
10949 {
10950 fog->eflags |= MFE_VERTICALFLIP;
10951 fog->flags2 |= MF2_OBJECTFLIP;
10952 }
10953 fog->destscale = actor->target->scale;
10954 P_SetScale(fog, fog->destscale);
10955
10956 P_SetTarget(&actor->tracer, fog);
10957 P_SetTarget(&fog->target, actor);
10958 P_SetTarget(&fog->tracer, actor->target);
10959 A_VileFire(fog);
10960 }
10961 else
10962 {
10963 // Our "Archvile" here is actually Oprah. "YOU GET A TARGET! YOU GET A TARGET! YOU ALL GET A TARGET!"
10964 for (i = 0; i < MAXPLAYERS; i++)
10965 {
10966 if (!playeringame[i] || players[i].spectator)
10967 continue;
10968
10969 if (!players[i].mo)
10970 continue;
10971
10972 if (!players[i].mo->health)
10973 continue;
10974
10975 fog = P_SpawnMobj(players[i].mo->x,
10976 players[i].mo->y,
10977 players[i].mo->z + ((players[i].mo->eflags & MFE_VERTICALFLIP) ? players[i].mo->height - mobjinfo[fogtype].height : 0),
10978 fogtype);
10979 if (players[i].mo->eflags & MFE_VERTICALFLIP)
10980 {
10981 fog->eflags |= MFE_VERTICALFLIP;
10982 fog->flags2 |= MF2_OBJECTFLIP;
10983 }
10984 fog->destscale = players[i].mo->scale;
10985 P_SetScale(fog, fog->destscale);
10986
10987 if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now
10988 P_SetTarget(&actor->tracer, fog);
10989 P_SetTarget(&fog->target, actor);
10990 P_SetTarget(&fog->tracer, players[i].mo);
10991 A_VileFire(fog);
10992 }
10993 }
10994 }
10995
10996 // Function: A_VileAttack
10997 //
10998 // Description: Instantly hurts the actor's target, if it's in the actor's line of sight.
10999 // Originally used by Archviles to cause explosions where their hellfire pillars were, hence the name.
11000 //
11001 // var1 = sound to play
11002 // var2:
11003 // Lower 16 bits = optional explosion object
11004 // Upper 16 bits = If 0, attack only the actor's target. Else, attack all the players. All of them.
11005 //
A_VileAttack(mobj_t * actor)11006 void A_VileAttack(mobj_t *actor)
11007 {
11008 INT32 locvar1 = var1;
11009 INT32 locvar2 = var2;
11010 sfxenum_t soundtoplay;
11011 mobjtype_t explosionType = MT_NULL;
11012 mobj_t *fire;
11013 INT32 i;
11014
11015 if (LUA_CallAction(A_VILEATTACK, actor))
11016 return;
11017
11018 if (!actor->target)
11019 return;
11020
11021 A_FaceTarget(actor);
11022
11023 if (locvar1 <= 0 || locvar1 >= NUMSFX)
11024 soundtoplay = sfx_brakrx;
11025 else
11026 soundtoplay = (sfxenum_t)locvar1;
11027
11028 if ((locvar2 & 0xFFFF) > 0 && (locvar2 & 0xFFFF) <= NUMMOBJTYPES)
11029 {
11030 explosionType = (mobjtype_t)(locvar2 & 0xFFFF);
11031 }
11032
11033 if (!(locvar2 & 0xFFFF0000)) {
11034 if (!P_CheckSight(actor, actor->target))
11035 return;
11036
11037 S_StartSound(actor, soundtoplay);
11038 P_DamageMobj(actor->target, actor, actor, 1, 0);
11039 //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
11040 actor->target->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(actor->target); // How we're doing it
11041 if (explosionType != MT_NULL)
11042 {
11043 P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, explosionType);
11044 }
11045
11046 // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
11047 fire = actor->tracer;
11048
11049 if (!fire)
11050 return;
11051
11052 // move the fire between the vile and the player
11053 //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
11054 //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
11055 P_TeleportMove(fire,
11056 actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
11057 actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
11058 fire->z);
11059 P_RadiusAttack(fire, actor, 70*FRACUNIT, 0, true);
11060 }
11061 else
11062 {
11063 // Oprahvile strikes again, but this time, she brings HOT PAIN
11064 for (i = 0; i < MAXPLAYERS; i++)
11065 {
11066 if (!playeringame[i] || players[i].spectator)
11067 continue;
11068
11069 if (!players[i].mo)
11070 continue;
11071
11072 if (!players[i].mo->health)
11073 continue;
11074
11075 if (!P_CheckSight(actor, players[i].mo))
11076 continue;
11077
11078 S_StartSound(actor, soundtoplay);
11079 P_DamageMobj(players[i].mo, actor, actor, 1, 0);
11080 //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
11081 players[i].mo->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(players[i].mo); // How we're doing it
11082 if (explosionType != MT_NULL)
11083 {
11084 P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z, explosionType);
11085 }
11086
11087 // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
11088 // However, it ONLY applies to the actor's target. Nobody else matters!
11089 if (actor->target != players[i].mo)
11090 continue;
11091
11092 fire = actor->tracer;
11093
11094 if (!fire)
11095 continue;
11096
11097 // move the fire between the vile and the player
11098 //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
11099 //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
11100 P_TeleportMove(fire,
11101 actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
11102 actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
11103 fire->z);
11104 P_RadiusAttack(fire, actor, 70*FRACUNIT, 0, true);
11105 }
11106 }
11107
11108 }
11109
11110 // Function: A_VileFire
11111 //
11112 // Description: Kind of like A_CapeChase; keeps this object in front of its tracer, unless its target can't see it.
11113 // Originally used by Archviles to keep their hellfire pillars on top of the player, hence the name (although it was just "A_Fire" there; added "Vile" to make it more specific).
11114 // Added some functionality to optionally draw a line directly to the enemy doing the targetting. Y'know, to hammer things in a bit.
11115 //
11116 // var1 = sound to play
11117 // var2:
11118 // Lower 16 bits = mobj to spawn (0 doesn't spawn a line at all)
11119 // Upper 16 bits = # to spawn (default is 8)
11120 //
A_VileFire(mobj_t * actor)11121 void A_VileFire(mobj_t *actor)
11122 {
11123 INT32 locvar1 = var1;
11124 INT32 locvar2 = var2;
11125 mobj_t *dest;
11126
11127 if (LUA_CallAction(A_VILEFIRE, actor))
11128 return;
11129
11130 dest = actor->tracer;
11131 if (!dest)
11132 return;
11133
11134 // don't move it if the vile lost sight
11135 if (!P_CheckSight(actor->target, dest))
11136 return;
11137
11138 // keep to same scale and gravity as tracer ALWAYS
11139 actor->destscale = dest->scale;
11140 P_SetScale(actor, actor->destscale);
11141 if (dest->eflags & MFE_VERTICALFLIP)
11142 {
11143 actor->eflags |= MFE_VERTICALFLIP;
11144 actor->flags2 |= MF2_OBJECTFLIP;
11145 }
11146 else
11147 {
11148 actor->eflags &= ~MFE_VERTICALFLIP;
11149 actor->flags2 &= ~MF2_OBJECTFLIP;
11150 }
11151
11152 P_UnsetThingPosition(actor);
11153 actor->x = dest->x + P_ReturnThrustX(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
11154 actor->y = dest->y + P_ReturnThrustY(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
11155 actor->z = dest->z + ((actor->eflags & MFE_VERTICALFLIP) ? dest->height-actor->height : 0);
11156 P_SetThingPosition(actor);
11157
11158 // Play sound, if one's specified
11159 if (locvar1 > 0 && locvar1 < NUMSFX)
11160 S_StartSound(actor, (sfxenum_t)locvar1);
11161
11162 // Now draw the line to the actor's target
11163 if (locvar2 & 0xFFFF)
11164 {
11165 mobjtype_t lineMobj;
11166 UINT16 numLineMobjs;
11167 fixed_t distX;
11168 fixed_t distY;
11169 fixed_t distZ;
11170 UINT16 i;
11171
11172 lineMobj = (mobjtype_t)(locvar2 & 0xFFFF);
11173 numLineMobjs = (UINT16)(locvar2 >> 16);
11174 if (numLineMobjs == 0) {
11175 numLineMobjs = 8;
11176 }
11177
11178 // Get distance for each step
11179 distX = (actor->target->x - actor->x) / numLineMobjs;
11180 distY = (actor->target->y - actor->y) / numLineMobjs;
11181 distZ = ((actor->target->z + FixedMul(actor->target->height/2, actor->target->scale)) - (actor->z + FixedMul(actor->height/2, actor->scale))) / numLineMobjs;
11182
11183 for (i = 1; i <= numLineMobjs; i++)
11184 {
11185 P_SpawnMobj(actor->x + (distX * i), actor->y + (distY * i), actor->z + (distZ * i) + FixedMul(actor->height/2, actor->scale), lineMobj);
11186 }
11187 }
11188 }
11189
11190 // Function: A_BrakChase
11191 //
11192 // Description: Chase after your target, but speed and attack are tied to health.
11193 //
11194 // Every time this is called, generate a random number from a 1/4 to 3/4 of mobj's spawn health.
11195 // If health is above that value, use missilestate to attack.
11196 // If health is at or below that value, use meleestate to attack (default to missile state if not available).
11197 //
11198 // Likewise, state will linearly speed up as health goes down.
11199 // Upper bound will be the frame's normal length.
11200 // Lower bound defaults to 1 tic (technically 0, but we round up), unless a lower bound is specified in var1.
11201 //
11202 // var1 = lower-bound of frame length, in tics
11203 // var2 = optional sound to play
11204 //
A_BrakChase(mobj_t * actor)11205 void A_BrakChase(mobj_t *actor)
11206 {
11207 INT32 delta;
11208 INT32 lowerbound;
11209 INT32 newtics;
11210 INT32 locvar1 = var1;
11211 INT32 locvar2 = var2;
11212
11213 if (LUA_CallAction(A_BRAKCHASE, actor))
11214 return;
11215
11216 // Set new tics NOW, in case the state changes while we're doing this and we try applying this to the painstate or something silly
11217 if (actor->tics > 1 && locvar1 < actor->tics) // Not much point, otherwise
11218 {
11219 if (locvar1 < 0)
11220 lowerbound = 0;
11221 else
11222 lowerbound = locvar1;
11223
11224 newtics = (((actor->tics - lowerbound) * actor->health) / actor->info->spawnhealth) + lowerbound;
11225 if (newtics < 1)
11226 newtics = 1;
11227
11228 actor->tics = newtics;
11229 }
11230
11231 if (actor->reactiontime)
11232 {
11233 actor->reactiontime--;
11234 if (actor->reactiontime == 0 && actor->type == MT_CYBRAKDEMON)
11235 S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
11236 }
11237
11238 // modify target threshold
11239 if (actor->threshold)
11240 {
11241 if (!actor->target || actor->target->health <= 0)
11242 actor->threshold = 0;
11243 else
11244 actor->threshold--;
11245 }
11246
11247 // turn towards movement direction if not there yet
11248 if (actor->movedir < NUMDIRS)
11249 {
11250 actor->angle &= (7<<29);
11251 delta = actor->angle - (actor->movedir << 29);
11252
11253 if (delta > 0)
11254 actor->angle -= ANGLE_45;
11255 else if (delta < 0)
11256 actor->angle += ANGLE_45;
11257 }
11258
11259 if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
11260 {
11261 // look for a new target
11262 if (P_LookForPlayers(actor, true, false, 0))
11263 return; // got a new target
11264
11265 P_SetMobjStateNF(actor, actor->info->spawnstate);
11266 return;
11267 }
11268
11269 // do not attack twice in a row
11270 if (actor->flags2 & MF2_JUSTATTACKED)
11271 {
11272 actor->flags2 &= ~MF2_JUSTATTACKED;
11273 P_NewChaseDir(actor);
11274 return;
11275 }
11276
11277 // Check if we can attack
11278 if (P_CheckMissileRange(actor) && !actor->movecount)
11279 {
11280 // Check if we should use "melee" attack first. (Yes, this still runs outside of melee range. Quiet, you.)
11281 if (actor->info->meleestate
11282 && actor->health <= P_RandomRange(actor->info->spawnhealth/4, (actor->info->spawnhealth * 3)/4)) // Guaranteed true if <= 1/4 health, guaranteed false if > 3/4 health
11283 {
11284 if (actor->info->attacksound)
11285 S_StartAttackSound(actor, actor->info->attacksound);
11286
11287 P_SetMobjState(actor, actor->info->meleestate);
11288 actor->flags2 |= MF2_JUSTATTACKED;
11289 return;
11290 }
11291 // Else, check for missile attack.
11292 else if (actor->info->missilestate)
11293 {
11294 P_SetMobjState(actor, actor->info->missilestate);
11295 actor->flags2 |= MF2_JUSTATTACKED;
11296 return;
11297 }
11298 }
11299
11300 // possibly choose another target
11301 if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
11302 && P_LookForPlayers(actor, true, false, 0))
11303 return; // got a new target
11304
11305 // chase towards player
11306 if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
11307 P_NewChaseDir(actor);
11308
11309 // Optionally play a sound effect
11310 if (locvar2 > 0 && locvar2 < NUMSFX)
11311 S_StartSound(actor, (sfxenum_t)locvar2);
11312
11313 // make active sound
11314 if (actor->type != MT_CYBRAKDEMON && actor->info->activesound && P_RandomChance(3*FRACUNIT/256))
11315 {
11316 S_StartSound(actor, actor->info->activesound);
11317 }
11318 }
11319
11320 // Function: A_BrakFireShot
11321 //
11322 // Description: Shoot an object at your target, offset to match where Brak's gun is.
11323 // Also, sets Brak's reaction time; behaves normally otherwise.
11324 //
11325 // var1 = object # to shoot
11326 // var2 = unused
11327 //
A_BrakFireShot(mobj_t * actor)11328 void A_BrakFireShot(mobj_t *actor)
11329 {
11330 fixed_t x, y, z;
11331 INT32 locvar1 = var1;
11332
11333 if (LUA_CallAction(A_BRAKFIRESHOT, actor))
11334 return;
11335
11336 if (!actor->target)
11337 return;
11338
11339 A_FaceTarget(actor);
11340
11341 x = actor->x
11342 + P_ReturnThrustX(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
11343 + P_ReturnThrustX(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
11344 y = actor->y
11345 + P_ReturnThrustY(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
11346 + P_ReturnThrustY(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
11347 if (actor->eflags & MFE_VERTICALFLIP)
11348 z = actor->z + actor->height - FixedMul(144*FRACUNIT, actor->scale);
11349 else
11350 z = actor->z + FixedMul(144*FRACUNIT, actor->scale);
11351
11352 P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
11353
11354 if (!(actor->flags & MF_BOSS))
11355 {
11356 if (ultimatemode)
11357 actor->reactiontime = actor->info->reactiontime*TICRATE;
11358 else
11359 actor->reactiontime = actor->info->reactiontime*TICRATE*2;
11360 }
11361 }
11362
11363 // Function: A_BrakLobShot
11364 //
11365 // Description: Lobs an object at the floor about a third of the way toward your target.
11366 // Implication is it'll bounce the rest of the way.
11367 // (You can also just aim straight at the target, but whatever)
11368 // Formula grabbed from http://en.wikipedia.org/wiki/Trajectory_of_a_projectile#Angle_required_to_hit_coordinate_.28x.2Cy.29
11369 //
11370 // var1 = object # to lob
11371 // var2:
11372 // Lower 16 bits: height offset to shoot from, from the actor's bottom (none that "airtime" malarky)
11373 // Upper 16 bits: if 0, aim 1/3 of the way. Else, aim directly at target.
11374 //
11375
A_BrakLobShot(mobj_t * actor)11376 void A_BrakLobShot(mobj_t *actor)
11377 {
11378 fixed_t v; // Velocity to shoot object
11379 fixed_t a1, a2, aToUse; // Velocity squared
11380 fixed_t g; // Gravity
11381 fixed_t x; // Horizontal difference
11382 INT32 x_int; // x! But in integer form!
11383 fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
11384 INT32 y_int; // y! But in integer form!
11385 INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
11386 fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
11387 angle_t theta; // Angle of attack
11388 mobjtype_t typeOfShot;
11389 mobj_t *shot; // Object to shoot
11390 fixed_t newTargetX; // If not aiming directly
11391 fixed_t newTargetY; // If not aiming directly
11392 INT32 locvar1 = var1;
11393 INT32 locvar2 = var2 & 0x0000FFFF;
11394 INT32 aimDirect = var2 & 0xFFFF0000;
11395
11396 if (LUA_CallAction(A_BRAKLOBSHOT, actor))
11397 return;
11398
11399 if (!actor->target)
11400 return; // Don't even bother if we've got nothing to aim at.
11401
11402 // Look up actor's current gravity situation
11403 if (actor->subsector->sector->gravity)
11404 g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
11405 else
11406 g = gravity;
11407
11408 // Look up distance between actor and its target
11409 x = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
11410 if (!aimDirect)
11411 {
11412 // Distance should actually be a third of the way over
11413 x = FixedDiv(x, 3<<FRACBITS);
11414 newTargetX = actor->x + P_ReturnThrustX(actor, actor->angle, x);
11415 newTargetY = actor->y + P_ReturnThrustY(actor, actor->angle, x);
11416 x = P_AproxDistance(newTargetX - actor->x, newTargetY - actor->y);
11417 // Look up height difference between actor and the ground 1/3 of the way to its target
11418 y = P_FloorzAtPos(newTargetX, newTargetY, actor->target->z, actor->target->height) - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
11419 }
11420 else
11421 {
11422 // Look up height difference between actor and its target
11423 y = actor->target->z - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
11424 }
11425
11426 // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
11427 x_int = x>>FRACBITS;
11428 y_int = y>>FRACBITS;
11429 intHypotenuse = (x_int*x_int) + (y_int*y_int);
11430 fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
11431
11432 // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
11433 a1 = FixedMul(g,y+fixedHypotenuse);
11434 a2 = FixedMul(g,y-fixedHypotenuse);
11435
11436 // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
11437 if (a1 < 0 || a2 < 0)
11438 {
11439 if (a1 < 0 && a2 < 0)
11440 {
11441 //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
11442 return;
11443 }
11444 // Just find which one's NOT negative, and use that
11445 aToUse = max(a1,a2);
11446 }
11447 else
11448 {
11449 // Both are positive; use whichever's smaller so it can decay faster
11450 aToUse = min(a1,a2);
11451 }
11452 v = FixedSqrt(aToUse);
11453 // Okay, so we know the velocity. Let's actually find theta.
11454 // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
11455 //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
11456 theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
11457
11458 // Okay, complicated math done. Let's fire our object already, sheesh.
11459 A_FaceTarget(actor);
11460 if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
11461 typeOfShot = MT_CANNONBALL;
11462 else typeOfShot = (mobjtype_t)locvar1;
11463 shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot);
11464 if (shot->info->seesound)
11465 S_StartSound(shot, shot->info->seesound);
11466 P_SetTarget(&shot->target, actor); // where it came from
11467
11468 shot->angle = actor->angle;
11469
11470 // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
11471 shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT));
11472 shot->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(shot->angle >> ANGLETOFINESHIFT));
11473 // Then the vertical axis. No angle-correction needed here.
11474 shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
11475 // I hope that's all that's needed, ugh
11476 }
11477
11478 // Function: A_NapalmScatter
11479 //
11480 // Description: Scatters a specific number of projectiles around in a circle.
11481 // Intended for use with objects that are affected by gravity; would be kind of silly otherwise.
11482 //
11483 // var1:
11484 // Lower 16 bits: object # to lob (TODO: come up with a default)
11485 // Upper 16 bits: Number to lob (default 8)
11486 // var2:
11487 // Lower 16 bits: distance to toss them (No default - 0 does just that - but negatives will revert to 128)
11488 // Upper 16 bits: airtime in tics (default 16)
11489 //
A_NapalmScatter(mobj_t * actor)11490 void A_NapalmScatter(mobj_t *actor)
11491 {
11492 mobjtype_t typeOfShot = var1 & 0x0000FFFF; // Type
11493 INT32 numToShoot = (var1 & 0xFFFF0000) >> 16; // How many
11494 fixed_t distance = (var2 & 0x0000FFFF) << FRACBITS; // How far
11495 fixed_t airtime = var2 & 0xFFFF0000; // How long until impact (assuming no obstacles)
11496 fixed_t vx; // Horizontal momentum
11497 fixed_t vy; // Vertical momentum
11498 fixed_t g; // Gravity
11499 INT32 i; // for-loop cursor
11500 mobj_t *mo; // each and every spawned napalm burst
11501
11502 if (LUA_CallAction(A_NAPALMSCATTER, actor))
11503 return;
11504
11505 // Some quick sanity-checking
11506 if (typeOfShot >= NUMMOBJTYPES) // I'd add a <0 check, too, but 0x0000FFFF isn't negative in this case
11507 typeOfShot = MT_NULL;
11508 if (numToShoot <= 0) // Presumably you forgot to set var1 up; else, why are you calling this to shoot nothing?
11509 numToShoot = 8;
11510 else if (numToShoot > 8192) // If you seriously need this many objects spawned, stop and ask yourself "Why am I doing this?"
11511 numToShoot = 8192;
11512 if (distance < 0) // Presumably you thought this was an unsigned integer, you naive fool
11513 distance = 32767<<FRACBITS;
11514 if (airtime <= 0) // Same deal as distance I guess
11515 airtime = 16<<FRACBITS;
11516
11517 // Look up actor's current gravity situation
11518 if (actor->subsector->sector->gravity)
11519 g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
11520 else
11521 g = gravity;
11522
11523 // vy = (g*(airtime-1))/2
11524 vy = FixedMul(g,(airtime-(1<<FRACBITS)))>>1;
11525 // vx = distance/airtime
11526 vx = FixedDiv(distance, airtime);
11527
11528 for (i = 0; i<numToShoot; i++)
11529 {
11530 const angle_t fa = (i*FINEANGLES/numToShoot) & FINEMASK;
11531
11532 mo = P_SpawnMobj(actor->x, actor->y, actor->z, typeOfShot);
11533 P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot
11534
11535 mo->angle = fa << ANGLETOFINESHIFT;
11536 mo->momx = FixedMul(FINECOSINE(fa),vx);
11537 mo->momy = FixedMul(FINESINE(fa),vx);
11538 mo->momz = vy;
11539 }
11540 }
11541
11542 // Function: A_SpawnFreshCopy
11543 //
11544 // Description: Spawns a copy of the mobj. x, y, z, angle, scale, target and tracer carry over; everything else starts anew.
11545 // Mostly writing this because I want to do multiple actions to pass these along in a single frame instead of several.
11546 //
11547 // var1 = unused
11548 // var2 = unused
11549 //
A_SpawnFreshCopy(mobj_t * actor)11550 void A_SpawnFreshCopy(mobj_t *actor)
11551 {
11552 mobj_t *newObject;
11553
11554 if (LUA_CallAction(A_SPAWNFRESHCOPY, actor))
11555 return;
11556
11557 newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type);
11558 newObject->flags2 = actor->flags2 & MF2_AMBUSH;
11559 newObject->angle = actor->angle;
11560 newObject->color = actor->color;
11561 P_SetTarget(&newObject->target, actor->target);
11562 P_SetTarget(&newObject->tracer, actor->tracer);
11563
11564 if (newObject->info->seesound)
11565 S_StartSound(newObject, newObject->info->seesound);
11566 }
11567
11568 // Internal Flicky spawning function.
P_InternalFlickySpawn(mobj_t * actor,mobjtype_t flickytype,fixed_t momz,boolean lookforplayers,SINT8 moveforward)11569 mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers, SINT8 moveforward)
11570 {
11571 mobj_t *flicky;
11572 fixed_t offsx = 0, offsy = 0;
11573
11574 if (!flickytype)
11575 {
11576 if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
11577 return NULL;
11578 else
11579 {
11580 INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
11581 flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
11582 }
11583 }
11584
11585 if (moveforward)
11586 {
11587 fixed_t scal = mobjinfo[flickytype].radius*((fixed_t)moveforward);
11588 offsx = P_ReturnThrustX(actor, actor->angle, scal);
11589 offsy = P_ReturnThrustY(actor, actor->angle, scal);
11590 }
11591
11592 flicky = P_SpawnMobjFromMobj(actor, offsx, offsy, 0, flickytype);
11593 flicky->angle = actor->angle;
11594
11595 if (flickytype == MT_SEED)
11596 flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
11597
11598 if (actor->eflags & MFE_UNDERWATER)
11599 momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
11600
11601 P_SetObjectMomZ(flicky, momz, false);
11602 flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1);
11603 flicky->fuse = P_RandomRange(595, 700); // originally 300, 350
11604 flicky->threshold = 0;
11605
11606 if (lookforplayers)
11607 P_LookForPlayers(flicky, true, false, 0);
11608
11609 return flicky;
11610 }
11611
11612 // Function: A_FlickySpawn
11613 //
11614 // Description: Flicky spawning function.
11615 //
11616 // var1:
11617 // lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
11618 // bit 17: if 0, no sound is played. Else, A_Scream is called.
11619 // bit 18: if 1, spawn flicky slightly forward from spawn position, to avoid being stuck in wall. doesn't stack with 19. (snailers)
11620 // bit 19: if 1, spawn flicky slightly backward from spawn position. doesn't stack with 18.
11621 // var2 = upwards thrust for spawned flicky. If zero, default value is provided.
11622 //
A_FlickySpawn(mobj_t * actor)11623 void A_FlickySpawn(mobj_t *actor)
11624 {
11625 INT32 locvar1 = var1 & 65535;
11626 INT32 locvar2 = var2;
11627 INT32 test = (var1 >> 16);
11628 SINT8 moveforward = 0;
11629
11630 if (LUA_CallAction(A_FLICKYSPAWN, actor))
11631 return;
11632
11633 if (test & 1)
11634 A_Scream(actor); // A shortcut for the truly lazy.
11635 if (test & 2)
11636 moveforward = 1;
11637 else if (test & 4)
11638 moveforward = -1;
11639
11640 P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true, moveforward);
11641 }
11642
11643 // Internal Flicky color setting
P_InternalFlickySetColor(mobj_t * actor,UINT8 extrainfo)11644 void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo)
11645 {
11646 UINT8 flickycolors[] = {
11647 SKINCOLOR_RED,
11648 SKINCOLOR_CYAN,
11649 SKINCOLOR_BLUE,
11650 SKINCOLOR_VAPOR,
11651 SKINCOLOR_PURPLE,
11652 SKINCOLOR_BUBBLEGUM,
11653 SKINCOLOR_NEON,
11654 SKINCOLOR_BLACK,
11655 SKINCOLOR_BEIGE,
11656 SKINCOLOR_LAVENDER,
11657 SKINCOLOR_RUBY,
11658 SKINCOLOR_SALMON,
11659 SKINCOLOR_SUNSET,
11660 SKINCOLOR_ORANGE,
11661 SKINCOLOR_YELLOW,
11662 };
11663
11664 if (extrainfo == 0)
11665 // until we can customize flicky colors by level header, just stick to SRB2's defaults
11666 actor->color = flickycolors[P_RandomKey(2)]; //flickycolors[P_RandomKey(sizeof(flickycolors))];
11667 else
11668 actor->color = flickycolors[min(extrainfo-1, 14)]; // sizeof(flickycolors)-1
11669 }
11670
11671 // Function: A_FlickyCenter
11672 //
11673 // Description: Place flickies in-level.
11674 //
11675 // var1:
11676 // Lower 16 bits = if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
11677 // Bits 17-20 = Flicky color, up to 15. Applies to fish.
11678 // Bit 21 = Flag MF_SLIDEME (see below)
11679 // Bit 22 = Flag MF_GRENADEBOUNCE (see below)
11680 // Bit 23 = Flag MF_NOCLIPTHING (see below)
11681 //
11682 // If actor is placed from a spawnpoint (map Thing), the Thing's properties take precedence.
11683 //
11684 // var2 = maximum default distance away from spawn the flickies are allowed to travel. If angle != 0, then that's the radius.
11685 //
11686 // If MTF_EXTRA (MF_SLIDEME): is flagged, Flickies move aimlessly. Else, orbit around the target.
11687 // If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE): Flickies stand in-place without gravity (unless they hop, then gravity is applied.)
11688 // If MTF_AMBUSH (MF_NOCLIPTHING): is flagged, Flickies hop.
11689 //
A_FlickyCenter(mobj_t * actor)11690 void A_FlickyCenter(mobj_t *actor)
11691 {
11692 INT32 locvar1 = var1;
11693 INT32 locvar2 = var2;
11694 UINT16 flickytype = (locvar1 & 0xFFFF);
11695 UINT8 flickycolor = ((locvar1 >> 16) & 0xFF);
11696 UINT8 flickyflags = ((locvar1 >> 20) & 0xF);
11697
11698 if (LUA_CallAction(A_FLICKYCENTER, actor))
11699 return;
11700
11701 if (!actor->tracer)
11702 {
11703 mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false, 0);
11704 P_SetTarget(&flicky->target, actor);
11705 P_SetTarget(&actor->tracer, flicky);
11706
11707 if (actor->spawnpoint)
11708 {
11709 actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
11710 actor->flags |= (
11711 ((actor->spawnpoint->options & MTF_EXTRA) ? MF_SLIDEME : 0)
11712 | ((actor->spawnpoint->options & MTF_OBJECTSPECIAL) ? MF_GRENADEBOUNCE : 0)
11713 | ((actor->spawnpoint->options & MTF_AMBUSH) ? MF_NOCLIPTHING : 0)
11714 );
11715 actor->extravalue1 = actor->spawnpoint->angle ? abs(actor->spawnpoint->angle) * FRACUNIT
11716 : locvar2 ? abs(locvar2) : 384 * FRACUNIT;
11717 actor->extravalue2 = actor->spawnpoint->extrainfo;
11718 actor->friction = actor->spawnpoint->x*FRACUNIT;
11719 actor->movefactor = actor->spawnpoint->y*FRACUNIT;
11720 actor->watertop = actor->spawnpoint->z*FRACUNIT;
11721 }
11722 else
11723 {
11724 actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
11725 actor->flags |= (
11726 ((flickyflags & 1) ? MF_SLIDEME : 0)
11727 | ((flickyflags & 2) ? MF_GRENADEBOUNCE : 0)
11728 | ((flickyflags & 4) ? MF_NOCLIPTHING : 0)
11729 );
11730 actor->extravalue1 = abs(locvar2);
11731 actor->extravalue2 = flickycolor;
11732 actor->friction = actor->x;
11733 actor->movefactor = actor->y;
11734 actor->watertop = actor->z;
11735 locvar1 = flickytype;
11736 }
11737
11738 if (actor->flags & MF_GRENADEBOUNCE) // in-place
11739 actor->tracer->fuse = 0;
11740 else if (actor->flags & MF_SLIDEME) // aimless
11741 {
11742 actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly.
11743 actor->tracer->angle = P_RandomKey(180)*ANG2;
11744 }
11745 else //orbit
11746 actor->tracer->fuse = FRACUNIT;
11747
11748 if (locvar1 == MT_FLICKY_08)
11749 P_InternalFlickySetColor(actor->tracer, actor->extravalue2);
11750
11751 actor->extravalue2 = 0;
11752 }
11753
11754 if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_GRENADEBOUNCE))
11755 {
11756 fixed_t originx = actor->friction;
11757 fixed_t originy = actor->movefactor;
11758 fixed_t originz = actor->watertop;
11759
11760 actor->tracer->fuse = FRACUNIT;
11761
11762 // Impose default home radius if flicky orbits around player
11763 if (!actor->extravalue1)
11764 actor->extravalue1 = locvar2 ? abs(locvar2) : 384 * FRACUNIT;
11765
11766 P_LookForPlayers(actor, true, false, actor->extravalue1);
11767
11768 if (actor->target && P_AproxDistance(actor->target->x - originx, actor->target->y - originy) < actor->extravalue1)
11769 {
11770 actor->extravalue2 = 1;
11771 P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z);
11772 }
11773 else if(actor->extravalue2)
11774 {
11775 actor->extravalue2 = 0;
11776 P_TeleportMove(actor, originx, originy, originz);
11777 }
11778 }
11779 }
11780
11781 // Internal Flicky bubbling function.
P_InternalFlickyBubble(mobj_t * actor)11782 void P_InternalFlickyBubble(mobj_t *actor)
11783 {
11784 if (actor->eflags & MFE_UNDERWATER)
11785 {
11786 mobj_t *overlay;
11787
11788 if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer)
11789 return;
11790
11791 overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
11792 P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate);
11793 P_SetTarget(&actor->tracer, overlay);
11794 P_SetTarget(&overlay->target, actor);
11795 return;
11796 }
11797
11798 if (!actor->tracer || P_MobjWasRemoved(actor->tracer))
11799 return;
11800
11801 P_RemoveMobj(actor->tracer);
11802 P_SetTarget(&actor->tracer, NULL);
11803 }
11804
11805 // Function: A_FlickyAim
11806 //
11807 // Description: Flicky aiming function.
11808 //
11809 // var1 = how far around the target (in angle constants) the flicky should look
11810 // var2 = distance from target to aim for
11811 //
A_FlickyAim(mobj_t * actor)11812 void A_FlickyAim(mobj_t *actor)
11813 {
11814 INT32 locvar1 = var1;
11815 INT32 locvar2 = var2;
11816 boolean flickyhitwall = false;
11817
11818 if (LUA_CallAction(A_FLICKYAIM, actor))
11819 return;
11820
11821 if ((actor->momx == actor->momy && actor->momy == 0)
11822 || (actor->target && P_IsFlickyCenter(actor->target->type)
11823 && actor->target->extravalue1 && (actor->target->flags & MF_SLIDEME)
11824 && P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) >= actor->target->extravalue1))
11825 flickyhitwall = true;
11826
11827 P_InternalFlickyBubble(actor);
11828 P_InstaThrust(actor, 0, 0);
11829
11830 if (!actor->target)
11831 {
11832 P_LookForPlayers(actor, true, false, 0);
11833 actor->angle = P_RandomKey(36)*ANG10;
11834 return;
11835 }
11836
11837 if (actor->fuse > 2*TICRATE)
11838 {
11839 angle_t posvar;
11840 fixed_t chasevar, chasex, chasey;
11841
11842 if (flickyhitwall)
11843 actor->movedir *= -1;
11844
11845 posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK;
11846 chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2;
11847
11848 chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar);
11849 chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar);
11850
11851 if (P_AproxDistance(chasex - actor->x, chasey - actor->y))
11852 actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey);
11853 }
11854 else if (flickyhitwall)
11855 {
11856 if (actor->target && P_IsFlickyCenter(actor->target->type))
11857 actor->angle = R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + P_RandomRange(112, 248) * ANG1;
11858 else
11859 actor->angle += P_RandomRange(112, 248)*ANG1;
11860 actor->threshold = 0;
11861 }
11862 }
11863
11864 //Internal Flicky flying function. Also usuable as an underwater swim thrust.
P_InternalFlickyFly(mobj_t * actor,fixed_t flyspeed,fixed_t targetdist,fixed_t chasez)11865 void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez)
11866 {
11867 angle_t vertangle;
11868
11869 flyspeed = FixedMul(flyspeed, actor->scale);
11870 actor->flags |= MF_NOGRAVITY;
11871
11872 var1 = ANG30;
11873 var2 = 32*FRACUNIT;
11874 A_FlickyAim(actor);
11875
11876 chasez *= 8;
11877 if (!actor->target || !(actor->fuse > 2*TICRATE))
11878 chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT);
11879 else
11880 {
11881 fixed_t add = actor->target->z + (actor->target->height - actor->height)/2;
11882 if (add > (actor->ceilingz - 24*actor->scale - actor->height))
11883 add = actor->ceilingz - 24*actor->scale - actor->height;
11884 else if (add < (actor->floorz + 24*actor->scale))
11885 add = actor->floorz + 24*actor->scale;
11886 chasez += add;
11887 }
11888
11889 if (!targetdist)
11890 targetdist = 16*FRACUNIT; //Default!
11891
11892 if (actor->target && abs(chasez - actor->z) > targetdist)
11893 targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
11894
11895 if (actor->target
11896 && P_IsFlickyCenter(actor->target->type)
11897 && (actor->target->flags & MF_SLIDEME))
11898 vertangle = 0;
11899 else
11900 vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK;
11901
11902 P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed));
11903 actor->momz = FixedMul(FINESINE(vertangle), flyspeed);
11904 }
11905
11906 // Function: A_FlickyFly
11907 //
11908 // Description: Flicky flying function.
11909 //
11910 // var1 = how fast to fly
11911 // var2 = how far ahead the target should be considered
11912 //
A_FlickyFly(mobj_t * actor)11913 void A_FlickyFly(mobj_t *actor)
11914 {
11915 INT32 locvar1 = var1;
11916 INT32 locvar2 = var2;
11917
11918 if (LUA_CallAction(A_FLICKYFLY, actor))
11919 return;
11920
11921 P_InternalFlickyFly(actor, locvar1, locvar2,
11922 FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK)
11923 );
11924 }
11925
11926 // Function: A_FlickySoar
11927 //
11928 // Description: Flicky soaring function - specific to puffin.
11929 //
11930 // var1 = how fast to fly
11931 // var2 = how far ahead the target should be considered
11932 //
A_FlickySoar(mobj_t * actor)11933 void A_FlickySoar(mobj_t *actor)
11934 {
11935 INT32 locvar1 = var1;
11936 INT32 locvar2 = var2;
11937
11938 if (LUA_CallAction(A_FLICKYSOAR, actor))
11939 return;
11940
11941 P_InternalFlickyFly(actor, locvar1, locvar2,
11942 2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK)))
11943 );
11944
11945 if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10)
11946 actor->frame = 3;
11947 }
11948
11949 //Function: A_FlickyCoast
11950 //
11951 // Description: Flicky swim-coasting function.
11952 //
11953 // var1 = speed to change state upon reaching
11954 // var2 = state to change to upon slowing down
11955 // the spawnstate of the mobj = state to change to when above water
11956 //
A_FlickyCoast(mobj_t * actor)11957 void A_FlickyCoast(mobj_t *actor)
11958 {
11959 INT32 locvar1 = var1;
11960 INT32 locvar2 = var2;
11961
11962 if (LUA_CallAction(A_FLICKYCOAST, actor))
11963 return;
11964
11965 if (actor->eflags & MFE_UNDERWATER)
11966 {
11967 actor->momx = (11*actor->momx)/12;
11968 actor->momy = (11*actor->momy)/12;
11969 actor->momz = (11*actor->momz)/12;
11970
11971 if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1)
11972 P_SetMobjState(actor, locvar2);
11973
11974 return;
11975 }
11976
11977 actor->flags &= ~MF_NOGRAVITY;
11978 P_SetMobjState(actor, mobjinfo[actor->type].spawnstate);
11979 }
11980
11981 // Internal Flicky hopping function.
P_InternalFlickyHop(mobj_t * actor,fixed_t momz,fixed_t momh,angle_t angle)11982 void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle)
11983 {
11984 if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
11985 || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
11986 {
11987 if (momz)
11988 {
11989 if (actor->eflags & MFE_UNDERWATER)
11990 momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
11991 P_SetObjectMomZ(actor, momz, false);
11992 }
11993 P_InstaThrust(actor, angle, FixedMul(momh, actor->scale));
11994 }
11995 }
11996
11997 // Function: A_FlickyHop
11998 //
11999 // Description: Flicky hopping function.
12000 //
12001 // var1 = vertical thrust
12002 // var2 = horizontal thrust
12003 //
A_FlickyHop(mobj_t * actor)12004 void A_FlickyHop(mobj_t *actor)
12005 {
12006 INT32 locvar1 = var1;
12007 INT32 locvar2 = var2;
12008
12009 if (LUA_CallAction(A_FLICKYHOP, actor))
12010 return;
12011
12012 P_InternalFlickyHop(actor, locvar1, locvar2, actor->angle);
12013 }
12014
12015 // Function: A_FlickyFlounder
12016 //
12017 // Description: Flicky floundering function.
12018 //
12019 // var1 = intended vertical thrust
12020 // var2 = intended horizontal thrust
12021 //
A_FlickyFlounder(mobj_t * actor)12022 void A_FlickyFlounder(mobj_t *actor)
12023 {
12024 INT32 locvar1 = var1;
12025 INT32 locvar2 = var2;
12026 angle_t hopangle;
12027
12028 if (LUA_CallAction(A_FLICKYFLOUNDER, actor))
12029 return;
12030
12031 locvar1 *= (P_RandomKey(2) + 1);
12032 locvar2 *= (P_RandomKey(2) + 1);
12033 hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2);
12034 P_InternalFlickyHop(actor, locvar1, locvar2, hopangle);
12035 }
12036
12037 // Function: A_FlickyCheck
12038 //
12039 // Description: Flicky airtime check function.
12040 //
12041 // var1 = state to change to upon touching the floor
12042 // var2 = state to change to upon falling
12043 // the meleestate of the mobj = state to change to when underwater
12044 //
A_FlickyCheck(mobj_t * actor)12045 void A_FlickyCheck(mobj_t *actor)
12046 {
12047 INT32 locvar1 = var1;
12048 INT32 locvar2 = var2;
12049
12050 if (LUA_CallAction(A_FLICKYCHECK, actor))
12051 return;
12052
12053 if (actor->target
12054 && P_IsFlickyCenter(actor->target->type)
12055 && (actor->target->flags & MF_GRENADEBOUNCE))
12056 {
12057 if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping
12058 {
12059 actor->momz = 0;
12060 actor->flags |= MF_NOGRAVITY;
12061 }
12062 actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY;
12063 P_SetMobjState(actor, mobjinfo[actor->type].seestate);
12064 }
12065 else if (locvar2 && P_MobjFlip(actor)*actor->momz < 1)
12066 P_SetMobjState(actor, locvar2);
12067 else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
12068 || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
12069 P_SetMobjState(actor, locvar1);
12070 else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER))
12071 P_SetMobjState(actor, mobjinfo[actor->type].meleestate);
12072 P_InternalFlickyBubble(actor);
12073 }
12074
12075 // Function: A_FlickyHeightCheck
12076 //
12077 // Description: Flicky height check function.
12078 //
12079 // var1 = state to change to when falling below height relative to target
12080 // var2 = height relative to target to change state at
12081 //
A_FlickyHeightCheck(mobj_t * actor)12082 void A_FlickyHeightCheck(mobj_t *actor)
12083 {
12084 INT32 locvar1 = var1;
12085 INT32 locvar2 = var2;
12086
12087 if (LUA_CallAction(A_FLICKYHEIGHTCHECK, actor))
12088 return;
12089
12090 if (actor->target
12091 && P_IsFlickyCenter(actor->target->type)
12092 && (actor->target->flags & MF_GRENADEBOUNCE))
12093 {
12094 if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping
12095 {
12096 actor->momz = 0;
12097 actor->flags |= MF_NOGRAVITY;
12098 }
12099 actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY;
12100 P_SetMobjState(actor, mobjinfo[actor->type].seestate);
12101 }
12102 else if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1
12103 && ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2)
12104 || (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz)))
12105 P_SetMobjState(actor, locvar1);
12106 P_InternalFlickyBubble(actor);
12107 }
12108
12109 // Function: A_FlickyFlutter
12110 //
12111 // Description: Flicky fluttering function - specific to chicken.
12112 //
12113 // var1 = state to change to upon touching the floor
12114 // var2 = state to change to upon falling
12115 // the meleestate of the mobj = state to change to when underwater
12116 //
A_FlickyFlutter(mobj_t * actor)12117 void A_FlickyFlutter(mobj_t *actor)
12118 {
12119 INT32 locvar1 = var1;
12120 INT32 locvar2 = var2;
12121
12122 if (LUA_CallAction(A_FLICKYFLUTTER, actor))
12123 return;
12124
12125 var1 = locvar1;
12126 var2 = locvar2;
12127 A_FlickyCheck(actor);
12128
12129 var1 = ANG30;
12130 var2 = 32*FRACUNIT;
12131 A_FlickyAim(actor);
12132
12133 P_InstaThrust(actor, actor->angle, 2*actor->scale);
12134 if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2)
12135 actor->momz = -P_MobjFlip(actor)*actor->scale/2;
12136 }
12137
12138 #undef FLICKYHITWALL
12139
12140 // Function: A_FlameParticle
12141 //
12142 // Description: Creates the mobj's painchance at a random position around the object's radius.
12143 //
12144 // var1 = unused
12145 // var2 = unused
12146 //
A_FlameParticle(mobj_t * actor)12147 void A_FlameParticle(mobj_t *actor)
12148 {
12149 mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance);
12150 fixed_t rad, hei;
12151 mobj_t *particle;
12152
12153 if (LUA_CallAction(A_FLAMEPARTICLE, actor))
12154 return;
12155
12156 if (!type)
12157 return;
12158
12159 rad = actor->radius>>FRACBITS;
12160 hei = actor->height>>FRACBITS;
12161 particle = P_SpawnMobjFromMobj(actor,
12162 P_RandomRange(rad, -rad)<<FRACBITS,
12163 P_RandomRange(rad, -rad)<<FRACBITS,
12164 P_RandomRange(hei/2, hei)<<FRACBITS,
12165 type);
12166 P_SetObjectMomZ(particle, 2<<FRACBITS, false);
12167 }
12168
12169 // Function: A_FadeOverlay
12170 //
12171 // Description: Makes a pretty overlay (primarily for super/NiGHTS transformation).
12172 //
12173 // var1 = bit 1 = bit 1 = don't make fast, bit 2 = don't set tracer
12174 // var2 = unused
12175 //
A_FadeOverlay(mobj_t * actor)12176 void A_FadeOverlay(mobj_t *actor)
12177 {
12178 mobj_t *fade;
12179 INT32 locvar1 = var1;
12180
12181 if (LUA_CallAction(A_FADEOVERLAY, actor))
12182 return;
12183
12184 fade = P_SpawnGhostMobj(actor);
12185 fade->frame = actor->frame;
12186
12187 if (!(locvar1 & 1))
12188 {
12189 fade->fuse = 15;
12190 fade->flags2 |= MF2_BOSSNOTRAP;
12191 }
12192 else
12193 fade->fuse = 20;
12194
12195 if (!(locvar1 & 2))
12196 P_SetTarget(&actor->tracer, fade);
12197 }
12198
12199 // Function: A_Boss5Jump
12200 //
12201 // Description: Makes an object jump in an arc to land on their tracer precicely.
12202 // Adapted from A_BrakLobShot, see there for explanation.
12203 //
12204 // var1 = unused
12205 // var2 = unused
12206 //
A_Boss5Jump(mobj_t * actor)12207 void A_Boss5Jump(mobj_t *actor)
12208 {
12209 fixed_t v; // Velocity to jump at
12210 fixed_t a1, a2, aToUse; // Velocity squared
12211 fixed_t g; // Gravity
12212 fixed_t x; // Horizontal difference
12213 INT32 x_int; // x! But in integer form!
12214 fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
12215 INT32 y_int; // y! But in integer form!
12216 INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
12217 fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
12218 angle_t theta; // Angle of attack
12219 // INT32 locvar1 = var1;
12220 // INT32 locvar2 = var2;
12221
12222 if (LUA_CallAction(A_BOSS5JUMP, actor))
12223 return;
12224
12225 if (!actor->tracer)
12226 return; // Don't even bother if we've got nothing to aim at.
12227
12228 // Look up actor's current gravity situation
12229 if (actor->subsector->sector->gravity)
12230 g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
12231 else
12232 g = gravity;
12233
12234 // Look up distance between actor and its tracer
12235 x = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
12236 // Look up height difference between actor and its tracer
12237 y = actor->tracer->z - actor->z;
12238
12239 // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
12240 x_int = x>>FRACBITS;
12241 y_int = y>>FRACBITS;
12242 intHypotenuse = (x_int*x_int) + (y_int*y_int);
12243 fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
12244
12245 // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
12246 a1 = FixedMul(g,y+fixedHypotenuse);
12247 a2 = FixedMul(g,y-fixedHypotenuse);
12248
12249 // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
12250 if (a1 < 0 || a2 < 0)
12251 {
12252 if (a1 < 0 && a2 < 0)
12253 {
12254 //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
12255 return;
12256 }
12257 // Just find which one's NOT negative, and use that
12258 aToUse = max(a1,a2);
12259 }
12260 else
12261 {
12262 // Both are positive; use whichever's smaller so it can decay faster
12263 aToUse = min(a1,a2);
12264 }
12265 v = FixedSqrt(aToUse);
12266 // Okay, so we know the velocity. Let's actually find theta.
12267 // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
12268 //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
12269 theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
12270
12271 // Okay, complicated math done. Let's make this object jump already.
12272 A_FaceTracer(actor);
12273
12274 if (actor->eflags & MFE_VERTICALFLIP)
12275 actor->z--;
12276 else
12277 actor->z++;
12278
12279 // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
12280 fixedHypotenuse = FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)); // variable reuse
12281 actor->momx = FixedMul(fixedHypotenuse, FINECOSINE(actor->angle >> ANGLETOFINESHIFT));
12282 actor->momy = FixedMul(fixedHypotenuse, FINESINE(actor->angle >> ANGLETOFINESHIFT));
12283 // Then the vertical axis. No angle-correction needed here.
12284 actor->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
12285 // I hope that's all that's needed, ugh
12286 }
12287
12288 // Function: A_LightBeamReset
12289 // Description: Resets momentum and position for DSZ's projecting light beams
12290 //
12291 // var1 = unused
12292 // var2 = unused
12293 //
A_LightBeamReset(mobj_t * actor)12294 void A_LightBeamReset(mobj_t *actor)
12295 {
12296 // INT32 locvar1 = var1;
12297 // INT32 locvar2 = var2;
12298
12299 if (LUA_CallAction(A_LIGHTBEAMRESET, actor))
12300 return;
12301
12302 actor->destscale = FRACUNIT + P_SignedRandom()*FRACUNIT/256;
12303 P_SetScale(actor, actor->destscale);
12304
12305 if (!actor->spawnpoint)
12306 return; // this can't work properly welp
12307
12308 actor->momx = -(P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128;
12309 actor->momy = (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128;
12310 actor->momz = (P_SignedRandom()*FRACUNIT)/128;
12311
12312 P_TeleportMove(actor,
12313 actor->spawnpoint->x*FRACUNIT - (P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2,
12314 actor->spawnpoint->y*FRACUNIT + (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2,
12315 actor->spawnpoint->z*FRACUNIT + (P_SignedRandom()*FRACUNIT)/2);
12316 }
12317
12318 // Function: A_MineExplode
12319 // Description: Handles the explosion of a DSZ mine.
12320 //
12321 // var1 = unused
12322 // var2 = unused
12323 //
A_MineExplode(mobj_t * actor)12324 void A_MineExplode(mobj_t *actor)
12325 {
12326 // INT32 locvar1 = var1;
12327 // INT32 locvar2 = var2;
12328
12329 if (LUA_CallAction(A_MINEEXPLODE, actor))
12330 return;
12331
12332 A_Scream(actor);
12333 actor->flags = MF_NOGRAVITY|MF_NOCLIP;
12334
12335 quake.epicenter = NULL;
12336 quake.radius = 512*FRACUNIT;
12337 quake.intensity = 8*FRACUNIT;
12338 quake.time = TICRATE/3;
12339
12340 P_RadiusAttack(actor, actor->tracer, 192*FRACUNIT, DMG_CANHURTSELF, true);
12341 P_MobjCheckWater(actor);
12342
12343 {
12344 #define dist 64
12345 UINT8 i;
12346 mobjtype_t type = ((actor->eflags & MFE_UNDERWATER) ? MT_UWEXPLODE : MT_SONIC3KBOSSEXPLODE);
12347 S_StartSound(actor, ((actor->eflags & MFE_UNDERWATER) ? sfx_s3k57 : sfx_s3k4e));
12348 P_SpawnMobj(actor->x, actor->y, actor->z, type);
12349 for (i = 0; i < 16; i++)
12350 {
12351 mobj_t *b = P_SpawnMobj(actor->x+P_RandomRange(-dist, dist)*FRACUNIT,
12352 actor->y+P_RandomRange(-dist, dist)*FRACUNIT,
12353 actor->z+P_RandomRange(((actor->eflags & MFE_UNDERWATER) ? -dist : 0), dist)*FRACUNIT,
12354 type);
12355 fixed_t dx = b->x - actor->x, dy = b->y - actor->y, dz = b->z - actor->z;
12356 fixed_t dm = P_AproxDistance(dz, P_AproxDistance(dy, dx));
12357 b->momx = FixedDiv(dx, dm)*3;
12358 b->momy = FixedDiv(dy, dm)*3;
12359 b->momz = FixedDiv(dz, dm)*3;
12360 if ((actor->watertop == INT32_MAX) || (b->z + b->height > actor->watertop))
12361 b->flags &= ~MF_NOGRAVITY;
12362 }
12363 #undef dist
12364
12365 if (actor->watertop != INT32_MAX)
12366 P_SpawnMobj(actor->x, actor->y, actor->watertop, (actor->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH);
12367 }
12368 }
12369
12370 // Function: A_MineRange
12371 // Description: If the target gets too close, change the state to meleestate.
12372 //
12373 // var1 = Distance to alert at
12374 // var2 = unused
12375 //
A_MineRange(mobj_t * actor)12376 void A_MineRange(mobj_t *actor)
12377 {
12378 fixed_t dm;
12379 INT32 locvar1 = var1;
12380 // INT32 locvar2 = var2;
12381
12382 if (LUA_CallAction(A_MINERANGE, actor))
12383 return;
12384
12385 if (!actor->target)
12386 return;
12387
12388 dm = P_AproxDistance(actor->z - actor->target->z, P_AproxDistance(actor->y - actor->target->y, actor->x - actor->target->x));
12389 if ((dm>>FRACBITS) < locvar1)
12390 P_SetMobjState(actor, actor->info->meleestate);
12391 }
12392
12393 // Function: A_ConnectToGround
12394 // Description: Create a palm tree trunk/mine chain.
12395 //
12396 // var1 = Object type to connect to ground
12397 // var2 = Object type to place on ground
12398 //
A_ConnectToGround(mobj_t * actor)12399 void A_ConnectToGround(mobj_t *actor)
12400 {
12401 mobj_t *work;
12402 fixed_t workz;
12403 fixed_t workh;
12404 angle_t ang;
12405 INT32 locvar1 = var1;
12406 INT32 locvar2 = var2;
12407
12408 if (LUA_CallAction(A_CONNECTTOGROUND, actor))
12409 return;
12410
12411 if (actor->subsector->sector->ffloors)
12412 P_AdjustMobjFloorZ_FFloors(actor, actor->subsector->sector, 2);
12413
12414 if (actor->flags2 & MF2_OBJECTFLIP)
12415 workz = (actor->z + actor->height) - actor->ceilingz;
12416 else
12417 workz = actor->floorz - actor->z;
12418
12419 if (locvar2)
12420 {
12421 workh = FixedMul(mobjinfo[locvar2].height, actor->scale);
12422 if (actor->flags2 & MF2_OBJECTFLIP)
12423 workz += workh;
12424 work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar2);
12425 workz += workh;
12426 }
12427
12428 if (!locvar1)
12429 return;
12430
12431 if (!(workh = FixedMul(mobjinfo[locvar1].height, actor->scale)))
12432 return;
12433
12434 ang = actor->angle + ANGLE_45;
12435 while (workz < 0)
12436 {
12437 work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar1);
12438 if (work)
12439 work->angle = ang;
12440 ang += ANGLE_90;
12441 workz += workh;
12442 }
12443
12444 if (workz != 0)
12445 actor->z += P_MobjFlip(actor)*workz;
12446 }
12447
12448 // Function: A_SpawnParticleRelative
12449 //
12450 // Description: Spawns a particle effect relative to the location of the actor
12451 //
12452 // var1:
12453 // var1 >> 16 = x
12454 // var1 & 65535 = y
12455 // var2:
12456 // var2 >> 16 = z
12457 // var2 & 65535 = state
12458 //
A_SpawnParticleRelative(mobj_t * actor)12459 void A_SpawnParticleRelative(mobj_t *actor)
12460 {
12461 INT16 x, y, z; // Want to be sure we can use negative values
12462 statenum_t state;
12463 mobj_t *mo;
12464 INT32 locvar1 = var1;
12465 INT32 locvar2 = var2;
12466
12467 if (LUA_CallAction(A_SPAWNPARTICLERELATIVE, actor))
12468 return;
12469
12470
12471 CONS_Debug(DBG_GAMELOGIC, "A_SpawnParticleRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
12472
12473 x = (INT16)(locvar1>>16);
12474 y = (INT16)(locvar1&65535);
12475 z = (INT16)(locvar2>>16);
12476 state = (statenum_t)(locvar2&65535);
12477
12478 // Spawn objects correctly in reverse gravity.
12479 // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
12480 mo = P_SpawnMobj(actor->x + FixedMul(x<<FRACBITS, actor->scale),
12481 actor->y + FixedMul(y<<FRACBITS, actor->scale),
12482 (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[MT_PARTICLE].height) - FixedMul(z<<FRACBITS, actor->scale)) : (actor->z + FixedMul(z<<FRACBITS, actor->scale)), MT_PARTICLE);
12483
12484 // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
12485 mo->angle = actor->angle;
12486
12487 if (actor->eflags & MFE_VERTICALFLIP)
12488 mo->flags2 |= MF2_OBJECTFLIP;
12489
12490 P_SetMobjState(mo, state);
12491 }
12492
12493 // Function: A_MultiShotDist
12494 //
12495 // Description: Spawns multiple shots based on player proximity
12496 //
12497 // var1 = same as A_MultiShot
12498 // var2 = same as A_MultiShot
12499 //
A_MultiShotDist(mobj_t * actor)12500 void A_MultiShotDist(mobj_t *actor)
12501 {
12502 INT32 locvar1 = var1;
12503 INT32 locvar2 = var2;
12504
12505 if (LUA_CallAction(A_MULTISHOTDIST, actor))
12506 return;
12507
12508 {
12509 UINT8 i;
12510 // Quick! Look through players!
12511 // Don't spawn dust unless a player is relatively close by (var1).
12512 for (i = 0; i < MAXPLAYERS; ++i)
12513 if (playeringame[i] && players[i].mo
12514 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (1600<<FRACBITS))
12515 break; // Stop looking.
12516 if (i == MAXPLAYERS)
12517 return; // don't make bubble!
12518 }
12519
12520 var1 = locvar1;
12521 var2 = locvar2;
12522 A_MultiShot(actor);
12523 }
12524
12525 // Function: A_WhoCaresIfYourSonIsABee
12526 //
12527 // Description: Makes a child object, storing the number of created children in the parent's extravalue1.
12528 //
12529 // var1 = mobjtype of child
12530 // var2 >> 16 = mobjtype of child
12531 // var2 & 65535 = vertical momentum
12532 // var2:
12533 // var2 >> 16 = forward offset
12534 // var2 & 65535 = vertical offset
12535 //
A_WhoCaresIfYourSonIsABee(mobj_t * actor)12536 void A_WhoCaresIfYourSonIsABee(mobj_t *actor)
12537 {
12538 INT32 locvar1 = var1;
12539 INT32 locvar2 = var2;
12540 fixed_t foffsetx;
12541 fixed_t foffsety;
12542 mobj_t *son;
12543
12544 if (LUA_CallAction(A_WHOCARESIFYOURSONISABEE, actor))
12545 return;
12546
12547 A_FaceTarget(actor);
12548
12549 if (actor->extravalue1)
12550 actor->extravalue1--;
12551
12552 if (actor->info->attacksound)
12553 S_StartSound(actor, actor->info->attacksound);
12554
12555 foffsetx = P_ReturnThrustX(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
12556 foffsety = P_ReturnThrustY(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
12557
12558 if (!(son = P_SpawnMobjFromMobj(actor, foffsetx, foffsety, (locvar2&65535)*FRACUNIT, (mobjtype_t)(locvar1 >> 16))))
12559 return;
12560
12561 P_SetObjectMomZ(son, (locvar1 & 65535)<<FRACBITS, true);
12562
12563 P_SetTarget(&son->tracer, actor);
12564 P_SetTarget(&son->target, actor->target);
12565 }
12566
12567 // Function: A_ParentTriesToSleep
12568 //
12569 // Description: If extravalue1 is less than or equal to var1, go to var2.
12570 //
12571 // var1 = state to go to when extravalue1
12572 // var2 = unused
12573 //
A_ParentTriesToSleep(mobj_t * actor)12574 void A_ParentTriesToSleep(mobj_t *actor)
12575 {
12576 INT32 locvar1 = var1;
12577
12578 if (LUA_CallAction(A_PARENTTRIESTOSLEEP, actor))
12579 return;
12580
12581 if (actor->extravalue1)
12582 {
12583 if (actor->info->seesound)
12584 S_StartSound(actor, actor->info->seesound);
12585 actor->reactiontime = 0;
12586 P_SetMobjState(actor, locvar1);
12587 }
12588 else if (!actor->reactiontime)
12589 {
12590 actor->reactiontime = 1;
12591 if (actor->info->activesound) // more like INactivesound doy hoy hoy
12592 S_StartSound(actor, actor->info->activesound);
12593 }
12594 }
12595
12596
12597 // Function: A_CryingToMomma
12598 //
12599 // Description: If you're a child, let your parent know something's happened to you through extravalue1. Also, prepare to die.
12600 //
12601 // var1 = unused
12602 // var2 = unused
12603 //
A_CryingToMomma(mobj_t * actor)12604 void A_CryingToMomma(mobj_t *actor)
12605 {
12606 if (LUA_CallAction(A_CRYINGTOMOMMA, actor))
12607 return;
12608
12609 if (actor->tracer)
12610 actor->tracer->extravalue1++;
12611
12612 actor->momx = actor->momy = actor->momz = 0;
12613
12614 P_UnsetThingPosition(actor);
12615 if (sector_list)
12616 {
12617 P_DelSeclist(sector_list);
12618 sector_list = NULL;
12619 }
12620 actor->flags = MF_NOBLOCKMAP|MF_NOCLIPTHING;
12621 P_SetThingPosition(actor);
12622 }
12623
12624 // Function: A_CheckFlags2
12625 //
12626 // Description: If actor->flags2 & var1, goto var2.
12627 //
12628 // var1 = mask
12629 // var2 = state to go
12630 //
A_CheckFlags2(mobj_t * actor)12631 void A_CheckFlags2(mobj_t *actor)
12632 {
12633 INT32 locvar1 = var1;
12634 INT32 locvar2 = var2;
12635
12636 if (LUA_CallAction(A_CHECKFLAGS2, actor))
12637 return;
12638
12639 if (actor->flags2 & locvar1)
12640 P_SetMobjState(actor, (statenum_t)locvar2);
12641 }
12642
12643 // Function: A_Boss5FindWaypoint
12644 //
12645 // Description: Finds the next waypoint in sequence and sets it as its tracer.
12646 //
12647 // var1 = if 1, always go to ambush-marked waypoint. if 2, go to MT_BOSSFLYPOINT.
12648 // var2 = unused
12649 //
A_Boss5FindWaypoint(mobj_t * actor)12650 void A_Boss5FindWaypoint(mobj_t *actor)
12651 {
12652 INT32 locvar1 = var1;
12653 boolean avoidcenter;
12654 UINT32 i;
12655 UINT8 extrainfo = (actor->spawnpoint ? actor->spawnpoint->extrainfo : 0);
12656
12657 if (LUA_CallAction(A_BOSS5FINDWAYPOINT, actor))
12658 return;
12659
12660 avoidcenter = !actor->tracer || (actor->health == actor->info->damage+1);
12661
12662 if (locvar1 == 2) // look for the boss waypoint
12663 {
12664 thinker_t *th;
12665 mobj_t *mo2;
12666 P_SetTarget(&actor->tracer, NULL);
12667 // Flee! Flee! Find a point to escape to! If none, just shoot upward!
12668 // scan the thinkers to find the runaway point
12669 for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
12670 {
12671 if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
12672 continue;
12673
12674 mo2 = (mobj_t *)th;
12675
12676 if (mo2->type != MT_BOSSFLYPOINT)
12677 continue;
12678
12679 if (mo2->spawnpoint && mo2->spawnpoint->extrainfo != extrainfo)
12680 continue;
12681
12682 // If this one's further then the last one, don't go for it.
12683 if (actor->tracer &&
12684 P_AproxDistance(P_AproxDistance(actor->x - mo2->x, actor->y - mo2->y), actor->z - mo2->z) >
12685 P_AproxDistance(P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y), actor->z - actor->tracer->z))
12686 continue;
12687
12688 // Otherwise... Do!
12689 P_SetTarget(&actor->tracer, mo2);
12690 }
12691 if (!actor->tracer)
12692 return; // no boss flypoints found
12693 }
12694 else if (locvar1 == 1) // always go to ambush-marked waypoint
12695 {
12696 if (avoidcenter)
12697 goto nowaypoints; // if we can't go the center, why on earth are we doing this?
12698
12699 for (i = 0; i < nummapthings; i++)
12700 {
12701 if (!mapthings[i].mobj)
12702 continue;
12703 if (mapthings[i].mobj->type != MT_FANGWAYPOINT)
12704 continue;
12705 if (mapthings[i].extrainfo != extrainfo)
12706 continue;
12707 if (!(mapthings[i].options & MTF_AMBUSH))
12708 continue;
12709
12710 P_SetTarget(&actor->tracer, mapthings[i].mobj);
12711 break;
12712 }
12713
12714 if (i == nummapthings)
12715 goto nowaypoints;
12716 }
12717 else // locvar1 == 0
12718 {
12719 fixed_t hackoffset = P_MobjFlip(actor)*56*FRACUNIT;
12720 INT32 numfangwaypoints = 0;
12721 mobj_t **fangwaypoints;
12722 INT32 key;
12723
12724 actor->z += hackoffset;
12725
12726 // first, count how many waypoints we have
12727 for (i = 0; i < nummapthings; i++)
12728 {
12729 if (!mapthings[i].mobj)
12730 continue;
12731 if (mapthings[i].mobj->type != MT_FANGWAYPOINT)
12732 continue;
12733 if (actor->tracer == mapthings[i].mobj) // this was your tracer last time
12734 continue;
12735 if (mapthings[i].extrainfo != extrainfo)
12736 continue;
12737 if (mapthings[i].options & MTF_AMBUSH)
12738 {
12739 if (avoidcenter)
12740 continue;
12741 }
12742 else if (mapthings[i].mobj->reactiontime > 0)
12743 continue;
12744 if (!P_CheckSight(actor, mapthings[i].mobj))
12745 continue;
12746 numfangwaypoints++;
12747 }
12748
12749 // players also count as waypoints apparently
12750 if (actor->extravalue2 > 1)
12751 {
12752 for (i = 0; i < MAXPLAYERS; i++)
12753 {
12754 if (!playeringame[i])
12755 continue;
12756 if (!players[i].mo)
12757 continue;
12758 if (players[i].spectator)
12759 continue;
12760 if (players[i].mo->health <= 0)
12761 continue;
12762 if (players[i].powers[pw_flashing])
12763 continue;
12764 if (actor->tracer == players[i].mo) // this was your tracer last time
12765 continue;
12766 if (!P_CheckSight(actor, players[i].mo))
12767 continue;
12768 numfangwaypoints++;
12769 }
12770 }
12771
12772 if (!numfangwaypoints)
12773 {
12774 // restore z position
12775 actor->z -= hackoffset;
12776 goto nowaypoints; // no waypoints :(
12777 }
12778
12779 // allocate the table and reset count to zero
12780 fangwaypoints = Z_Calloc(sizeof(*waypoints)*numfangwaypoints, PU_STATIC, NULL);
12781 numfangwaypoints = 0;
12782
12783 // now find them again and add them to the table!
12784 for (i = 0; i < nummapthings; i++)
12785 {
12786 if (!mapthings[i].mobj)
12787 continue;
12788 if (mapthings[i].mobj->type != MT_FANGWAYPOINT)
12789 continue;
12790 if (actor->tracer == mapthings[i].mobj) // this was your tracer last time
12791 continue;
12792 if (mapthings[i].extrainfo != extrainfo)
12793 continue;
12794 if (mapthings[i].options & MTF_AMBUSH)
12795 {
12796 if (avoidcenter)
12797 continue;
12798 }
12799 else if (mapthings[i].mobj->reactiontime > 0)
12800 {
12801 mapthings[i].mobj->reactiontime--;
12802 continue;
12803 }
12804 if (!P_CheckSight(actor, mapthings[i].mobj))
12805 continue;
12806 fangwaypoints[numfangwaypoints++] = mapthings[i].mobj;
12807 }
12808
12809 if (actor->extravalue2 > 1)
12810 {
12811 for (i = 0; i < MAXPLAYERS; i++)
12812 {
12813 if (!playeringame[i])
12814 continue;
12815 if (!players[i].mo)
12816 continue;
12817 if (players[i].spectator)
12818 continue;
12819 if (players[i].mo->health <= 0)
12820 continue;
12821 if (players[i].powers[pw_flashing])
12822 continue;
12823 if (actor->tracer == players[i].mo) // this was your tracer last time
12824 continue;
12825 if (!P_CheckSight(actor, players[i].mo))
12826 continue;
12827 fangwaypoints[numfangwaypoints++] = players[i].mo;
12828 }
12829 }
12830
12831 // restore z position
12832 actor->z -= hackoffset;
12833
12834 if (!numfangwaypoints)
12835 {
12836 Z_Free(fangwaypoints); // free table
12837 goto nowaypoints; // ???
12838 }
12839
12840 key = P_RandomKey(numfangwaypoints);
12841
12842 P_SetTarget(&actor->tracer, fangwaypoints[key]);
12843 if (actor->tracer->type == MT_FANGWAYPOINT)
12844 actor->tracer->reactiontime = numfangwaypoints/4; // Monster Iestyn: is this how it should be? I count center waypoints as waypoints unlike the original Lua script
12845 Z_Free(fangwaypoints); // free table
12846 }
12847
12848 // now face the tracer you just set!
12849 A_FaceTracer(actor);
12850 return;
12851
12852 nowaypoints:
12853 // no waypoints at all, guess the mobj has to disappear
12854 if (actor->health)
12855 P_KillMobj(actor, NULL, NULL, 0);
12856 else
12857 P_RemoveMobj(actor);
12858 return;
12859 }
12860
12861 // Function: A_DoNPCSkid
12862 //
12863 // Description: Something that looks like a player is skidding.
12864 //
12865 // var1 = state to change to upon being slow enough
12866 // var2 = minimum speed
12867 //
A_DoNPCSkid(mobj_t * actor)12868 void A_DoNPCSkid(mobj_t *actor)
12869 {
12870 INT32 locvar1 = var1;
12871 INT32 locvar2 = var2;
12872 fixed_t x, y, z;
12873
12874 if (LUA_CallAction(A_DONPCSKID, actor))
12875 return;
12876
12877 x = actor->x;
12878 y = actor->y;
12879 z = actor->z;
12880
12881 if (!locvar2)
12882 locvar2 = FRACUNIT/2;
12883
12884 if ((FixedHypot(actor->momx, actor->momy) < locvar2)
12885 || !P_TryMove(actor, actor->x + actor->momx, actor->y + actor->momy, false))
12886 {
12887 actor->momx = actor->momy = 0;
12888 P_SetMobjState(actor, locvar1);
12889 return;
12890 }
12891 else
12892 {
12893 actor->momx = (2*actor->momx)/3;
12894 actor->momy = (2*actor->momy)/3;
12895 }
12896
12897 P_TeleportMove(actor, x, y, z);
12898
12899 // Spawn a particle every 3 tics.
12900 if (!(leveltime % 3))
12901 {
12902 mobj_t *particle = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_SPINDUST);
12903 particle->tics = 10;
12904
12905 P_SetScale(particle, 2*actor->scale/3);
12906 particle->destscale = actor->scale;
12907 P_SetObjectMomZ(particle, FRACUNIT, false);
12908 }
12909 }
12910
12911 // Function: A_DoNPCPain
12912 //
12913 // Description: Something that looks like a player was hit, put them in pain.
12914 //
12915 // var1 = If zero, always fling the same amount.
12916 // Otherwise, slowly reduce the vertical
12917 // and horizontal speed to the base value
12918 // multiplied by this the more damage is done.
12919 // var2 = If zero, use default fling values.
12920 // Otherwise, vertical and horizontal speed
12921 // will be multiplied by this.
12922 //
A_DoNPCPain(mobj_t * actor)12923 void A_DoNPCPain(mobj_t *actor)
12924 {
12925 INT32 locvar1 = var1;
12926 INT32 locvar2 = var2;
12927 fixed_t vspeed = 0;
12928 fixed_t hspeed = FixedMul(4*FRACUNIT, actor->scale);
12929
12930 if (LUA_CallAction(A_DONPCPAIN, actor))
12931 return;
12932
12933 actor->flags &= ~(MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT);
12934
12935 var1 = var2 = 0;
12936 A_Pain(actor);
12937
12938 actor->z += P_MobjFlip(actor);
12939
12940 if (actor->eflags & MFE_UNDERWATER)
12941 vspeed = FixedDiv(10511*FRACUNIT,2600*FRACUNIT);
12942 else
12943 vspeed = FixedDiv(69*FRACUNIT,10*FRACUNIT);
12944
12945 if (actor->target)
12946 actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + actor->target->momx, actor->target->y + actor->target->momy);
12947
12948 if (locvar1)
12949 {
12950 if (!actor->info->spawnhealth)
12951 return; // there's something very wrong here if you're using this action on something with no starting health
12952 locvar1 += ((FRACUNIT - locvar1)/actor->info->spawnhealth)*actor->health;
12953 hspeed = FixedMul(hspeed, locvar1);
12954 vspeed = FixedMul(vspeed, locvar1);
12955 }
12956
12957 if (locvar2)
12958 {
12959 hspeed = FixedMul(hspeed, locvar2);
12960 vspeed = FixedMul(vspeed, locvar2);
12961 }
12962
12963 P_SetObjectMomZ(actor, vspeed, false);
12964 P_InstaThrust(actor, actor->angle, -hspeed);
12965 }
12966
12967 // Function: A_PrepareRepeat
12968 //
12969 // Description: Simple way to prepare A_Repeat.
12970 //
12971 // var1 = value to set extravalue2 to
12972 // var2 = unused
12973 //
A_PrepareRepeat(mobj_t * actor)12974 void A_PrepareRepeat(mobj_t *actor)
12975 {
12976 INT32 locvar1 = var1;
12977
12978 if (LUA_CallAction(A_PREPAREREPEAT, actor))
12979 return;
12980
12981 actor->extravalue2 = locvar1;
12982 }
12983
12984 // Function: A_Boss5ExtraRepeat
12985 //
12986 // Description: Simple way to prepare A_Repeat.
12987 //
12988 // var1 = maximum value to setextravalue2 to (normally)
12989 // var2 = pinch annoyance
12990 //
A_Boss5ExtraRepeat(mobj_t * actor)12991 void A_Boss5ExtraRepeat(mobj_t *actor)
12992 {
12993 INT32 locvar1 = var1;
12994 INT32 locvar2 = var2;
12995 INT32 calc;
12996 INT32 locspawn;
12997 INT32 lochealth;
12998
12999 if (LUA_CallAction(A_BOSS5EXTRAREPEAT, actor))
13000 return;
13001
13002 if (actor->extravalue2 > 0 && !(actor->flags2 & MF2_FRET))
13003 return;
13004
13005 locspawn = actor->info->spawnhealth - actor->info->damage;
13006 lochealth = actor->health - actor->info->damage;
13007
13008 if (locspawn <= 0 || lochealth <= 0)
13009 calc = locvar1;
13010 else
13011 calc = (locvar1*(locspawn - lochealth))/locspawn;
13012
13013 if (calc > 2)
13014 actor->extravalue2 = 1 + calc/2 + P_RandomKey(calc/2);
13015 else
13016 actor->extravalue2 = 1 + calc;
13017
13018 if (lochealth <= 0)
13019 actor->extravalue2 += locvar2;
13020 }
13021
13022 // Function: A_Boss5Calm
13023 //
13024 // Description: Simple way to disable MF2_FRET (and enable MF_SHOOTABLE the first time it's called)
13025 //
13026 // var1 = unused
13027 // var2 = unused
13028 //
A_Boss5Calm(mobj_t * actor)13029 void A_Boss5Calm(mobj_t *actor)
13030 {
13031 if (LUA_CallAction(A_BOSS5CALM, actor))
13032 return;
13033
13034 actor->flags |= MF_SHOOTABLE;
13035 actor->flags2 &= ~MF2_FRET;
13036 }
13037
13038 // Function: A_Boss5CheckOnGround
13039 //
13040 // Description: Ground checker.
13041 //
13042 // var1 = state to change to upon hitting ground.
13043 // var2 = state to change to upon hitting ground if health == pinchhealth, assuming it exists
13044 //
A_Boss5CheckOnGround(mobj_t * actor)13045 void A_Boss5CheckOnGround(mobj_t *actor)
13046 {
13047 INT32 locvar1 = var1;
13048 INT32 locvar2 = var2;
13049
13050 if (LUA_CallAction(A_BOSS5CHECKONGROUND, actor))
13051 return;
13052
13053 if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
13054 || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz))
13055 {
13056 if (locvar2 && (!actor->health || (actor->health == actor->info->damage && !(actor->flags2 & MF2_STRONGBOX))))
13057 P_SetMobjState(actor, locvar2);
13058 else
13059 P_SetMobjState(actor, locvar1);
13060 }
13061
13062 if (actor->tracer && P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y) < 2*actor->radius)
13063 {
13064 actor->momx = (4*actor->momx)/5;
13065 actor->momy = (4*actor->momy)/5;
13066 }
13067 }
13068
13069 // Function: A_Boss5CheckFalling
13070 //
13071 // Description: Falling checker.
13072 //
13073 // var1 = state to change to when hitting ground.
13074 // var2 = state to change to when falling.
13075 //
A_Boss5CheckFalling(mobj_t * actor)13076 void A_Boss5CheckFalling(mobj_t *actor)
13077 {
13078 INT32 locvar1 = var1;
13079 INT32 locvar2 = var2;
13080
13081 if (LUA_CallAction(A_BOSS5CHECKFALLING, actor))
13082 return;
13083
13084 if (actor->health && actor->extravalue2 > 1)
13085 {
13086 var1 = locvar1;
13087 var2 = 0;
13088 A_Boss5CheckOnGround(actor);
13089 return;
13090 }
13091
13092 if (P_MobjFlip(actor)*actor->momz <= 0)
13093 P_SetMobjState(actor, locvar2);
13094 }
13095
13096 // Function: A_Boss5PinchShot
13097 //
13098 // Description: Fires a missile directly upwards if in pinch.
13099 //
13100 // var1 = object # to shoot
13101 // var2 = height offset (from default of +48 FU)
13102 //
A_Boss5PinchShot(mobj_t * actor)13103 void A_Boss5PinchShot(mobj_t *actor)
13104 {
13105 INT32 locvar1 = var1;
13106 INT32 locvar2 = var2;
13107 fixed_t zoffset;
13108 mobj_t *missile;
13109
13110 if (LUA_CallAction(A_BOSS5PINCHSHOT, actor))
13111 return;
13112
13113 if (actor->health > actor->info->damage)
13114 return;
13115
13116 if (actor->eflags & MFE_VERTICALFLIP)
13117 zoffset = actor->z + actor->height - FixedMul((48 + locvar2)*FRACUNIT, actor->scale);
13118 else
13119 zoffset = actor->z + FixedMul((48 + locvar2)*FRACUNIT, actor->scale);
13120
13121 missile = P_SpawnPointMissile(actor, actor->x, actor->y, zoffset, locvar1,
13122 actor->x, actor->y, zoffset);
13123
13124 if (!missile)
13125 return;
13126
13127 missile->momx = missile->momy = 0;
13128 missile->momz = P_MobjFlip(actor)*missile->info->speed/2;
13129 }
13130
13131 // Function: A_Boss5MakeItRain
13132 //
13133 // Description: Pinch crisis.
13134 //
13135 // var1 = object # to shoot
13136 // var2 = height offset (from default of +48 FU)
13137 //
A_Boss5MakeItRain(mobj_t * actor)13138 void A_Boss5MakeItRain(mobj_t *actor)
13139 {
13140 INT32 locvar1 = var1;
13141 INT32 locvar2 = var2;
13142 INT32 offset = (48 + locvar2)<<16; // upper 16 bits, not fixed_t!
13143 INT32 i;
13144
13145 if (LUA_CallAction(A_BOSS5MAKEITRAIN, actor))
13146 return;
13147
13148 actor->flags2 |= MF2_STRONGBOX;
13149
13150 var1 = locvar1;
13151 var2 = offset + 90;
13152 A_TrapShot(actor);
13153
13154 for (i = 0; i < 8; i++)
13155 {
13156 actor->angle += ANGLE_45;
13157
13158 var1 = locvar1;
13159 var2 = offset + (i & 1) ? 80 : 85;
13160 A_TrapShot(actor);
13161 }
13162
13163 actor->extravalue2 = 0;
13164 }
13165
13166 // Function: A_Boss5MakeJunk
13167 //
13168 // Description: Make a mess.
13169 //
13170 // var1 = state # to set on MT_BROKENROBOT (if 0 do nothing, if -1 go to if colorized)
13171 // var2 = mode (-1 = spin, 0 = make 1, & 1 make 8, & 2 alart mode)
13172 //
A_Boss5MakeJunk(mobj_t * actor)13173 void A_Boss5MakeJunk(mobj_t *actor)
13174 {
13175 INT32 locvar1 = var1;
13176 INT32 locvar2 = var2;
13177 mobj_t *broked = NULL;
13178 angle_t ang;
13179 INT32 i = ((locvar2 & 1) ? 8 : 1);
13180
13181 if (LUA_CallAction(A_BOSS5MAKEJUNK, actor))
13182 return;
13183
13184 if (locvar1 < 0 && (actor->flags2 & MF2_SLIDEPUSH)) // this entire action is a hack, don't judge me
13185 {
13186 INT32 curextravalue2 = actor->extravalue2;
13187 P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_PROJECTORLIGHT);
13188 actor->z += P_MobjFlip(actor)*actor->height;
13189 actor->flags |= MF_NOGRAVITY;
13190 S_StartSound(actor, sfx_vwre);
13191 actor->extravalue2 = 49;
13192 P_SetMobjState(actor, -locvar1);
13193 actor->extravalue2 = curextravalue2;
13194 actor->angle -= FixedAngle((49*45)<<FRACBITS);
13195 return;
13196 }
13197
13198 if (locvar2 == -1)
13199 {
13200 INT32 trans = (10*actor->extravalue2)/50;
13201 if (trans > 9)
13202 trans = 9;
13203 if (trans < 0)
13204 trans = 0;
13205 if (!(actor->extravalue2 & 1))
13206 {
13207 if (actor->extravalue2 > 10)
13208 {
13209 mobj_t *front = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_VWREF);
13210 broked = P_SpawnMobjFromMobj(front, 0, 0, 0, MT_VWREB);
13211 front->z = broked->z = front->z - broked->height;
13212 P_SetObjectMomZ(front, (4<<FRACBITS), false);
13213 broked->momz = front->momz;
13214 broked->fuse = front->fuse = (actor->height+(2*front->height))/front->momz;
13215 }
13216 if (!(actor->colorized = !actor->colorized))
13217 actor->frame |= FF_FULLBRIGHT;
13218 }
13219 actor->angle += ANGLE_45;
13220 actor->frame = (actor->frame & ~FF_TRANSMASK)|(trans<<FF_TRANSSHIFT);
13221 return;
13222 }
13223
13224 ang = FixedAngle((P_RandomKey(36)*10)<<FRACBITS);
13225 while (i--)
13226 {
13227 broked = P_SpawnMobjFromMobj(actor, 0, 0, FRACUNIT, MT_BROKENROBOT);
13228 if (locvar2 & 2)
13229 broked->fuse = TICRATE;
13230 else
13231 broked->fuse = (((locvar2 & 1) ? 4 : 2)*TICRATE)/3;
13232 broked->angle = ang;
13233 P_InstaThrust(broked, ang, ((locvar2 & 2) ? 8 : 5)*actor->scale);
13234 P_SetObjectMomZ(broked, (((locvar2) ? 4 : 0) + P_RandomRange(2, 5))<<FRACBITS, false);
13235 if (locvar1 > 0)
13236 P_SetMobjState(broked, locvar1);
13237 if (!P_MobjWasRemoved(broked))
13238 P_TeleportMove(broked, broked->x + broked->momx, broked->y + broked->momy, broked->z);
13239 ang += ANGLE_45;
13240 }
13241
13242 if (locvar2 & 2)
13243 {
13244 broked = P_SpawnMobjFromMobj(actor, 0, 0, 64<<FRACBITS, MT_GHOST);
13245 S_StartSound(broked, sfx_alart);
13246 broked->fuse = states[S_FANG_INTRO12].tics+10;
13247 P_SetMobjState(broked, S_ALART1);
13248 }
13249 else if (locvar2 & 1)
13250 {
13251 broked->z += broked->momz;
13252 S_StartSound(actor, sfx_s3kccs);
13253 actor->flags &= ~MF_NOCLIPTHING;
13254 }
13255 else
13256 S_StartSound(actor, sfx_s3kd3s);
13257 }
13258
13259 // Function: A_LookForBetter
13260 //
13261 // Description: A_Look, except it finds a better target in multiplayer, and doesn't lose the target in singleplayer.
13262 //
13263 // var1 lower 16 bits = 0 - looks only in front, 1 - looks all around
13264 // var1 upper 16 bits = distance limit
13265 // var2 = unused
13266 //
A_LookForBetter(mobj_t * actor)13267 void A_LookForBetter(mobj_t *actor)
13268 {
13269 INT32 locvar1 = var1;
13270
13271 if (LUA_CallAction(A_LOOKFORBETTER, actor))
13272 return;
13273
13274 P_LookForPlayers(actor, (locvar1 & 65535), false, FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale));
13275 A_FaceTarget(actor);
13276 }
13277
13278 /* * Spawns a dust ring.
13279 * The dust ring behaves slightly randomly so it doesn't look too uniform.
13280 *
13281 * \param mobjtype Thing type to make a ring of.
13282 * \param div Amount of things to spawn on the ring.
13283 * \param x Center X coordinates.
13284 * \param y Center Y coordinates.
13285 * \param z Center Z coordinates.
13286 * \param radius Radius.
13287 * \param speed Additional thrust on particles.
13288 * \param initscale Initial scale when spawning.
13289 * \param scale "Default" scale.
13290 */
P_DustRing(mobjtype_t mobjtype,UINT32 div,fixed_t x,fixed_t y,fixed_t z,fixed_t radius,fixed_t speed,fixed_t initscale,fixed_t scale)13291 static void P_DustRing(mobjtype_t mobjtype, UINT32 div, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t speed, fixed_t initscale, fixed_t scale)
13292 {
13293 angle_t ang = FixedAngle(FixedDiv(360*FRACUNIT, div*FRACUNIT)); //(ANGLE_180/div)*2;
13294 UINT32 i;
13295
13296 // it turned out the radius was effectively nullified thanks to errors in the original script
13297 // BUT people preferred how it looked before I "fixed" it, so I got rid of the radius calculations altogether
13298 // this was a bit of a mess to sort out, but at least it's probably somewhat fine now?
13299 // -- Monster Iestyn (21/05/19)
13300 (void)radius;
13301
13302 for (i = 0; i < div; i++)
13303 {
13304 mobj_t *dust = P_SpawnMobj(
13305 x, //+ FixedMul(radius, FINECOSINE((ang*i) >> ANGLETOFINESHIFT)),
13306 y, //+ FixedMul(radius, FINESINE((ang*i) >> ANGLETOFINESHIFT)),
13307 z,
13308 mobjtype
13309 );
13310
13311 dust->angle = ang*i + ANGLE_90;
13312 P_SetScale(dust, FixedMul(initscale, scale));
13313 dust->destscale = FixedMul(4*FRACUNIT + P_RandomFixed(), scale);
13314 dust->scalespeed = scale/24;
13315 P_Thrust(dust, ang*i, speed + FixedMul(P_RandomFixed(), scale));
13316 dust->momz = P_SignedRandom()*scale/64;
13317 }
13318 }
13319
13320 // Function: A_Boss5BombExplode
13321 //
13322 // Description: Boss 5's bomb exploding.
13323 //
13324 // var1 = Thing type to spawn as dust
13325 // var2 = unused
13326 //
A_Boss5BombExplode(mobj_t * actor)13327 void A_Boss5BombExplode(mobj_t *actor)
13328 {
13329 INT32 locvar1 = var1;
13330
13331 if (LUA_CallAction(A_BOSS5BOMBEXPLODE, actor))
13332 return;
13333
13334 // The original Lua script did not use |= to add flags but just set these flags exactly apparently?
13335 // (I may modify this later)
13336 // -- Monster Iestyn (21/05/19)
13337 actor->flags = MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP;
13338 actor->flags2 = MF2_EXPLOSION;
13339
13340 if (actor->target)
13341 P_RadiusAttack(actor, actor->target, 7*actor->radius, 0, true);
13342
13343 P_DustRing(locvar1, 4, actor->x, actor->y, actor->z+actor->height, 2*actor->radius, 0, FRACUNIT, actor->scale);
13344 P_DustRing(locvar1, 6, actor->x, actor->y, actor->z+actor->height/2, 3*actor->radius, FRACUNIT, FRACUNIT, actor->scale);
13345 //P_StartQuake(9*actor->scale, TICRATE/6, {actor->x, actor->y, actor->z}, 20*actor->radius);
13346 // the above does not exist, so we set the quake values directly instead
13347 quake.intensity = 9*actor->scale;
13348 quake.time = TICRATE/6;
13349 // the following quake values have no effect atm? ah well, may as well set them anyway
13350 {
13351 mappoint_t q_epicenter = {actor->x, actor->y, actor->z};
13352 quake.epicenter = &q_epicenter;
13353 }
13354 quake.radius = 20*actor->radius;
13355 }
13356
13357 static mobj_t *dustdevil;
13358
PIT_DustDevilLaunch(mobj_t * thing)13359 static boolean PIT_DustDevilLaunch(mobj_t *thing)
13360 {
13361 player_t *player = thing->player;
13362
13363 if (!player)
13364 return true;
13365
13366 if (player->powers[pw_carry] != CR_DUSTDEVIL && (player->powers[pw_ignorelatch] & (1<<15)))
13367 return true;
13368
13369 if (abs(thing->x - dustdevil->x) > dustdevil->radius || abs(thing->y - dustdevil->y) > dustdevil->radius)
13370 return true;
13371
13372 if (thing->z + thing->height >= dustdevil->z && dustdevil->z + dustdevil->height >= thing->z) {
13373 fixed_t pos = thing->z - dustdevil->z;
13374 fixed_t thrust = max(FixedDiv(pos, dustdevil->height) * 20, 8 * FRACUNIT);
13375 angle_t fa = R_PointToAngle2(thing->x, thing->y, dustdevil->x, dustdevil->y) >> ANGLETOFINESHIFT;
13376 fixed_t c = FINECOSINE(fa);
13377 fixed_t s = FINESINE(fa);
13378 fixed_t thresh = dustdevil->scale * 20;
13379
13380 //Player in the swirl part.
13381 if (dustdevil->height - pos > thresh)
13382 {
13383 fixed_t dist = FixedHypot(thing->x - dustdevil->x, thing->y - dustdevil->y);
13384 fixed_t dragamount = player->speed;
13385 fixed_t x, y;
13386
13387 if (player->powers[pw_nocontrol] == 0)
13388 {
13389 P_ResetPlayer(player);
13390 A_PlayActiveSound(dustdevil);
13391 }
13392 player->powers[pw_carry] = CR_DUSTDEVIL;
13393 player->powers[pw_nocontrol] = 2;
13394 P_SetTarget(&thing->tracer, dustdevil);
13395 P_SetPlayerMobjState(thing, S_PLAY_PAIN);
13396
13397 if (dist > dragamount)
13398 {
13399 x = thing->x + FixedMul(c, dragamount);
13400 y = thing->y + FixedMul(s, dragamount);
13401 }
13402 else
13403 {
13404 x = dustdevil->x;
13405 y = dustdevil->y;
13406 }
13407 P_TryMove(thing, x - thing->momx, y - thing->momy, true);
13408 }
13409 else
13410 { //Player on the top of the tornado.
13411 P_ResetPlayer(player);
13412 thing->z = dustdevil->z + dustdevil->height;
13413 thrust = 20 * FRACUNIT;
13414 player->powers[pw_carry] = CR_NONE;
13415 player->powers[pw_nocontrol] = 0;
13416 P_SetTarget(&thing->tracer, NULL);
13417 S_StartSound(thing, sfx_wdjump);
13418 P_SetPlayerMobjState(thing, S_PLAY_FALL);
13419 }
13420
13421 thing->momz = thrust;
13422 }
13423
13424 return true;
13425 }
13426
13427 // Function: A_DustDevilThink
13428 //
13429 // Description: Thinker for the dust devil.
13430 //
13431 // var1 = unused
13432 // var2 = unused
13433 //
A_DustDevilThink(mobj_t * actor)13434 void A_DustDevilThink(mobj_t *actor)
13435 {
13436 fixed_t scale = actor->scale;
13437 mobj_t *layer = actor->tracer;
13438 INT32 bx, by, xl, xh, yl, yh;
13439 fixed_t radius = actor->radius;
13440
13441 if (LUA_CallAction(A_DUSTDEVILTHINK, actor))
13442 return;
13443
13444 //Chained thinker for the spiralling dust column.
13445 while (layer && !P_MobjWasRemoved(layer)) {
13446 angle_t fa = layer->angle >> ANGLETOFINESHIFT;
13447 P_TeleportMove(layer, layer->x + 5 * FixedMul(scale, FINECOSINE(fa)), layer->y + 5 * FixedMul(scale, FINESINE(fa)), layer->z);
13448 layer->scale = scale;
13449 layer->angle += ANG10 / 2;
13450 layer->momx = actor->momx;
13451 layer->momy = actor->momy;
13452 layer = layer->tracer;
13453 }
13454
13455 //Spawn random dust around the column on the base.
13456 if (P_IsObjectOnGround(actor)) {
13457 angle_t dustang = ((P_RandomRange(0, 7)*ANGLE_45)>>ANGLETOFINESHIFT) & FINEMASK;
13458 mobj_t *dust = P_SpawnMobj(actor->x + 96 * FixedMul(scale, FINECOSINE(dustang)), actor->y + 96 * FixedMul(scale, FINESINE(dustang)), actor->z, MT_ARIDDUST);
13459 P_SetMobjState(dust, dust->info->spawnstate + P_RandomRange(0, 2));
13460 dust->destscale = scale * 3;
13461 P_SetScale(dust, dust->destscale);
13462 }
13463
13464 actor->extravalue1++;
13465 if (actor->extravalue1 == 12) {
13466 size_t i = 0;
13467 actor->extravalue1 = 0;
13468
13469 //Create a set of items for the rising dust column
13470 for (; i <= 3; i++) {
13471 fixed_t fa = (ANGLE_90*i) >> ANGLETOFINESHIFT;
13472 fixed_t px = actor->x + 70 * FixedMul(scale, FINECOSINE(fa));
13473 fixed_t py = actor->y + 70 * FixedMul(scale, FINESINE(fa));
13474 fixed_t pz = actor->z;
13475
13476 layer = P_SpawnMobj(px, py, pz, MT_DUSTLAYER);
13477 layer->momz = 5 * scale;
13478 layer->angle = ANGLE_90 + ANGLE_90*i;
13479 layer->extravalue1 = TICRATE * 3;
13480
13481 //Chain them
13482 P_SetTarget(&layer->tracer, actor->tracer);
13483 P_SetTarget(&actor->tracer, layer);
13484 }
13485 }
13486
13487 //The physics are handled here.
13488 yh = (unsigned)(actor->y + radius - bmaporgy) >> MAPBLOCKSHIFT;
13489 yl = (unsigned)(actor->y - radius - bmaporgy) >> MAPBLOCKSHIFT;
13490 xh = (unsigned)(actor->x + radius - bmaporgx) >> MAPBLOCKSHIFT;
13491 xl = (unsigned)(actor->x - radius - bmaporgx) >> MAPBLOCKSHIFT;
13492
13493 BMBOUNDFIX(xl, xh, yl, yh);
13494
13495 dustdevil = actor;
13496
13497 for (bx = xl; bx <= xh; bx++)
13498 for (by = yl; by <= yh; by++)
13499 P_BlockThingsIterator(bx, by, PIT_DustDevilLaunch);
13500
13501 //Whirlwind sound effect.
13502 if (leveltime % 70 == 0)
13503 S_StartSound(actor, sfx_s3kcel);
13504 }
13505
13506 // stuff used by A_TNTExplode
13507 static mobj_t *barrel;
13508 static fixed_t exploderadius;
13509 static fixed_t explodethrust;
13510
PIT_TNTExplode(mobj_t * nearby)13511 static boolean PIT_TNTExplode(mobj_t *nearby)
13512 {
13513 fixed_t dx, dy, dz;
13514 fixed_t dm;
13515
13516 if (nearby == barrel)
13517 return true;
13518
13519 dx = nearby->x - barrel->x;
13520 dy = nearby->y - barrel->y;
13521 dz = nearby->z - barrel->z + (nearby->height - barrel->height/2)/2;
13522 dm = P_AproxDistance(P_AproxDistance(dx, dy), dz);
13523
13524 if (dm >= exploderadius || !P_CheckSight(barrel, nearby)) // out of range or not visible
13525 return true;
13526
13527 if (barrel->type == nearby->type) // nearby is also a barrel
13528 {
13529 if (nearby->state == &states[nearby->info->spawnstate])
13530 {
13531 if (barrel->info->attacksound)
13532 S_StartSound(nearby, barrel->info->attacksound);
13533 nearby->momx = FixedMul(FixedDiv(dx, dm), explodethrust);
13534 nearby->momy = FixedMul(FixedDiv(dy, dm), explodethrust);
13535 nearby->momz = FixedMul(FixedDiv(dz, dm), explodethrust);
13536 P_UnsetThingPosition(nearby);
13537 if (sector_list)
13538 {
13539 P_DelSeclist(sector_list);
13540 sector_list = NULL;
13541 }
13542 nearby->flags = MF_NOBLOCKMAP|MF_MISSILE;
13543 P_SetThingPosition(nearby);
13544 P_SetMobjState(nearby, nearby->info->missilestate);
13545 }
13546 }
13547 else
13548 {
13549 if (barrel->target == nearby)
13550 {
13551 mobj_t *tar = barrel->target; // temporarily store barrel's target
13552 P_SetTarget(&barrel->target, NULL);
13553 P_DamageMobj(nearby, barrel, NULL, 1, 0);
13554 if (!P_MobjWasRemoved(barrel))
13555 P_SetTarget(&barrel->target, tar);
13556 }
13557 else
13558 {
13559 P_DamageMobj(nearby,
13560 (barrel->target) ? barrel->target : barrel,
13561 (barrel->target) ? barrel->target : barrel,
13562 1, 0);
13563 }
13564 }
13565
13566 return true;
13567 }
13568
13569 // Function: A_TNTExplode
13570 //
13571 // Description: Creates a TNT explosion. Used by TNT barrels and the like.
13572 //
13573 // var1 = Thing type to spawn as dust.
13574 // var2 = unused
13575 //
A_TNTExplode(mobj_t * actor)13576 void A_TNTExplode(mobj_t *actor)
13577 {
13578 INT32 locvar1 = var1;
13579 INT32 x, y;
13580 INT32 xl, xh, yl, yh;
13581 static mappoint_t epicenter = {0,0,0};
13582
13583 if (LUA_CallAction(A_TNTEXPLODE, actor))
13584 return;
13585
13586 if (actor->tracer)
13587 {
13588 P_SetTarget(&actor->tracer->tracer, NULL);
13589 P_SetTarget(&actor->tracer, NULL);
13590 }
13591
13592 P_UnsetThingPosition(actor);
13593 if (sector_list)
13594 {
13595 P_DelSeclist(sector_list);
13596 sector_list = NULL;
13597 }
13598 actor->flags = MF_NOCLIP|MF_NOGRAVITY|MF_NOBLOCKMAP;
13599 P_SetThingPosition(actor);
13600 actor->flags2 = MF2_EXPLOSION;
13601 if (actor->info->deathsound)
13602 S_StartSound(actor, actor->info->deathsound);
13603
13604 explodethrust = 32*FRACUNIT;
13605 exploderadius = 256*FRACUNIT;
13606
13607 xl = (unsigned)(actor->x - exploderadius - bmaporgx)>>MAPBLOCKSHIFT;
13608 xh = (unsigned)(actor->x + exploderadius - bmaporgx)>>MAPBLOCKSHIFT;
13609 yl = (unsigned)(actor->y - exploderadius - bmaporgy)>>MAPBLOCKSHIFT;
13610 yh = (unsigned)(actor->y + exploderadius - bmaporgy)>>MAPBLOCKSHIFT;
13611
13612 BMBOUNDFIX(xl, xh, yl, yh);
13613
13614 barrel = actor;
13615
13616 for (x = xl; x <= xh; x++)
13617 for (y = yl; y <= yh; y++)
13618 P_BlockThingsIterator(x, y, PIT_TNTExplode);
13619
13620 // cause a quake -- P_StartQuake does not exist yet
13621 epicenter.x = actor->x;
13622 epicenter.y = actor->y;
13623 epicenter.z = actor->z;
13624 quake.intensity = 9*FRACUNIT;
13625 quake.time = TICRATE/6;
13626 quake.epicenter = &epicenter;
13627 quake.radius = 512*FRACUNIT;
13628
13629 if (locvar1)
13630 {
13631 P_DustRing(locvar1, 4, actor->x, actor->y, actor->z+actor->height, 64, 0, FRACUNIT, actor->scale);
13632 P_DustRing(locvar1, 6, actor->x, actor->y, actor->z+actor->height/2, 96, FRACUNIT, FRACUNIT, actor->scale);
13633 }
13634
13635 actor->destscale *= 4;
13636 }
13637
13638 // Function: A_DebrisRandom
13639 //
13640 // Description: Randomizes debris frame and movement.
13641 //
13642 // var1 = Frame range.
13643 // var2 = unused
13644 //
A_DebrisRandom(mobj_t * actor)13645 void A_DebrisRandom(mobj_t *actor)
13646 {
13647 INT32 locvar1 = var1;
13648
13649 if (LUA_CallAction(A_DEBRISRANDOM, actor))
13650 return;
13651
13652 actor->frame |= P_RandomRange(0, locvar1);
13653 var1 = 0;
13654 var2 = 359;
13655 A_ChangeAngleAbsolute(actor);
13656 P_Thrust(actor, actor->angle, FRACUNIT * 2);
13657 }
13658
P_TrainSeg(mobj_t * src,fixed_t x,fixed_t y,fixed_t z,angle_t ang,spritenum_t spr,UINT32 frame)13659 static mobj_t *P_TrainSeg(mobj_t *src, fixed_t x, fixed_t y, fixed_t z, angle_t ang, spritenum_t spr, UINT32 frame)
13660 {
13661 mobj_t *s = P_SpawnMobj(x, y, z, MT_TRAINSEG);
13662 s->fuse = 16*TICRATE;
13663 s->sprite = spr;
13664 s->frame = frame|FF_PAPERSPRITE;
13665 s->angle = ang;
13666 P_Thrust(s, src->angle, 7*FRACUNIT);
13667 return s;
13668 }
13669
13670 // Function: A_TrainCameo
13671 //
13672 // Description: Sets up train cameo locomotive.
13673 //
13674 // var1 = Train width.
13675 // var2 = Train length.
13676 //
A_TrainCameo(mobj_t * actor)13677 void A_TrainCameo(mobj_t *actor)
13678 {
13679 INT32 locvar1 = var1;
13680 INT32 locvar2 = var2;
13681 fixed_t x = actor->x;
13682 fixed_t y = actor->y;
13683 fixed_t z = actor->z;
13684 angle_t ang = actor->angle;
13685 mobj_t *m;
13686 spritenum_t spr = SPR_TRAE;
13687 fixed_t span = locvar1*FRACUNIT;
13688 fixed_t len = locvar2*FRACUNIT;
13689
13690 if (LUA_CallAction(A_TRAINCAMEO, actor))
13691 return;
13692
13693 //Spawn sides.
13694 P_TrainSeg(actor, x, y + span, z, ang, spr, 0);
13695 P_TrainSeg(actor, x, y - span, z, ang, spr, 0);
13696
13697 //Center.
13698 P_TrainSeg(actor, x, y, z, ang, spr, 1);
13699
13700 //Front and back.
13701 P_TrainSeg(actor, x + len, y, z, ang + ANGLE_90, spr, 2);
13702 P_TrainSeg(actor, x - len, y, z, ang + ANGLE_90, spr, 2);
13703
13704 //Smoke spawner.
13705 m = P_TrainSeg(actor, x - (20 * FRACUNIT), y, z + (30 * FRACUNIT), ang + ANGLE_90, spr, 0);
13706 P_SetMobjState(m, S_TRAINPUFFMAKER);
13707 }
13708
13709 // Function: A_TrainCameo2
13710 //
13711 // Description: Sets up train cameo wagon.
13712 //
13713 // var1 = Train width.
13714 // var2 = Train length.
13715 //
A_TrainCameo2(mobj_t * actor)13716 void A_TrainCameo2(mobj_t *actor)
13717 {
13718 INT32 locvar1 = var1;
13719 INT32 locvar2 = var2;
13720 fixed_t x = actor->x;
13721 fixed_t y = actor->y;
13722 fixed_t z = actor->z;
13723 angle_t ang = actor->angle;
13724 spritenum_t spr = SPR_TRAI;
13725 fixed_t span = locvar1*FRACUNIT;
13726 fixed_t len = locvar2*FRACUNIT;
13727
13728 if (LUA_CallAction(A_TRAINCAMEO2, actor))
13729 return;
13730
13731 //Spawn sides.
13732 P_TrainSeg(actor, x, y + span, z, ang, spr, 0);
13733 P_TrainSeg(actor, x, y - span, z, ang, spr, 0);
13734
13735 //Center.
13736 P_TrainSeg(actor, x, y, z, ang, spr, 1);
13737
13738 //Front and back.
13739 P_TrainSeg(actor, x + len, y, z, ang + ANGLE_90, spr, 2);
13740 P_TrainSeg(actor, x - len, y, z, ang + ANGLE_90, spr, 2);
13741 }
13742
13743 // Function: A_CanarivoreGas
13744 //
13745 // Description: Releases gas clouds. Used by the Canarivore.
13746 //
13747 // var1 = Mobj type.
13748 // var2 = Unused
13749 //
A_CanarivoreGas(mobj_t * actor)13750 void A_CanarivoreGas(mobj_t *actor)
13751 {
13752 INT32 locvar1 = var1;
13753
13754 if (LUA_CallAction(A_CANARIVOREGAS, actor))
13755 return;
13756
13757 P_DustRing(locvar1, 4, actor->x, actor->y, actor->z + actor->height / 5, 18, 0, FRACUNIT/10, actor->scale);
13758 P_DustRing(locvar1, 6, actor->x, actor->y, actor->z + actor->height / 5, 28, FRACUNIT, FRACUNIT/10, actor->scale);
13759 }
13760
13761 //
13762 // Function: A_KillSegments
13763 //
13764 // Description: Causes segments attached via tracer chain to be killed.
13765 //
13766 // var1 = Fuse (if 0, default to TICRATE/2).
13767 // var2 = Unused
13768 //
A_KillSegments(mobj_t * actor)13769 void A_KillSegments(mobj_t *actor)
13770 {
13771 INT32 locvar1 = var1;
13772 mobj_t *seg = actor->tracer;
13773 INT32 fuse = locvar1 ? locvar1 : TICRATE/2;
13774
13775 if (LUA_CallAction(A_KILLSEGMENTS, actor))
13776 return;
13777
13778 while (seg)
13779 {
13780 mobj_t *kseg = seg;
13781 seg = seg->tracer;
13782
13783 kseg->flags = MF_NOBLOCKMAP|MF_BOUNCE;
13784 kseg->flags2 = 0;
13785 kseg->fuse = fuse;
13786 P_Thrust(kseg, R_PointToAngle2(actor->x, actor->y, kseg->x, kseg->y), 3*actor->scale);
13787 kseg->momz = 3*actor->scale;
13788 }
13789 }
13790
P_SnapperLegPlace(mobj_t * mo)13791 static void P_SnapperLegPlace(mobj_t *mo)
13792 {
13793 mobj_t *seg = mo->tracer;
13794 angle_t a = mo->angle;
13795 angle_t fa = (a >> ANGLETOFINESHIFT) & FINEMASK;
13796 fixed_t c = FINECOSINE(fa);
13797 fixed_t s = FINESINE(fa);
13798 fixed_t x, y;
13799 INT32 o1, o2;
13800 INT32 woffset = mo->extravalue1;
13801 INT32 side = mo->extravalue2;
13802 INT32 alt;
13803
13804 // Move head first.
13805 fixed_t rad = mo->radius;
13806 INT32 necklen = (32*(mo->info->reactiontime - mo->reactiontime))/mo->info->reactiontime; // Not in FU
13807
13808 seg->z = mo->z + ((mo->eflags & MFE_VERTICALFLIP) ? (((mo->height<<1)/3) - seg->height) : mo->height/3);
13809 P_TryMove(seg, mo->x + FixedMul(c, rad) + necklen*c, mo->y + FixedMul(s, rad) + necklen*s, true);
13810 seg->angle = a;
13811
13812 // Move as many legs as available.
13813 seg = seg->tracer;
13814 do
13815 {
13816 o1 = seg->extravalue1;
13817 o2 = seg->extravalue2;
13818 alt = seg->cusval;
13819
13820 if (alt == 1)
13821 o2 += woffset;
13822 else
13823 o2 -= woffset;
13824
13825 if (alt != side)
13826 {
13827 x = c*o2 + s*o1;
13828 y = s*o2 - c*o1;
13829 seg->z = mo->z + (((mo->eflags & MFE_VERTICALFLIP) ? (mo->height - seg->height) : 0));
13830 P_TryMove(seg, mo->x + x, mo->y + y, true);
13831 P_SetMobjState(seg, seg->info->raisestate);
13832 }
13833 else
13834 P_SetMobjState(seg, seg->info->spawnstate);
13835
13836 seg->angle = R_PointToAngle2(mo->x, mo->y, seg->x, seg->y);
13837
13838 seg = seg->tracer;
13839 } while (seg);
13840 }
13841
13842 //
13843 // Function: A_SnapperSpawn
13844 //
13845 // Description: Sets up Green Snapper legs and head.
13846 //
13847 // var1 = Leg mobj type.
13848 // var2 = Head mobj type.
13849 //
A_SnapperSpawn(mobj_t * actor)13850 void A_SnapperSpawn(mobj_t *actor)
13851 {
13852 mobjtype_t legtype = (mobjtype_t)var1;
13853 mobjtype_t headtype = (mobjtype_t)var2;
13854 mobj_t *ptr = actor;
13855 INT32 i;
13856 mobj_t *seg;
13857
13858 if (LUA_CallAction(A_SNAPPERSPAWN, actor))
13859 return;
13860
13861 // It spawns 1 head.
13862 seg = P_SpawnMobjFromMobj(actor, 0, 0, 0, headtype);
13863 P_SetTarget(&ptr->tracer, seg);
13864 ptr = seg;
13865
13866 // It spawns 4 legs which will be handled in the thinker function.
13867 for (i = 1; i <= 4; i++)
13868 {
13869 seg = P_SpawnMobjFromMobj(actor, 0, 0, 0, legtype);
13870 P_SetTarget(&ptr->tracer, seg);
13871 ptr = seg;
13872
13873 // The legs' base offsets are stored as extravalues, as relative coordinates in xy space.
13874 seg->extravalue1 = 28;
13875 seg->extravalue2 = 28;
13876 if (i % 2)
13877 seg->extravalue1 = -seg->extravalue1;
13878
13879 if ((i/2) % 2)
13880 seg->extravalue2 = -seg->extravalue2;
13881
13882 // Alternating motion stuff.
13883 seg->cusval = ((i + 1)/2) % 2;
13884 }
13885
13886 actor->extravalue1 = 0;
13887 actor->extravalue2 = 0;
13888 P_SnapperLegPlace(actor);
13889 }
13890
13891 //
13892 // Function: A_SnapperThinker
13893 //
13894 // Description: Thinker for Green Snapper.
13895 //
13896 // var1 = Unused
13897 // var2 = Unused
13898 //
A_SnapperThinker(mobj_t * actor)13899 void A_SnapperThinker(mobj_t *actor)
13900 {
13901 fixed_t x0 = actor->x;
13902 fixed_t y0 = actor->y;
13903 fixed_t xs, ys;
13904 fixed_t x1, y1;
13905 fixed_t dist;
13906 boolean chasing;
13907
13908 if (LUA_CallAction(A_SNAPPERTHINKER, actor))
13909 return;
13910
13911 // We make a check just in case there's no spawnpoint.
13912 if (actor->spawnpoint)
13913 {
13914 xs = actor->spawnpoint->x*FRACUNIT;
13915 ys = actor->spawnpoint->y*FRACUNIT;
13916 }
13917 else
13918 {
13919 xs = x0;
13920 ys = y0;
13921 }
13922
13923 // Look for nearby, valid players to chase angrily at.
13924 if ((actor->target || P_LookForPlayers(actor, true, false, 1024*FRACUNIT))
13925 && P_AproxDistance(actor->target->x - xs, actor->target->y - ys) < 2048*FRACUNIT
13926 && abs(actor->target->z - actor->z) < 80*FRACUNIT
13927 && P_CheckSight(actor, actor->target))
13928 {
13929 chasing = true;
13930 x1 = actor->target->x;
13931 y1 = actor->target->y;
13932 }
13933 else
13934 {
13935 chasing = false;
13936 x1 = xs;
13937 y1 = ys;
13938 }
13939
13940 dist = P_AproxDistance(x1 - x0, y1 - y0);
13941
13942 // The snapper either chases what it considers to be a nearby player, or instead decides to go back to its spawnpoint.
13943 if (chasing || dist > 32*FRACUNIT)
13944 {
13945 INT32 speed = actor->info->speed + actor->info->reactiontime - actor->reactiontime;
13946
13947 angle_t maxang = FixedAngle(speed*FRACUNIT/2);
13948 angle_t ang = actor->angle;
13949 angle_t realang = R_PointToAngle2(x0, y0, x1, y1);
13950 angle_t dif = realang - ang;
13951 angle_t fa;
13952 fixed_t c, s;
13953
13954 if (dif < ANGLE_180 && dif > maxang)
13955 actor->angle += maxang;
13956 else if (dif >= ANGLE_180 && dif < InvAngle(maxang))
13957 actor->angle -= maxang;
13958 else
13959 actor->angle = realang;
13960
13961 fa = (actor->angle >> ANGLETOFINESHIFT) & FINEMASK;
13962 c = FINECOSINE(fa);
13963 s = FINESINE(fa);
13964
13965 P_TryMove(actor, actor->x + c*speed, actor->y + s*speed, false);
13966
13967 // The snapper spawns dust if going fast!
13968 if (actor->reactiontime < 4)
13969 {
13970 mobj_t *dust = P_SpawnMobj(x0, y0, actor->z, MT_SPINDUST);
13971 P_Thrust(dust, ang + ANGLE_180 + FixedAngle(P_RandomRange(-20, 20)*FRACUNIT), speed*FRACUNIT);
13972 }
13973
13974 if (actor->extravalue2 == 0)
13975 {
13976 if (actor->extravalue1 > 16)
13977 {
13978 A_PlayActiveSound(actor);
13979 actor->extravalue2 = 1;
13980
13981 // If the snapper is chasing, accelerate; otherwise, decelerate.
13982 if (chasing)
13983 actor->reactiontime = max(0, actor->reactiontime - 1);
13984 else
13985 actor->reactiontime = min(actor->info->reactiontime, actor->reactiontime + 1);
13986 }
13987 else
13988 actor->extravalue1 += speed;
13989 }
13990 else
13991 {
13992 if (actor->extravalue1 < -16)
13993 {
13994 A_PlayActiveSound(actor);
13995 actor->extravalue2 = 0;
13996
13997 // If the snapper is chasing, accelerate; otherwise, decelerate.
13998 if (chasing)
13999 actor->reactiontime = max(0, actor->reactiontime - 1);
14000 else
14001 actor->reactiontime = min(actor->info->reactiontime, actor->reactiontime + 1);
14002 }
14003 else
14004 actor->extravalue1 -= speed;
14005 }
14006 }
14007
14008 P_SnapperLegPlace(actor);
14009 }
14010
14011 // Function: A_SaloonDoorSpawn
14012 //
14013 // Description: Spawns a saloon door.
14014 //
14015 // var1 = mobjtype for sides
14016 // var2 = distance sides should be placed apart
14017 //
A_SaloonDoorSpawn(mobj_t * actor)14018 void A_SaloonDoorSpawn(mobj_t *actor)
14019 {
14020 INT32 locvar1 = var1;
14021 INT32 locvar2 = var2;
14022 angle_t ang = actor->angle;
14023 angle_t fa = (ang >> ANGLETOFINESHIFT) & FINEMASK;
14024 fixed_t c = FINECOSINE(fa)*locvar2;
14025 fixed_t s = FINESINE(fa)*locvar2;
14026 mobj_t *door;
14027 mobjflag2_t ambush = (actor->flags2 & MF2_AMBUSH);
14028
14029 if (LUA_CallAction(A_SALOONDOORSPAWN, actor))
14030 return;
14031
14032 if (!locvar1)
14033 return;
14034
14035 // One door...
14036 if (!(door = P_SpawnMobjFromMobj(actor, c, s, 0, locvar1))) return;
14037 door->angle = ang + ANGLE_180;
14038 door->extravalue1 = AngleFixed(door->angle); // Origin angle
14039 door->extravalue2 = 0; // Angular speed
14040 P_SetTarget(&door->tracer, actor); // Origin door
14041 door->flags2 |= ambush; // Can be opened by normal players?
14042
14043 // ...two door!
14044 if (!(door = P_SpawnMobjFromMobj(actor, -c, -s, 0, locvar1))) return;
14045 door->angle = ang;
14046 door->extravalue1 = AngleFixed(door->angle); // Origin angle
14047 door->extravalue2 = 0; // Angular speed
14048 P_SetTarget(&door->tracer, actor); // Origin door
14049 door->flags2 |= ambush; // Can be opened by normal players?
14050 }
14051
14052 // Function: A_MinecartSparkThink
14053 //
14054 // Description: Thinker for the minecart spark.
14055 //
14056 // var1 = unused
14057 // var2 = unused
14058 //
A_MinecartSparkThink(mobj_t * actor)14059 void A_MinecartSparkThink(mobj_t *actor)
14060 {
14061 fixed_t dx = actor->momx;
14062 fixed_t dy = actor->momy;
14063 fixed_t dz, dm;
14064 UINT8 i;
14065
14066 if (LUA_CallAction(A_MINECARTSPARKTHINK, actor))
14067 return;
14068
14069 if (actor->momz == 0 && P_IsObjectOnGround(actor))
14070 actor->momz = P_RandomRange(2, 4)*FRACUNIT;
14071
14072 dz = actor->momz;
14073 dm = FixedHypot(FixedHypot(dx, dy), dz);
14074 dx = FixedDiv(dx, dm);
14075 dy = FixedDiv(dy, dm);
14076 dz = FixedDiv(dz, dm);
14077
14078 for (i = 1; i <= 8; i++)
14079 {
14080 mobj_t *trail = P_SpawnMobj(actor->x - dx*i, actor->y - dy*i, actor->z - dz*i, MT_PARTICLE);
14081 trail->tics = 2;
14082 trail->sprite = actor->sprite;
14083 P_SetScale(trail, trail->scale/4);
14084 trail->destscale = trail->scale;
14085 }
14086 }
14087
14088 // Function: A_ModuloToState
14089 //
14090 // Description: Modulo operation to state
14091 //
14092 // var1 = Modulo
14093 // var2 = State
14094 //
A_ModuloToState(mobj_t * actor)14095 void A_ModuloToState(mobj_t *actor)
14096 {
14097 INT32 locvar1 = var1;
14098 INT32 locvar2 = var2;
14099
14100 if (LUA_CallAction(A_MODULOTOSTATE, actor))
14101 return;
14102
14103 if ((modulothing % locvar1 == 0))
14104 P_SetMobjState(actor, (locvar2));
14105 modulothing++;
14106 }
14107
14108 // Function: A_LavafallRocks
14109 //
14110 // Description: Spawn random rock particles.
14111 //
14112 // var1 = unused
14113 // var2 = unused
14114 //
A_LavafallRocks(mobj_t * actor)14115 void A_LavafallRocks(mobj_t *actor)
14116 {
14117 UINT8 i;
14118
14119 if (LUA_CallAction(A_LAVAFALLROCKS, actor))
14120 return;
14121
14122 // Don't spawn rocks unless a player is relatively close by.
14123 for (i = 0; i < MAXPLAYERS; ++i)
14124 if (playeringame[i] && players[i].mo
14125 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (actor->info->speed >> 1))
14126 break; // Stop looking.
14127
14128 if (i < MAXPLAYERS)
14129 {
14130 angle_t fa = (FixedAngle(P_RandomKey(360) << FRACBITS) >> ANGLETOFINESHIFT) & FINEMASK;
14131 fixed_t offset = P_RandomRange(4, 12) << FRACBITS;
14132 fixed_t xoffs = FixedMul(FINECOSINE(fa), actor->radius + offset);
14133 fixed_t yoffs = FixedMul(FINESINE(fa), actor->radius + offset);
14134 P_SpawnMobjFromMobj(actor, xoffs, yoffs, 0, MT_LAVAFALLROCK);
14135 }
14136 }
14137
14138 // Function: A_LavafallLava
14139 //
14140 // Description: Spawn lava from lavafall.
14141 //
14142 // var1 = unused
14143 // var2 = unused
14144 //
A_LavafallLava(mobj_t * actor)14145 void A_LavafallLava(mobj_t *actor)
14146 {
14147 mobj_t *lavafall;
14148 UINT8 i;
14149
14150 if (LUA_CallAction(A_LAVAFALLLAVA, actor))
14151 return;
14152
14153 if ((40 - actor->fuse) % (2*(actor->scale >> FRACBITS)))
14154 return;
14155
14156 // Don't spawn lava unless a player is nearby.
14157 for (i = 0; i < MAXPLAYERS; ++i)
14158 if (playeringame[i] && players[i].mo
14159 && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (actor->info->speed))
14160 break; // Stop looking.
14161
14162 if (i >= MAXPLAYERS)
14163 return;
14164
14165 lavafall = P_SpawnMobjFromMobj(actor, 0, 0, -8*FRACUNIT, MT_LAVAFALL_LAVA);
14166 lavafall->momz = -P_MobjFlip(actor)*25*FRACUNIT;
14167 }
14168
14169 // Function: A_FallingLavaCheck
14170 //
14171 // Description: If actor hits the ground or a water surface, enter the death animation.
14172 //
14173 // var1 = unused
14174 // var2 = unused
14175 //
A_FallingLavaCheck(mobj_t * actor)14176 void A_FallingLavaCheck(mobj_t *actor)
14177 {
14178 if (LUA_CallAction(A_FALLINGLAVACHECK, actor))
14179 return;
14180
14181 if (actor->eflags & MFE_TOUCHWATER || P_IsObjectOnGround(actor))
14182 {
14183 actor->flags = MF_NOGRAVITY|MF_NOCLIPTHING;
14184 actor->momz = 0;
14185 if (actor->eflags & MFE_TOUCHWATER)
14186 actor->z = (actor->eflags & MFE_VERTICALFLIP) ? actor->waterbottom : actor->watertop;
14187 P_SetMobjState(actor, actor->info->deathstate);
14188 }
14189 }
14190
14191 // Function: A_FireShrink
14192 //
14193 // Description: Shrink the actor down to the specified scale at the specified speed.
14194 //
14195 // var1 = Scale to shrink to
14196 // var2 = Shrinking speed
14197 //
A_FireShrink(mobj_t * actor)14198 void A_FireShrink(mobj_t *actor)
14199 {
14200 INT32 locvar1 = var1;
14201 INT32 locvar2 = var2;
14202
14203 if (LUA_CallAction(A_FIRESHRINK, actor))
14204 return;
14205
14206 actor->destscale = locvar1;
14207 actor->scalespeed = FRACUNIT/locvar2;
14208 }
14209
14210 // Function: A_SpawnPterabytes
14211 //
14212 // Description: Spawn Pterabytes around the actor in a circle.
14213 //
14214 // var1 = unused
14215 // var2 = unused
14216 //
A_SpawnPterabytes(mobj_t * actor)14217 void A_SpawnPterabytes(mobj_t *actor)
14218 {
14219 mobj_t *waypoint, *ptera;
14220 fixed_t c, s;
14221 fixed_t rad = 280*FRACUNIT;
14222 angle_t ang = 0;
14223 angle_t interval, fa;
14224 UINT8 amount = 1;
14225 UINT8 i;
14226
14227 if (LUA_CallAction(A_SPAWNPTERABYTES, actor))
14228 return;
14229
14230 if (actor->spawnpoint)
14231 amount = actor->spawnpoint->extrainfo + 1;
14232
14233 interval = FixedAngle(FRACUNIT*360/amount);
14234
14235 for (i = 0; i < amount; i++)
14236 {
14237 fa = (ang >> ANGLETOFINESHIFT) & FINEMASK;
14238 c = FINECOSINE(fa);
14239 s = FINESINE(fa);
14240 waypoint = P_SpawnMobjFromMobj(actor, FixedMul(c, rad), FixedMul(s, rad), 0, MT_PTERABYTEWAYPOINT);
14241 waypoint->angle = ang + ANGLE_90;
14242 P_SetTarget(&waypoint->tracer, actor);
14243 ptera = P_SpawnMobjFromMobj(waypoint, 0, 0, 0, MT_PTERABYTE);
14244 ptera->angle = waypoint->angle;
14245 P_SetTarget(&ptera->tracer, waypoint);
14246 ptera->extravalue1 = 0;
14247 ang += interval;
14248 }
14249 }
14250
14251 // Function: A_PterabyteHover
14252 //
14253 // Description: Hover in a circular fashion, bobbing up and down slightly.
14254 //
14255 // var1 = unused
14256 // var2 = unused
14257 //
A_PterabyteHover(mobj_t * actor)14258 void A_PterabyteHover(mobj_t *actor)
14259 {
14260 angle_t ang, fa;
14261
14262 if (LUA_CallAction(A_PTERABYTEHOVER, actor))
14263 return;
14264
14265 P_InstaThrust(actor, actor->angle, actor->info->speed);
14266 actor->angle += ANG1;
14267 actor->extravalue1 = (actor->extravalue1 + 3) % 360;
14268 ang = actor->extravalue1*ANG1;
14269 fa = (ang >> ANGLETOFINESHIFT) & FINEMASK;
14270 actor->z += FINESINE(fa);
14271 }
14272 // Function: A_RolloutSpawn
14273 //
14274 // Description: Spawns a new Rollout Rock when the currently spawned rock is destroyed or moves far enough away.
14275 //
14276 // var1 = Distance currently spawned rock should travel before spawning a new one
14277 // var2 = Object type to spawn
14278 //
A_RolloutSpawn(mobj_t * actor)14279 void A_RolloutSpawn(mobj_t *actor)
14280 {
14281 INT32 locvar1 = var1;
14282 INT32 locvar2 = var2;
14283
14284 if (LUA_CallAction(A_ROLLOUTSPAWN, actor))
14285 return;
14286
14287 if (!(actor->target)
14288 || P_MobjWasRemoved(actor->target)
14289 || P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) > locvar1)
14290 {
14291 actor->target = P_SpawnMobj(actor->x, actor->y, actor->z, locvar2);
14292 actor->target->flags2 |= (actor->flags2 & (MF2_AMBUSH | MF2_OBJECTFLIP)) | MF2_SLIDEPUSH;
14293 actor->target->eflags |= (actor->eflags & MFE_VERTICALFLIP);
14294
14295 if (actor->target->flags2 & MF2_AMBUSH)
14296 {
14297 actor->target->color = SKINCOLOR_SUPERRUST3;
14298 actor->target->colorized = true;
14299 }
14300 }
14301 }
14302
14303 // Function: A_RolloutRock
14304 //
14305 // Description: Thinker for Rollout Rock.
14306 //
14307 // var1 = Drag
14308 // var2 = Vertical bobbing speed factor
14309 //
A_RolloutRock(mobj_t * actor)14310 void A_RolloutRock(mobj_t *actor)
14311 {
14312 INT32 locvar1 = var1;
14313 INT32 locvar2 = var2;
14314 UINT8 maxframes = actor->info->reactiontime; // number of frames the mobj cycles through
14315 fixed_t pi = (22*FRACUNIT/7);
14316 fixed_t circumference = FixedMul(2 * pi, actor->radius); // used to calculate when to change frame
14317 fixed_t speed = P_AproxDistance(actor->momx, actor->momy), topspeed = FixedMul(actor->info->speed, actor->scale);
14318 boolean inwater = actor->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER);
14319
14320 if (LUA_CallAction(A_ROLLOUTROCK, actor))
14321 return;
14322
14323 actor->friction = FRACUNIT; // turns out riding on solids sucks, so let's just make it easier on ourselves
14324
14325 if (actor->eflags & MFE_JUSTHITFLOOR)
14326 S_StartSound(actor, actor->info->painsound);
14327
14328 if (actor->threshold)
14329 actor->threshold--;
14330
14331 if (inwater && !(actor->flags2 & MF2_AMBUSH)) // buoyancy in water (or lava)
14332 {
14333 UINT8 flip = P_MobjFlip(actor);
14334 fixed_t prevmomz = actor->momz;
14335 actor->momz = FixedMul(actor->momz, locvar2);
14336 actor->momz += flip * FixedMul(locvar2, actor->scale);
14337 if (flip*prevmomz < 0 && flip*actor->momz >= 0 && !actor->threshold)
14338 {
14339 if (actor->eflags & MFE_UNDERWATER)
14340 S_StartSound(actor, sfx_splash);
14341 else if (!actor->threshold)
14342 S_StartSound(actor, sfx_splish);
14343 actor->threshold = max((topspeed - speed) >> FRACBITS, 8);
14344 }
14345 }
14346
14347 if (speed > topspeed) // cap speed
14348 {
14349 actor->momx = FixedMul(FixedDiv(actor->momx, speed), topspeed);
14350 actor->momy = FixedMul(FixedDiv(actor->momy, speed), topspeed);
14351 }
14352
14353 if (P_IsObjectOnGround(actor) || inwater) // apply drag to speed (compensates for lack of friction but also works in liquids)
14354 {
14355 actor->momx = FixedMul(actor->momx, locvar1);
14356 actor->momy = FixedMul(actor->momy, locvar1);
14357 }
14358
14359 speed = P_AproxDistance(actor->momx, actor->momy); // recalculate speed for visual rolling
14360
14361 if (speed < actor->scale >> 1) // stop moving if speed is insignificant
14362 {
14363 actor->momx = 0;
14364 actor->momy = 0;
14365 }
14366 else if (speed > actor->scale)
14367 {
14368 actor->movecount = 1; // rock has moved; fuse should be set so we don't have a trillion rocks lying around
14369 actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy); // set rock's angle to movement direction
14370 actor->movefactor += speed;
14371 if (actor->movefactor > circumference / maxframes) // if distance moved is enough to change frame, change it!
14372 {
14373 actor->reactiontime++;
14374 actor->reactiontime %= maxframes;
14375 actor->movefactor = 0;
14376 }
14377 }
14378
14379 actor->frame = actor->reactiontime % maxframes; // set frame
14380
14381 if (!actor->tracer || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health)
14382 actor->flags |= MF_PUSHABLE;
14383
14384 if (!(actor->flags & MF_PUSHABLE) || (actor->movecount != 1)) // if being ridden or haven't moved, don't disappear
14385 actor->fuse = actor->info->painchance;
14386 else if (actor->fuse < 2*TICRATE)
14387 actor->flags2 ^= MF2_DONTDRAW;
14388
14389 }
14390
14391 // Function: A_DragonbomberSpawn
14392 //
14393 // Description: Spawns the body parts for Dragonbomber
14394 //
14395 // var1 = Tail segments to spawn
14396 // var2 = unused
14397 //
A_DragonbomberSpawn(mobj_t * actor)14398 void A_DragonbomberSpawn(mobj_t *actor)
14399 {
14400 UINT8 i;
14401 mobj_t *mo = actor;
14402
14403 if (LUA_CallAction(A_DRAGONBOMBERSPAWN, actor))
14404 return;
14405
14406 for (i = 0; i < var1; i++) // spawn tail segments
14407 {
14408 mobj_t *segment;
14409 fixed_t x, y;
14410 x = P_ReturnThrustX(mo, mo->angle, -mo->radius << 1);
14411 y = P_ReturnThrustY(mo, mo->angle, -mo->radius << 1);
14412 segment = P_SpawnMobjFromMobj(mo, x, y, 0, MT_DRAGONTAIL);
14413 P_SetTarget(&segment->target, mo);
14414 P_SetTarget(&mo->tracer, segment);
14415 segment->angle = mo->angle;
14416 mo = segment;
14417 }
14418 for (i = 0; i < 2; i++) // spawn wings
14419 {
14420 mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_DRAGONWING);
14421 P_SetTarget(&mo->target, actor);
14422 mo->movedir = ANGLE_90 + i * ANGLE_180;
14423 }
14424 }
14425
14426 // Function: A_DragonWing
14427 //
14428 // Description: Moves actor such that it is placed away from its target at a distance equal to the target's radius in the direction of its target's angle.
14429 // The actor's movedir can be used to offset the angle.
14430 //
14431 // var1 = unused
14432 // var2 = unused
14433 //
A_DragonWing(mobj_t * actor)14434 void A_DragonWing(mobj_t *actor)
14435 {
14436 mobj_t *target = actor->target;
14437 fixed_t x, y;
14438
14439 if (LUA_CallAction(A_DRAGONWING, actor))
14440 return;
14441
14442 if (target == NULL || !target->health)
14443 {
14444 P_RemoveMobj(actor);
14445 return;
14446 }
14447 actor->angle = target->angle + actor->movedir;
14448 x = target->x + P_ReturnThrustX(actor, actor->angle, -target->radius);
14449 y = target->y + P_ReturnThrustY(actor, actor->angle, -target->radius);
14450 P_TeleportMove(actor, x, y, target->z);
14451 }
14452
14453 // Function: A_DragonSegment
14454 //
14455 // Description: Moves actor such that it is placed away from its target at an absolute distance equal to the sum of the two mobjs' radii.
14456 //
14457 // var1 = unused
14458 // var2 = unused
14459 //
A_DragonSegment(mobj_t * actor)14460 void A_DragonSegment(mobj_t *actor)
14461 {
14462 mobj_t *target = actor->target;
14463 fixed_t dist;
14464 fixed_t radius;
14465 angle_t hangle;
14466 angle_t zangle;
14467 fixed_t hdist;
14468 fixed_t xdist;
14469 fixed_t ydist;
14470 fixed_t zdist;
14471
14472 if (LUA_CallAction(A_DRAGONSEGMENT, actor))
14473 return;
14474
14475 if (target == NULL || !target->health)
14476 {
14477 P_RemoveMobj(actor);
14478 return;
14479 }
14480
14481 dist = P_AproxDistance(P_AproxDistance(actor->x - target->x, actor->y - target->y), actor->z - target->z);
14482 radius = actor->radius + target->radius;
14483 hangle = R_PointToAngle2(target->x, target->y, actor->x, actor->y);
14484 zangle = R_PointToAngle2(0, target->z, dist, actor->z);
14485 hdist = P_ReturnThrustX(target, zangle, radius);
14486 xdist = P_ReturnThrustX(target, hangle, hdist);
14487 ydist = P_ReturnThrustY(target, hangle, hdist);
14488 zdist = P_ReturnThrustY(target, zangle, radius);
14489
14490 actor->angle = hangle;
14491 P_TeleportMove(actor, target->x + xdist, target->y + ydist, target->z + zdist);
14492 }
14493
14494 // Function: A_ChangeHeight
14495 //
14496 // Description: Changes the actor's height by var1
14497 //
14498 // var1 = height
14499 // var2 =
14500 // &1: height is absolute
14501 // &2: scale with actor's scale
14502 //
A_ChangeHeight(mobj_t * actor)14503 void A_ChangeHeight(mobj_t *actor)
14504 {
14505 INT32 locvar1 = var1;
14506 INT32 locvar2 = var2;
14507 fixed_t height = locvar1;
14508 boolean reverse;
14509
14510 if (LUA_CallAction(A_CHANGEHEIGHT, actor))
14511 return;
14512
14513 reverse = (actor->eflags & MFE_VERTICALFLIP) || (actor->flags2 & MF2_OBJECTFLIP);
14514
14515 if (locvar2 & 2)
14516 height = FixedMul(height, actor->scale);
14517
14518 P_UnsetThingPosition(actor);
14519 if (locvar2 & 1)
14520 {
14521 if (reverse)
14522 actor->z += actor->height - locvar1;
14523 actor->height = locvar1;
14524 }
14525 else
14526 {
14527 if (reverse)
14528 actor->z -= locvar1;
14529 actor->height += locvar1;
14530 }
14531 P_SetThingPosition(actor);
14532 }
14533