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