Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2214-pre1
  • 2214-pre2
  • 2214-pre3
  • 2214-pre4
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • better-distance-math
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • classic-netcode-fixes
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-dedi-pthread
  • fix-enemy-target
  • fix-opengl-parameter-crash
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • just-in-case
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-gfx-2
  • lua-gfx-sprites
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.15
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
142 results

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
  • Jisk/srb-2-beef-jerky
  • voltybystorm/SRB2
  • ZenithNeko/srb-2-xp
  • Nep2Disk/SRB2
  • Cloudeon/SRB2
  • mushe/srb-2-ps-b
122 results
Select Git revision
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • accel-momentum
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • better-player-states
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • few-kart-lua-changes
  • ffloorclip
  • fix-1215
  • fix-167
  • fix-cvar-conflicts
  • fix-equation-slopes-near-edges
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • gitlab-ci
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-debug-library
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • map-components-signedness-fixes
  • master
  • menu-edits
  • mobj-dispoffset
  • more-cleanup
  • movie
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
141 results
Show changes
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -36,6 +36,9 @@
#include "m_perfstats.h" // ps_checkposition_calls
// Formerly called MAXRADIUS
#define MAXTRYMOVE (32*FRACUNIT)
fixed_t tmbbox[4];
mobj_t *tmthing;
static INT32 tmflags;
......@@ -389,7 +392,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
{
INT32 pflags;
UINT8 secondjump;
boolean washoming;
if (spring->flags & MF_ENEMY) // Spring shells
P_SetTarget(&spring->target, object);
......@@ -412,7 +414,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
{
pflags = PF_SPINNING;
P_SetMobjState(object, S_PLAY_ROLL);
S_StartSound(object, sfx_spin);
S_StartSoundFromMobj(object, sfx_spin);
}
else
P_SetMobjState(object, S_PLAY_ROLL);
......@@ -421,7 +423,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
{
boolean wasSpindashing = object->player->dashspeed > 0 && (object->player->charability2 == CA2_SPINDASH);
pflags = object->player->pflags & (PF_STARTJUMP | PF_JUMPED | PF_NOJUMPDAMAGE | PF_SPINNING | PF_THOKKED | PF_BOUNCING); // I still need these.
pflags = object->player->pflags & (PF_STARTJUMP | PF_JUMPED | PF_NOJUMPDAMAGE | PF_SPINNING | PF_BOUNCING); // I still need these.
if (wasSpindashing) // Ensure we're in the rolling state, and not spindash.
P_SetMobjState(object, S_PLAY_ROLL);
......@@ -433,7 +435,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
}
}
secondjump = object->player->secondjump;
washoming = object->player->homing;
P_ResetPlayer(object->player);
if (spring->info->painchance == 1) // For all those ancient, SOC'd abilities.
......@@ -445,8 +446,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
{
object->player->pflags |= (pflags &~ PF_STARTJUMP);
object->player->secondjump = secondjump;
if (washoming)
object->player->pflags &= ~PF_THOKKED;
}
else if (!vertispeed)
{
......@@ -495,79 +494,63 @@ springstate:
{
if (object->player->charability == CA_TWINSPIN || object->player->charability2 == CA2_MELEE)
P_TwinSpinRejuvenate(object->player, (object->player->charability == CA_TWINSPIN ? object->player->thokitem : object->player->revitem));
S_StartSound(object, sfx_sprong); // strong spring. sprong.
S_StartSoundFromMobj(object, sfx_sprong); // strong spring. sprong.
}
}
return final;
}
static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
static void P_DoFan(mobj_t *fan, mobj_t *object)
{
player_t *p = object->player; // will be NULL if not a player
fixed_t zdist; // distance between bottoms
fixed_t speed = spring->info->mass; // conveniently, both fans and gas jets use this for the vertical thrust
SINT8 flipval = P_MobjFlip(spring); // virtually everything here centers around the thruster's gravity, not the object's!
fixed_t speed = fan->info->mass; // fans use this for the vertical thrust
SINT8 flipval = P_MobjFlip(fan); // virtually everything here centers around the thruster's gravity, not the object's!
if (p && object->state == &states[object->info->painstate]) // can't use fans and gas jets when player is in pain!
if (p && P_IsPlayerInState(p, S_PLAY_PAIN)) // can't use fans when player is in pain!
return;
// is object's top below thruster's position? if not, calculate distance between their bottoms
if (spring->eflags & MFE_VERTICALFLIP)
if (fan->eflags & MFE_VERTICALFLIP)
{
if (object->z > spring->z + spring->height)
if (object->z > fan->z + fan->height)
return;
zdist = (spring->z + spring->height) - (object->z + object->height);
zdist = (fan->z + fan->height) - (object->z + object->height);
}
else
{
if (object->z + object->height < spring->z)
if (object->z + object->height < fan->z)
return;
zdist = object->z - spring->z;
zdist = object->z - fan->z;
}
object->standingslope = NULL; // No launching off at silly angles for you.
switch (spring->type)
switch (fan->type)
{
case MT_FAN: // fan
if (zdist > (spring->health << FRACBITS)) // max z distance determined by health (set by map thing args[0])
if (zdist > (fan->health << FRACBITS)) // max z distance determined by health (set by map thing args[0])
break;
if (flipval*object->momz >= FixedMul(speed, spring->scale)) // if object's already moving faster than your best, don't bother
if (flipval*object->momz >= FixedMul(speed, fan->scale)) // if object's already moving faster than your best, don't bother
break;
if (p && (p->climbing || p->pflags & PF_GLIDING)) // doesn't affect Knux when he's using his abilities!
break;
object->momz += flipval*FixedMul(speed/4, spring->scale);
object->momz += flipval*FixedMul(speed/4, fan->scale);
// limit the speed if too high
if (flipval*object->momz > FixedMul(speed, spring->scale))
object->momz = flipval*FixedMul(speed, spring->scale);
if (flipval*object->momz > FixedMul(speed, fan->scale))
object->momz = flipval*FixedMul(speed, fan->scale);
if (p && !p->powers[pw_tailsfly] && !p->powers[pw_carry]) // doesn't reset anim for Tails' flight
{
P_ResetPlayer(p);
P_SetMobjState(object, S_PLAY_FALL);
P_SetTarget(&object->tracer, spring);
P_SetTarget(&object->tracer, fan);
p->powers[pw_carry] = CR_FAN;
}
break;
case MT_STEAM: // Steam
if (zdist > FixedMul(16*FRACUNIT, spring->scale))
break;
if (spring->state != &states[S_STEAM1]) // Only when it bursts
break;
object->eflags |= MFE_SPRUNG;
object->momz = flipval*FixedMul(speed, FixedSqrt(FixedMul(spring->scale, object->scale))); // scale the speed with both objects' scales, just like with springs!
if (p)
{
P_ResetPlayer(p);
if (p->panim != PA_FALL)
P_SetMobjState(object, S_PLAY_FALL);
}
break;
default:
break;
}
......@@ -602,7 +585,7 @@ static void P_DoPterabyteCarry(player_t *player, mobj_t *ptera)
P_SetTarget(&player->mo->tracer, ptera);
player->pflags &= ~PF_APPLYAUTOBRAKE;
player->powers[pw_carry] = CR_PTERABYTE;
S_StartSound(player->mo, sfx_s3k4a);
S_StartSoundFromMobj(player->mo, sfx_s3k4a);
P_UnsetThingPosition(player->mo);
player->mo->x = ptera->x;
player->mo->y = ptera->y;
......@@ -671,7 +654,7 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
P_ResetPlayer(sonic);
P_SetTarget(&sonic->mo->tracer, tails->mo);
sonic->powers[pw_carry] = CR_PLAYER;
S_StartSound(sonic->mo, sfx_s3k4a);
S_StartSoundFromMobj(sonic->mo, sfx_s3k4a);
P_UnsetThingPosition(sonic->mo);
sonic->mo->x = tails->mo->x;
sonic->mo->y = tails->mo->y;
......@@ -716,7 +699,7 @@ static void P_SlapStick(mobj_t *fang, mobj_t *pole)
var1 = var2 = 0;
A_Scream(pole->tracer->tracer);
S_StartSound(fang, sfx_altdi1);
S_StartSoundFromMobj(fang, sfx_altdi1);
P_SetTarget(&pole->tracer->tracer, NULL);
P_SetMobjState(pole->tracer, pole->info->xdeathstate);
......@@ -827,7 +810,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
{
mobj_t *iter;
if (thing->flags & MF_SOLID)
S_StartSound(tmthing, thing->info->deathsound);
S_StartSoundFromMobj(tmthing, thing->info->deathsound);
for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
P_KillMobj(iter, tmthing, tmthing, 0);
......@@ -854,7 +837,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
return CHECKTHING_NOCOLLIDE; // underneath
if (thing->flags & MF_SOLID)
S_StartSound(tmthing, thing->info->deathsound);
S_StartSoundFromMobj(tmthing, thing->info->deathsound);
for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
P_KillMobj(iter, tmthing, tmthing, 0);
......@@ -1021,7 +1004,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
fixed_t dm = min(FixedHypot(ref->momx, ref->momy), 16*FRACUNIT);
angle_t ang = R_PointToAngle2(0, 0, ref->momx, ref->momy) - thing->angle;
fixed_t s = FINESINE((ang >> ANGLETOFINESHIFT) & FINEMASK);
S_StartSound(tmthing, thing->info->activesound);
S_StartSoundFromMobj(tmthing, thing->info->activesound);
thing->extravalue2 += 2*FixedMul(s, dm)/3;
return CHECKTHING_COLLIDE;
}
......@@ -1043,7 +1026,6 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
if ((thing->flags & MF_PUSHABLE) // not carrying a player
&& (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something
&& !(tmthing->player->powers[pw_ignorelatch] & (1<<15))
&& ((tmthing->eflags & MFE_VERTICALFLIP) == (thing->eflags & MFE_VERTICALFLIP))
&& (P_MobjFlip(tmthing)*tmthing->momz <= 0)
&& ((!(tmthing->eflags & MFE_VERTICALFLIP) && abs(thing->z + thing->height - tmthing->z) < (thing->height>>2))
|| (tmthing->eflags & MFE_VERTICALFLIP && abs(tmthing->z + tmthing->height - thing->z) < (thing->height>>2))))
......@@ -1057,6 +1039,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
P_SetTarget(&tmthing->tracer, thing);
if (!P_IsObjectOnGround(thing))
thing->momz += tmthing->momz;
return CHECKTHING_COLLIDE;
}
}
......@@ -1087,7 +1070,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
thing->momy = tmthing->momy;
tmthing->momx = tempmomx;
tmthing->momy = tempmomy;
S_StartSound(thing, thing->info->painsound);
S_StartSoundFromMobj(thing, thing->info->painsound);
}
}
......@@ -1143,7 +1126,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
tmthing->momy /= -8;
tmthing->momz /= -8;
if (thing->info->activesound)
S_StartSound(thing, thing->info->activesound);
S_StartSoundFromMobj(thing, thing->info->activesound);
P_SetMobjState(thing, thing->info->meleestate);
P_SetTarget(&thing->tracer, tmthing->tracer);
return CHECKTHING_COLLIDE;
......@@ -1162,7 +1145,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
thing->momx = P_ReturnThrustX(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
thing->momy = P_ReturnThrustY(tmthing, tmthing->angle, 2*tmthing->extravalue1*tmthing->scale/3);
if (thing->info->activesound)
S_StartSound(thing, thing->info->activesound);
S_StartSoundFromMobj(thing, thing->info->activesound);
P_SetMobjState(thing, thing->info->meleestate);
if (tmthing->tracer)
P_SetTarget(&thing->tracer, tmthing->tracer->target);
......@@ -1200,7 +1183,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
if (!damagetype && thing->flags & MF_FIRE) // BURN!
damagetype = DMG_FIRE;
if (P_DamageMobj(tmthing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
S_StartSound(thing, damagetype);
S_StartSoundFromMobj(thing, damagetype);
return CHECKTHING_COLLIDE;
}
return CHECKTHING_NOCOLLIDE;
......@@ -1218,7 +1201,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
if (!damagetype && tmthing->flags & MF_FIRE) // BURN!
damagetype = DMG_FIRE;
if (P_DamageMobj(thing, tmthing, tmthing, 1, damagetype) && (damagetype = (tmthing->info->mass>>8)))
S_StartSound(tmthing, damagetype);
S_StartSoundFromMobj(tmthing, damagetype);
return CHECKTHING_COLLIDE;
}
return CHECKTHING_NOCOLLIDE;
......@@ -1278,8 +1261,9 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type)
{
// Don't hit same species as originator.
if (thing == tmthing->target)
// Don't hit yourself, and if a player, don't hit bots
if (thing == tmthing->target
|| (thing->player && tmthing->target->player && (thing->player->bot == BOT_2PAI || thing->player->bot == BOT_2PHUMAN)))
return CHECKTHING_IGNORE;
if (thing->type != MT_PLAYER)
......@@ -1442,7 +1426,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
}
if (thing->type != MT_GARGOYLE || P_IsObjectOnGround(thing))
S_StartSound(thing, thing->info->activesound);
S_StartSoundFromMobj(thing, thing->info->activesound);
P_SetTarget(&thing->target, tmthing);
}
......@@ -1484,13 +1468,13 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
}
// check for special pickup
if (thing->flags & MF_SPECIAL && tmthing->player)
if (thing->flags & MF_SPECIAL && (tmthing->player || (tmthing->flags & MF_PUSHABLE))) // MF_PUSHABLE added for steam jets
{
P_TouchSpecialThing(thing, tmthing, true); // can remove thing
return CHECKTHING_COLLIDE;
}
// check again for special pickup
if (tmthing->flags & MF_SPECIAL && thing->player)
if (tmthing->flags & MF_SPECIAL && (thing->player || (thing->flags & MF_PUSHABLE))) // MF_PUSHABLE added for steam jets
{
P_TouchSpecialThing(tmthing, thing, true); // can remove thing
return CHECKTHING_COLLIDE;
......@@ -1578,15 +1562,15 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
if (thing->flags & MF_PUSHABLE)
{
if (tmthing->type == MT_FAN || tmthing->type == MT_STEAM)
P_DoFanAndGasJet(tmthing, thing);
if (tmthing->type == MT_FAN)
P_DoFan(tmthing, thing);
}
if (tmthing->flags & MF_PUSHABLE)
{
if (thing->type == MT_FAN || thing->type == MT_STEAM)
if (thing->type == MT_FAN)
{
P_DoFanAndGasJet(thing, tmthing);
P_DoFan(thing, tmthing);
return CHECKTHING_COLLIDE;
}
else if (thing->flags & MF_SPRING)
......@@ -1679,8 +1663,8 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
}
}
if (tmthing->type == MT_FAN || tmthing->type == MT_STEAM)
P_DoFanAndGasJet(tmthing, thing);
if (tmthing->type == MT_FAN)
P_DoFan(tmthing, thing);
}
if (tmthing->player) // Is the moving/interacting object the player?
......@@ -1688,8 +1672,8 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
if (!tmthing->health)
return CHECKTHING_IGNORE;
if (thing->type == MT_FAN || thing->type == MT_STEAM)
P_DoFanAndGasJet(thing, tmthing);
if (thing->type == MT_FAN)
P_DoFan(thing, tmthing);
else if (thing->flags & MF_SPRING && tmthing->player->powers[pw_carry] != CR_MINECART)
{
if ( thing->z <= tmthing->z + tmthing->height
......@@ -1755,8 +1739,8 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
// not solid not blocked
unsigned collide = CHECKTHING_NOCOLLIDE;
if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
; // springs, gas jets and springs should never be able to step up onto a player
if ((tmthing->flags & MF_SPRING || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
; // springs and spikes should never be able to step up onto a player
// z checking at last
// Treat noclip things as non-solid!
else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
......@@ -2184,15 +2168,10 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
}
}
// The bounding box is extended by MAXRADIUS
// because mobj_ts are grouped into mapblocks
// based on their origin point, and can overlap
// into adjacent blocks by up to MAXRADIUS units.
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);
......@@ -2283,7 +2262,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
for (bx = xl; bx <= xh; bx++)
for (by = yl; by <= yh; by++)
{
if (!P_BlockThingsIterator(bx, by, PIT_CheckThing))
if (!P_BlockThingsIterator(bx, by, PIT_CheckThing, tmthing))
blockval = false;
else
tmhitthing = tmfloorthing;
......@@ -2412,11 +2391,6 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
}
}
// The bounding box is extended by MAXRADIUS
// because mobj_ts are grouped into mapblocks
// based on their origin point, and can overlap
// into adjacent blocks by up to MAXRADIUS units.
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
......@@ -2521,6 +2495,9 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
floatok = false;
if (dedicated) // this crashes so don't even try it
return false;
if (twodlevel
|| (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD))
|| (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)))
......@@ -2544,16 +2521,16 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
}
do {
if (x-tryx > MAXRADIUS)
tryx += MAXRADIUS;
else if (x-tryx < -MAXRADIUS)
tryx -= MAXRADIUS;
if (x-tryx > MAXTRYMOVE)
tryx += MAXTRYMOVE;
else if (x-tryx < -MAXTRYMOVE)
tryx -= MAXTRYMOVE;
else
tryx = x;
if (y-tryy > MAXRADIUS)
tryy += MAXRADIUS;
else if (y-tryy < -MAXRADIUS)
tryy -= MAXRADIUS;
if (y-tryy > MAXTRYMOVE)
tryy += MAXTRYMOVE;
else if (y-tryy < -MAXTRYMOVE)
tryy -= MAXTRYMOVE;
else
tryy = y;
......@@ -2699,7 +2676,7 @@ increment_move
floatok = false;
// This makes sure that there are no freezes from computing extremely small movements.
// Originally was MAXRADIUS/2, but that can cause some bad inconsistencies for small players.
// Originally was MAXTRYMOVE/2, but that can cause some bad inconsistencies for small players.
radius = max(radius, thing->scale);
// And we also have to prevent Big Large (tm) movements, as those can skip too far
......@@ -2888,10 +2865,10 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
{
INT32 xl, xh, yl, yh;
yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT;
xh = (unsigned)(thing->x + MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT;
xl = (unsigned)(thing->x - MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT;
yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
xl = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);
......@@ -2899,7 +2876,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
standx = x;
standy = y;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushableMoved);
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushableMoved, stand);
}
// Link the thing into its new position
......@@ -2963,16 +2940,16 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y)
tryx = thing->x;
tryy = thing->y;
do {
if (x-tryx > MAXRADIUS)
tryx += MAXRADIUS;
else if (x-tryx < -MAXRADIUS)
tryx -= MAXRADIUS;
if (x-tryx > MAXTRYMOVE)
tryx += MAXTRYMOVE;
else if (x-tryx < -MAXTRYMOVE)
tryx -= MAXTRYMOVE;
else
tryx = x;
if (y-tryy > MAXRADIUS)
tryy += MAXRADIUS;
else if (y-tryy < -MAXRADIUS)
tryy -= MAXRADIUS;
if (y-tryy > MAXTRYMOVE)
tryy += MAXTRYMOVE;
else if (y-tryy < -MAXTRYMOVE)
tryy -= MAXTRYMOVE;
else
tryy = y;
......@@ -3056,7 +3033,7 @@ static boolean P_ThingHeightClip(mobj_t *thing)
if (tmfloorz > oldfloorz+thing->height)
return true;
bouncing = thing->player && thing->state-states == S_PLAY_BOUNCE_LANDING && P_IsObjectOnGround(thing);
bouncing = thing->player && P_IsPlayerInState(thing->player, S_PLAY_BOUNCE_LANDING) && P_IsObjectOnGround(thing);
if ((onfloor || bouncing) && !(thing->flags & MF_NOGRAVITY) && floormoved)
{
......@@ -3499,12 +3476,12 @@ static void PTR_GlideClimbTraverse(line_t *li)
if (!slidemo->player->climbing)
{
S_StartSound(slidemo, sfx_s3k4a);
S_StartSoundFromMobj(slidemo, sfx_s3k4a);
slidemo->player->climbing = 5;
if (slidemo->player->powers[pw_super])
{
P_Earthquake(slidemo, slidemo, 256*FRACUNIT);
S_StartSound(slidemo, sfx_s3k49);
S_StartSoundFromMobj(slidemo, sfx_s3k49);
}
}
......@@ -3980,23 +3957,25 @@ papercollision:
mo->momy = tmymove;
}
const fixed_t tmradius = mo->radius > 8 ? mo->radius : 8;
do {
if (tmxmove > mo->radius) {
newx = mo->x + mo->radius;
tmxmove -= mo->radius;
} else if (tmxmove < -mo->radius) {
newx = mo->x - mo->radius;
tmxmove += mo->radius;
if (tmxmove > tmradius) {
newx = mo->x + tmradius;
tmxmove -= tmradius;
} else if (tmxmove < -tmradius) {
newx = mo->x - tmradius;
tmxmove += tmradius;
} else {
newx = mo->x + tmxmove;
tmxmove = 0;
}
if (tmymove > mo->radius) {
newy = mo->y + mo->radius;
tmymove -= mo->radius;
} else if (tmymove < -mo->radius) {
newy = mo->y - mo->radius;
tmymove += mo->radius;
if (tmymove > tmradius) {
newy = mo->y + tmradius;
tmymove -= tmradius;
} else if (tmymove < -tmradius) {
newy = mo->y - tmradius;
tmymove += tmradius;
} else {
newy = mo->y + tmymove;
tmymove = 0;
......@@ -4229,7 +4208,8 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
INT32 xl, xh, yl, yh;
fixed_t dist;
dist = FixedMul(damagedist, spot->scale) + MAXRADIUS;
dist = FixedMul(damagedist, spot->scale);
yh = (unsigned)(spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
yl = (unsigned)(spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
xh = (unsigned)(spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
......@@ -4243,7 +4223,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
bombdamagetype = damagetype;
bombsightcheck = sightcheck;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_RadiusAttack);
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_RadiusAttack, bombspot);
}
//
......@@ -4330,7 +4310,7 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush, boolean crunch
{
crumble_t *crumbler;
if (think->function.acp1 != (actionf_p1)T_StartCrumble)
if (think->function != (actionf_p1)T_StartCrumble)
continue;
crumbler = (crumble_t *)think;
......@@ -4399,15 +4379,15 @@ static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boo
{
mobj_t *mo;
blocknode_t *block;
blocknode_t *next = NULL;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue;
block = blocklinks[y * bmapwidth + x];
for (; block; block = block->mnext)
for (block = blocklinks[y * bmapwidth + x]; block != NULL; block = next)
{
mo = block->mobj;
next = block->mnext;
// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
if (!P_MobjInsidePolyobj(po, mo))
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -500,8 +500,24 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
INT32 texnum = R_GetTextureNum(side->midtexture); // make sure the texture is actually valid
if (texnum) {
fixed_t scaley = abs(side->scaley_mid);
fixed_t offsetvalue = FixedDiv(side->rowoffset + side->offsety_mid, scaley);
fixed_t midopentop, midopenbottom;
if (linedef->flags & ML_NOSKEW)
{
// Use the sector's actual heights if the midtexture is not skewed
midopentop = min(front->ceilingheight, back->ceilingheight);
midopenbottom = max(front->floorheight, back->floorheight);
}
else
{
midopentop = opentop;
midopenbottom = openbottom;
}
// Get the midtexture's height
texheight = textures[texnum]->height << FRACBITS;
texheight = FixedDiv(textureheight[texnum], scaley);
// Set texbottom and textop to the Z coordinates of the texture's boundaries
#if 0
......@@ -509,26 +525,26 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
// on non-solid polyobjects should NEVER happen in the future
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat
texbottom = back->floorheight + side->rowoffset + side->offsety_mid;
textop = back->ceilingheight + side->rowoffset + side->offsety_mid;
texbottom = back->floorheight + offsetvalue;
textop = back->ceilingheight + offsetvalue;
} else if (linedef->flags & ML_MIDTEX) {
texbottom = back->floorheight + side->rowoffset + side->offsety_mid;
texbottom = back->floorheight + offsetvalue;
textop = texbottom + texheight*(side->repeatcnt+1);
} else {
textop = back->ceilingheight + side->rowoffset + side->offsety_mid;
textop = back->ceilingheight + offsetvalue;
texbottom = textop - texheight*(side->repeatcnt+1);
}
} else
#endif
{
if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat
texbottom = openbottom + side->rowoffset + side->offsety_mid;
textop = opentop + side->rowoffset + side->offsety_mid;
texbottom = midopenbottom + offsetvalue;
textop = midopentop + offsetvalue;
} else if (linedef->flags & ML_MIDPEG) {
texbottom = openbottom + side->rowoffset + side->offsety_mid;
texbottom = midopenbottom + offsetvalue;
textop = texbottom + texheight*(side->repeatcnt+1);
} else {
textop = opentop + side->rowoffset + side->offsety_mid;
textop = midopentop + offsetvalue;
texbottom = textop - texheight*(side->repeatcnt+1);
}
}
......@@ -539,11 +555,21 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
delta2 = abs(thingtop - texmid);
if (delta1 > delta2) { // Below
if (opentop > texbottom)
if (opentop > texbottom) {
opentop = texbottom;
if (linedef->flags & ML_NOSKEW)
opentopslope = NULL; // Object is not actually on a slope
else
opentopslope = linedef->midtexslope;
}
} else { // Above
if (openbottom < textop)
if (openbottom < textop) {
openbottom = textop;
if (linedef->flags & ML_NOSKEW)
openbottomslope = NULL; // Object is not actually on a slope
else
openbottomslope = linedef->midtexslope;
}
}
}
}
......@@ -1024,35 +1050,39 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
//
// P_BlockThingsIterator
//
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *), mobj_t *thing)
{
mobj_t *mobj;
blocknode_t *block;
blocknode_t *block, *next = NULL;
boolean checkthing = false;
if (thing)
checkthing = true;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
return true;
// Check interaction with the objects in the blockmap.
for (block = blocklinks[y*bmapwidth + x]; block; block = block->mnext)
for (block = blocklinks[y*bmapwidth + x]; block != NULL; block = next)
{
mobj = block->mobj;
next = block->mnext; // We want to note our reference to mnext here!
if (!func(mobj))
if (!func(block->mobj))
return false;
if (P_MobjWasRemoved(tmthing)) // func just broke blockmap chain, cannot continue.
if (checkthing && P_MobjWasRemoved(thing)) // func just popped our tmthing, cannot continue.
return true;
}
return true;
}
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *))
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *), mobj_t *thing)
{
boolean status = true;
for (INT32 bx = x1; bx <= x2; bx++)
for (INT32 by = y1; by <= y2; by++)
if (!P_BlockThingsIterator(bx, by, func))
if (!P_BlockThingsIterator(bx, by, func, thing))
status = false;
return status;
......@@ -1425,7 +1455,7 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
INT32 flags, traverser_t trav)
{
fixed_t xt1, yt1, xt2, yt2;
fixed_t xstep, ystep, partial, xintercept, yintercept;
fixed_t xstep, ystep, partialx, partialy, xintercept, yintercept;
INT32 mapx, mapy, mapxstep, mapystep, count;
earlyout = flags & PT_EARLYOUT;
......@@ -1444,56 +1474,82 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
trace.dx = px2 - px1;
trace.dy = py2 - py1;
px1 -= bmaporgx;
py1 -= bmaporgy;
xt1 = (unsigned)px1>>MAPBLOCKSHIFT;
yt1 = (unsigned)py1>>MAPBLOCKSHIFT;
xt1 = px1>>MAPBLOCKSHIFT;
yt1 = py1>>MAPBLOCKSHIFT;
px1 = (unsigned)(px1 - bmaporgx);
py1 = (unsigned)(py1 - bmaporgy);
px2 -= bmaporgx;
py2 -= bmaporgy;
xt2 = (unsigned)px2>>MAPBLOCKSHIFT;
yt2 = (unsigned)py2>>MAPBLOCKSHIFT;
xt2 = px2>>MAPBLOCKSHIFT;
yt2 = py2>>MAPBLOCKSHIFT;
px2 = (unsigned)(px2 - bmaporgx);
py2 = (unsigned)(py2 - bmaporgy);
if (xt2 > xt1)
{
mapxstep = 1;
partial = FRACUNIT - ((px1>>MAPBTOFRAC) & FRACMASK);
partialx = FRACUNIT - (((unsigned)px1>>MAPBTOFRAC) & FRACMASK);
ystep = FixedDiv(py2 - py1, abs(px2 - px1));
}
else if (xt2 < xt1)
{
mapxstep = -1;
partial = (px1>>MAPBTOFRAC) & FRACMASK;
partialx = ((unsigned)px1>>MAPBTOFRAC) & FRACMASK;
ystep = FixedDiv(py2 - py1, abs(px2 - px1));
}
else
{
mapxstep = 0;
partial = FRACUNIT;
partialx = FRACUNIT;
ystep = 256*FRACUNIT;
}
yintercept = (py1>>MAPBTOFRAC) + FixedMul(partial, ystep);
yintercept = ((unsigned)py1>>MAPBTOFRAC) + FixedMul(partialx, ystep);
if (yt2 > yt1)
{
mapystep = 1;
partial = FRACUNIT - ((py1>>MAPBTOFRAC) & FRACMASK);
partialy = FRACUNIT - (((unsigned)py1>>MAPBTOFRAC) & FRACMASK);
xstep = FixedDiv(px2 - px1, abs(py2 - py1));
}
else if (yt2 < yt1)
{
mapystep = -1;
partial = (py1>>MAPBTOFRAC) & FRACMASK;
partialy = ((unsigned)py1>>MAPBTOFRAC) & FRACMASK;
xstep = FixedDiv(px2 - px1, abs(py2 - py1));
}
else
{
mapystep = 0;
partial = FRACUNIT;
partialy = FRACUNIT;
xstep = 256*FRACUNIT;
}
xintercept = (px1>>MAPBTOFRAC) + FixedMul(partial, xstep);
xintercept = ((unsigned)px1>>MAPBTOFRAC) + FixedMul(partialy, xstep);
// [RH] Fix for traces that pass only through blockmap corners. In that case,
// xintercept and yintercept can both be set ahead of mapx and mapy, so the
// for loop would never advance anywhere.
if (abs(xstep) == 1 && abs(ystep) == 1)
{
if (ystep < 0)
{
partialx = FRACUNIT - partialx;
}
if (xstep < 0)
{
partialy = FRACUNIT - partialy;
}
if (partialx == partialy)
{
xintercept = xt1;
yintercept = yt1;
}
}
xt1 = (unsigned)px1>>MAPBLOCKSHIFT;
yt1 = (unsigned)py1>>MAPBLOCKSHIFT;
xt2 = (unsigned)px2>>MAPBLOCKSHIFT;
yt2 = (unsigned)py2>>MAPBLOCKSHIFT;
// Step through map blocks.
// Count is present to prevent a round off error
......@@ -1508,23 +1564,67 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
return false; // early out
if (flags & PT_ADDTHINGS)
if (!P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts))
if (!P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts, NULL))
return false; // early out
if (mapx == xt2 && mapy == yt2)
// both coordinates reached the end, so end the traversing.
if ((mapxstep | mapystep) == 0)
break;
if ((yintercept >> FRACBITS) == mapy)
// [RH] Handle corner cases properly instead of pretending they don't exist.
switch ((((yintercept >> FRACBITS) == mapy) << 1) | ((xintercept >> FRACBITS) == mapx))
{
case 0: // neither xintercept nor yintercept match!
count = 64; // Stop traversing, because somebody screwed up.
break;
case 1: // xintercept matches
xintercept += xstep;
mapy += mapystep;
if (mapy == yt2)
mapystep = 0;
break;
case 2: // yintercept matches
yintercept += ystep;
mapx += mapxstep;
if (mapx == xt2)
mapxstep = 0;
break;
case 3: // xintercept and yintercept both match
// The trace is exiting a block through its corner. Not only does the block
// being entered need to be checked (which will happen when this loop
// continues), but the other two blocks adjacent to the corner also need to
// be checked.
if (flags & PT_ADDLINES)
{
if (!P_BlockLinesIterator(mapx + mapxstep, mapy, PIT_AddLineIntercepts))
return false; // early out
if (!P_BlockLinesIterator(mapx, mapy + mapystep, PIT_AddLineIntercepts))
return false; // early out
}
else if ((xintercept >> FRACBITS) == mapx)
if (flags & PT_ADDTHINGS)
{
if (!P_BlockThingsIterator(mapx + mapxstep, mapy, PIT_AddThingIntercepts, NULL))
return false; // early out
if (!P_BlockThingsIterator(mapx, mapy + mapystep, PIT_AddThingIntercepts, NULL))
return false; // early out
}
xintercept += xstep;
yintercept += ystep;
mapx += mapxstep;
mapy += mapystep;
if (mapx == xt2)
mapxstep = 0;
if (mapy == yt2)
mapystep = 0;
break;
}
}
// Go through the sorted list
return P_TraverseIntercepts(trav, FRACUNIT);
}
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -61,7 +61,7 @@ extern ffloor_t *openfloorrover, *openceilingrover;
void P_LineOpening(line_t *plinedef, mobj_t *mobj);
boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean(*func)(line_t *));
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *));
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *), mobj_t *thing);
void P_ClearBlockNodes(void);
......@@ -94,7 +94,7 @@ typedef struct bthingit_s
bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2);
mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly);
void P_FreeBlockThingsIterator(bthingit_t *it);
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *));
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *), mobj_t *thing);
#define PT_ADDLINES 1
#define PT_ADDTHINGS 2
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -36,10 +36,11 @@
#include "p_slopes.h"
#include "f_finale.h"
#include "m_cond.h"
#include "simple_hashmap.h"
#include "netcode/net_command.h"
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
consvar_t cv_movebob = CVAR_INIT ("movebob", "0.25", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
actioncache_t actioncachehead;
......@@ -63,7 +64,7 @@ void P_RunCachedActions(void)
var2 = states[ac->statenum].var2;
astate = &states[ac->statenum];
if (ac->mobj && !P_MobjWasRemoved(ac->mobj)) // just in case...
states[ac->statenum].action.acp1(ac->mobj);
states[ac->statenum].action(ac->mobj);
next = ac->next;
Z_Free(ac);
}
......@@ -85,16 +86,26 @@ void P_AddCachedAction(mobj_t *mobj, INT32 statenum)
//
static void P_SetupStateAnimation(mobj_t *mobj, state_t *st)
{
INT32 animlength = (mobj->sprite == SPR_PLAY && mobj->skin)
? (INT32)(((skin_t *)mobj->skin)->sprites[mobj->sprite2].numframes) - 1
: st->var1;
INT32 animlength;
if (mobj->sprite == SPR_PLAY && mobj->skin)
{
spritedef_t *spritedef = P_GetSkinSpritedef(mobj->skin, mobj->sprite2);
animlength = (INT32)(spritedef->numframes) - 1;
}
else
animlength = st->var1;
if (!(st->frame & FF_ANIMATE))
{
mobj->anim_duration = 0;
return;
}
if (animlength <= 0 || st->var2 == 0)
{
mobj->frame &= ~FF_ANIMATE;
mobj->anim_duration = 0;
return; // Crash/stupidity prevention
}
......@@ -138,9 +149,14 @@ FUNCINLINE static ATTRINLINE void P_CycleStateAnimation(mobj_t *mobj)
}
// sprite2 version of above
if (mobj->skin && (((++mobj->frame) & FF_FRAMEMASK) >= (UINT32)(((skin_t *)mobj->skin)->sprites[mobj->sprite2].numframes)))
if (mobj->skin)
{
spritedef_t *spritedef = P_GetSkinSpritedef(mobj->skin, mobj->sprite2);
UINT32 anim_length = (UINT32)(spritedef->numframes);
if (((++mobj->frame) & FF_FRAMEMASK) >= anim_length)
mobj->frame &= ~FF_FRAMEMASK;
}
}
//
// P_CycleMobjState
......@@ -163,24 +179,58 @@ static void P_CycleMobjState(mobj_t *mobj)
}
}
//
// P_CycleMobjState for players.
//
static void P_CyclePlayerMobjState(mobj_t *mobj)
static panim_t GetPlayerAnimationFromState(player_t *player, statenum_t state)
{
// state animations
P_CycleStateAnimation(mobj);
// cycle through states,
// calling action functions at transitions
if (mobj->tics != -1)
switch(P_GetCanonicalPlayerState(player, state))
{
mobj->tics--;
// you can cycle through multiple states in a tic
if (!mobj->tics && mobj->state)
if (!P_SetMobjState(mobj, mobj->state->nextstate))
return; // freed itself
case S_PLAY_STND:
case S_PLAY_WAIT:
case S_PLAY_NIGHTS_STAND:
return PA_IDLE;
case S_PLAY_EDGE:
return PA_EDGE;
case S_PLAY_WALK:
case S_PLAY_SKID:
case S_PLAY_FLOAT:
return PA_WALK;
case S_PLAY_RUN:
case S_PLAY_FLOAT_RUN:
return PA_RUN;
case S_PLAY_DASH:
return PA_DASH;
case S_PLAY_PAIN:
case S_PLAY_STUN:
return PA_PAIN;
case S_PLAY_ROLL:
//case S_PLAY_SPINDASH: -- everyone can ROLL thanks to zoom tubes...
case S_PLAY_NIGHTS_ATTACK:
return PA_ROLL;
case S_PLAY_JUMP:
return PA_JUMP;
case S_PLAY_SPRING:
return PA_SPRING;
case S_PLAY_FALL:
case S_PLAY_NIGHTS_FLOAT:
return PA_FALL;
case S_PLAY_FLY:
case S_PLAY_FLY_TIRED:
case S_PLAY_SWIM:
case S_PLAY_GLIDE:
case S_PLAY_BOUNCE:
case S_PLAY_BOUNCE_LANDING:
case S_PLAY_TWINSPIN:
return PA_ABILITY;
case S_PLAY_SPINDASH: // ...but the act of SPINDASHING is charability2 specific.
case S_PLAY_FIRE:
case S_PLAY_FIRE_FINISH:
case S_PLAY_MELEE:
case S_PLAY_MELEE_FINISH:
case S_PLAY_MELEE_LANDING:
return PA_ABILITY2;
case S_PLAY_RIDE:
return PA_RIDE;
default:
return PA_ETC;
}
}
......@@ -207,6 +257,12 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
I_Error("P_SetPlayerMobjState used for non-player mobj. Use P_SetMobjState instead!\n(Mobj type: %d, State: %d)", mobj->type, state);
#endif
// If the state has been overriden for this skin, use the replacement instead
statenum_t customskinstate;
SIMPLEHASH_FIND_INT(skins[player->skin]->defaulttocustomstate, hashentry_int32_int32_t, state, S_NULL, customskinstate)
if (customskinstate)
state = customskinstate;
// Catch falling for nojumpspin
if ((state == S_PLAY_JUMP) && (player->charflags & SF_NOJUMPSPIN) && (P_MobjFlip(mobj)*mobj->momz < 0))
return P_SetPlayerMobjState(mobj, S_PLAY_FALL);
......@@ -228,88 +284,24 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
{
if (state == S_PLAY_JUMP)
{
if (player->mo->state-states == S_PLAY_WALK)
if (P_IsPlayerInState(player, S_PLAY_WALK))
return P_SetPlayerMobjState(mobj, S_PLAY_FLOAT);
return true;
}
else if (player->mo->state-states == S_PLAY_FLOAT && state == S_PLAY_STND)
else if (P_IsPlayerInState(player, S_PLAY_FLOAT) && state == S_PLAY_STND)
return true;
}
// You were in pain state after taking a hit, and you're moving out of pain state now?
else if (mobj->state == &states[mobj->info->painstate] && player->powers[pw_flashing] == flashingtics && state != mobj->info->painstate)
else if (P_IsPlayerInState(player, S_PLAY_PAIN)
&& player->powers[pw_flashing] == flashingtics
&& P_GetCanonicalPlayerState(player, state) != S_PLAY_PAIN)
{
// Start flashing, since you've landed.
player->powers[pw_flashing] = flashingtics-1;
P_DoPityCheck(player);
}
// Set animation state
// The pflags version of this was just as convoluted.
switch(state)
{
case S_PLAY_STND:
case S_PLAY_WAIT:
case S_PLAY_NIGHTS_STAND:
player->panim = PA_IDLE;
break;
case S_PLAY_EDGE:
player->panim = PA_EDGE;
break;
case S_PLAY_WALK:
case S_PLAY_SKID:
case S_PLAY_FLOAT:
player->panim = PA_WALK;
break;
case S_PLAY_RUN:
case S_PLAY_FLOAT_RUN:
player->panim = PA_RUN;
break;
case S_PLAY_DASH:
player->panim = PA_DASH;
break;
case S_PLAY_PAIN:
case S_PLAY_STUN:
player->panim = PA_PAIN;
break;
case S_PLAY_ROLL:
//case S_PLAY_SPINDASH: -- everyone can ROLL thanks to zoom tubes...
case S_PLAY_NIGHTS_ATTACK:
player->panim = PA_ROLL;
break;
case S_PLAY_JUMP:
player->panim = PA_JUMP;
break;
case S_PLAY_SPRING:
player->panim = PA_SPRING;
break;
case S_PLAY_FALL:
case S_PLAY_NIGHTS_FLOAT:
player->panim = PA_FALL;
break;
case S_PLAY_FLY:
case S_PLAY_FLY_TIRED:
case S_PLAY_SWIM:
case S_PLAY_GLIDE:
case S_PLAY_BOUNCE:
case S_PLAY_BOUNCE_LANDING:
case S_PLAY_TWINSPIN:
player->panim = PA_ABILITY;
break;
case S_PLAY_SPINDASH: // ...but the act of SPINDASHING is charability2 specific.
case S_PLAY_FIRE:
case S_PLAY_FIRE_FINISH:
case S_PLAY_MELEE:
case S_PLAY_MELEE_FINISH:
case S_PLAY_MELEE_LANDING:
player->panim = PA_ABILITY2;
break;
case S_PLAY_RIDE:
player->panim = PA_RIDE;
break;
default:
player->panim = PA_ETC;
break;
}
player->panim = GetPlayerAnimationFromState(player, state);
if (recursion++) // if recursion detected,
memset(seenstate = tempstate, 0, sizeof tempstate); // clear state table
......@@ -329,8 +321,10 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
mobj->state = st;
mobj->tics = st->tics;
// Adjust the player's animation speed to match their velocity.
if (player->panim == PA_EDGE && (player->charflags & SF_FASTEDGE))
// Adjust the player's animation speed
if (state == S_PLAY_WAIT && (player->charflags & SF_FASTWAIT))
mobj->tics = 5;
else if (player->panim == PA_EDGE && (player->charflags & SF_FASTEDGE))
mobj->tics = 2;
else if (!(disableSpeedAdjust || player->charflags & SF_NOSPEEDADJUST))
{
......@@ -395,31 +389,23 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
{
skin_t *skin = ((skin_t *)mobj->skin);
UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
UINT8 numframes, spr2;
UINT8 numframes;
UINT16 spr2;
if (skin)
{
UINT16 stateframe = st->frame;
spr2 = P_GetStateSprite2(st);
// Add/Remove FF_SPR2SUPER based on certain conditions
if (player->charflags & SF_NOSUPERSPRITES || (player->powers[pw_carry] == CR_NIGHTSMODE && (player->charflags & SF_NONIGHTSSUPER)))
stateframe = stateframe & ~FF_SPR2SUPER;
else if (player->powers[pw_super] || (player->powers[pw_carry] == CR_NIGHTSMODE && (player->charflags & SF_SUPER)))
stateframe = stateframe | FF_SPR2SUPER;
// Add or remove SPR2F_SUPER based on certain conditions
spr2 = P_ApplySuperFlagToSprite2(spr2, mobj);
if (stateframe & FF_SPR2SUPER)
{
if (mobj->eflags & MFE_FORCENOSUPER)
stateframe = stateframe & ~FF_SPR2SUPER;
}
else if (mobj->eflags & MFE_FORCESUPER)
stateframe = stateframe | FF_SPR2SUPER;
// Get the needed sprite2 and frame number
spr2 = P_GetSkinSprite2(skin, spr2, mobj->player);
// Get the sprite2 and frame number
spr2 = P_GetSkinSprite2(skin, (stateframe & FF_FRAMEMASK), mobj->player);
numframes = skin->sprites[spr2].numframes;
spritedef_t *sprdef = P_GetSkinSpritedef(skin, spr2);
numframes = sprdef->numframes;
if (state == S_PLAY_STND && (spr2 & FF_SPR2SUPER) && skin->sprites[SPR2_WAIT|FF_SPR2SUPER].numframes == 0)
if (state == S_PLAY_STND && (spr2 & SPR2F_SUPER) && sprdef[SPR2_WAIT].numframes == 0)
mobj->tics = -1; // If no super wait, don't wait at all
}
else
......@@ -432,15 +418,22 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
if (mobj->sprite != SPR_PLAY)
{
mobj->sprite = SPR_PLAY;
frame = 0;
frame = P_GetSprite2StateFrame(st);
}
else if (mobj->sprite2 != spr2)
{
if ((st->frame & FF_SPR2MIDSTART) && numframes && P_RandomChance(FRACUNIT/2))
if (st->frame & FF_SPR2MIDSTART)
{
if (numframes && P_RandomChance(FRACUNIT/2))
frame = numframes/2;
else
frame = 0;
}
else if (numframes)
frame = P_GetSprite2StateFrame(st) % numframes;
else
frame = 0;
}
if (frame >= numframes)
{
......@@ -452,6 +445,7 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
{
if (mobj->frame & FF_FRAMEMASK)
mobj->frame--;
return P_SetPlayerMobjState(mobj, st->var1);
}
}
......@@ -476,12 +470,12 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
// Modified handling.
// Call action functions when the state is set
if (st->action.acp1)
if (st->action)
{
var1 = st->var1;
var2 = st->var2;
astate = st;
st->action.acp1(mobj);
st->action(mobj);
// woah. a player was removed by an action.
// this sounds like a VERY BAD THING, but there's nothing we can do now...
......@@ -539,26 +533,23 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
{
skin_t *skin = ((skin_t *)mobj->skin);
UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
UINT8 numframes, spr2;
UINT8 numframes;
UINT16 spr2;
if (skin)
{
UINT16 stateframe = st->frame;
spr2 = P_GetStateSprite2(st);
// Add/Remove FF_SPR2SUPER based on certain conditions
if (stateframe & FF_SPR2SUPER)
{
if (mobj->eflags & MFE_FORCENOSUPER)
stateframe = stateframe & ~FF_SPR2SUPER;
}
else if (mobj->eflags & MFE_FORCESUPER)
stateframe = stateframe | FF_SPR2SUPER;
// Add or remove SPR2F_SUPER based on certain conditions
spr2 = P_ApplySuperFlagToSprite2(spr2, mobj);
// Get the needed sprite2 and frame number
spr2 = P_GetSkinSprite2(skin, spr2, NULL);
// Get the sprite2 and frame number
spr2 = P_GetSkinSprite2(skin, (stateframe & FF_FRAMEMASK), NULL);
numframes = skin->sprites[spr2].numframes;
spritedef_t *sprdef = P_GetSkinSpritedef(skin, spr2);
numframes = sprdef->numframes;
if (state == S_PLAY_STND && (spr2 & FF_SPR2SUPER) && skin->sprites[SPR2_WAIT|FF_SPR2SUPER].numframes == 0)
if (state == S_PLAY_STND && (spr2 & SPR2F_SUPER) && sprdef[SPR2_WAIT].numframes == 0)
mobj->tics = -1; // If no super wait, don't wait at all
}
else
......@@ -571,15 +562,22 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
if (mobj->sprite != SPR_PLAY)
{
mobj->sprite = SPR_PLAY;
frame = 0;
frame = P_GetSprite2StateFrame(st);
}
else if (mobj->sprite2 != spr2)
{
if ((st->frame & FF_SPR2MIDSTART) && numframes && P_RandomChance(FRACUNIT/2))
if (st->frame & FF_SPR2MIDSTART)
{
if (numframes && P_RandomChance(FRACUNIT/2))
frame = numframes/2;
else
frame = 0;
}
else if (numframes)
frame = P_GetSprite2StateFrame(st) % numframes;
else
frame = 0;
}
if (frame >= numframes)
{
......@@ -591,6 +589,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
{
if (mobj->frame & FF_FRAMEMASK)
mobj->frame--;
return P_SetMobjState(mobj, st->var1);
}
}
......@@ -613,12 +612,12 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
// Modified handling.
// Call action functions when the state is set
if (st->action.acp1)
if (st->action)
{
var1 = st->var1;
var2 = st->var2;
astate = st;
st->action.acp1(mobj);
st->action(mobj);
if (P_MobjWasRemoved(mobj))
return false;
}
......@@ -749,7 +748,7 @@ void P_EmeraldManager(void)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (think->removing)
continue;
mo = (mobj_t *)think;
......@@ -917,38 +916,38 @@ void P_ExplodeMissile(mobj_t *mo)
explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE);
if (!P_MobjWasRemoved(explodemo))
{
P_SetScale(explodemo, mo->scale);
P_SetScale(explodemo, mo->scale, true);
explodemo->destscale = mo->destscale;
explodemo->momx += (P_RandomByte() % 32) * FixedMul(FRACUNIT/8, explodemo->scale);
explodemo->momy += (P_RandomByte() % 32) * FixedMul(FRACUNIT/8, explodemo->scale);
S_StartSound(explodemo, sfx_pop);
S_StartSoundFromMobj(explodemo, sfx_pop);
}
explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE);
if (!P_MobjWasRemoved(explodemo))
{
P_SetScale(explodemo, mo->scale);
P_SetScale(explodemo, mo->scale, true);
explodemo->destscale = mo->destscale;
explodemo->momx += (P_RandomByte() % 64) * FixedMul(FRACUNIT/8, explodemo->scale);
explodemo->momy -= (P_RandomByte() % 64) * FixedMul(FRACUNIT/8, explodemo->scale);
S_StartSound(explodemo, sfx_dmpain);
S_StartSoundFromMobj(explodemo, sfx_dmpain);
}
explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE);
if (!P_MobjWasRemoved(explodemo))
{
P_SetScale(explodemo, mo->scale);
P_SetScale(explodemo, mo->scale, true);
explodemo->destscale = mo->destscale;
explodemo->momx -= (P_RandomByte() % 128) * FixedMul(FRACUNIT/8, explodemo->scale);
explodemo->momy += (P_RandomByte() % 128) * FixedMul(FRACUNIT/8, explodemo->scale);
S_StartSound(explodemo, sfx_pop);
S_StartSoundFromMobj(explodemo, sfx_pop);
}
explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE);
if (!P_MobjWasRemoved(explodemo))
{
P_SetScale(explodemo, mo->scale);
P_SetScale(explodemo, mo->scale, true);
explodemo->destscale = mo->destscale;
explodemo->momx -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale);
explodemo->momy -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale);
S_StartSound(explodemo, sfx_cybdth);
S_StartSoundFromMobj(explodemo, sfx_cybdth);
}
}
......@@ -958,7 +957,7 @@ void P_ExplodeMissile(mobj_t *mo)
mo->flags |= MF_NOCLIPTHING; // Dummy flag to indicate that this was already called.
if (mo->info->deathsound && !(mo->flags2 & MF2_DEBRIS))
S_StartSound(mo, mo->info->deathsound);
S_StartSoundFromMobj(mo, mo->info->deathsound);
P_SetMobjState(mo, mo->info->deathstate);
}
......@@ -1092,9 +1091,8 @@ static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, line_t *line,
);
}
fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect)
fixed_t P_MobjFloorZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect)
{
I_Assert(mobj != NULL);
I_Assert(sector != NULL);
if (sector->f_slope) {
......@@ -1103,14 +1101,14 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t
// Get the corner of the object that should be the highest on the slope
if (slope->d.x < 0)
testx = mobj->radius;
testx = radius;
else
testx = -mobj->radius;
testx = -radius;
if (slope->d.y < 0)
testy = mobj->radius;
testy = radius;
else
testy = -mobj->radius;
testy = -radius;
if ((slope->zdelta > 0) ^ !!(lowest)) {
testx = -testx;
......@@ -1125,7 +1123,7 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t
return P_GetSlopeZAt(slope, testx, testy);
// If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point
if (perfect) {
if (perfect && boundsec) {
size_t i;
line_t *ld;
fixed_t bbox[4];
......@@ -1136,10 +1134,10 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t
else
finalheight = INT32_MIN;
bbox[BOXLEFT] = x-mobj->radius;
bbox[BOXRIGHT] = x+mobj->radius;
bbox[BOXTOP] = y+mobj->radius;
bbox[BOXBOTTOM] = y-mobj->radius;
bbox[BOXLEFT] = x-radius;
bbox[BOXRIGHT] = x+radius;
bbox[BOXTOP] = y+radius;
bbox[BOXBOTTOM] = y-radius;
for (i = 0; i < boundsec->linecount; i++) {
ld = boundsec->lines[i];
......@@ -1151,9 +1149,9 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t
continue;
if (lowest)
finalheight = min(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, true));
finalheight = min(finalheight, HighestOnLine(radius, x, y, ld, slope, true));
else
finalheight = max(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, false));
finalheight = max(finalheight, HighestOnLine(radius, x, y, ld, slope, false));
}
return finalheight;
......@@ -1164,14 +1162,13 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t
if (line == NULL)
return P_GetSlopeZAt(slope, x, y);
return HighestOnLine(mobj->radius, x, y, line, slope, lowest);
return HighestOnLine(radius, x, y, line, slope, lowest);
} else // Well, that makes it easy. Just get the floor height
return sector->floorheight;
}
fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect)
fixed_t P_MobjCeilingZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect)
{
I_Assert(mobj != NULL);
I_Assert(sector != NULL);
if (sector->c_slope) {
......@@ -1180,14 +1177,14 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed
// Get the corner of the object that should be the highest on the slope
if (slope->d.x < 0)
testx = mobj->radius;
testx = radius;
else
testx = -mobj->radius;
testx = -radius;
if (slope->d.y < 0)
testy = mobj->radius;
testy = radius;
else
testy = -mobj->radius;
testy = -radius;
if ((slope->zdelta > 0) ^ !!(lowest)) {
testx = -testx;
......@@ -1202,7 +1199,7 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed
return P_GetSlopeZAt(slope, testx, testy);
// If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point
if (perfect) {
if (perfect && boundsec) {
size_t i;
line_t *ld;
fixed_t bbox[4];
......@@ -1213,10 +1210,10 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed
else
finalheight = INT32_MIN;
bbox[BOXLEFT] = x-mobj->radius;
bbox[BOXRIGHT] = x+mobj->radius;
bbox[BOXTOP] = y+mobj->radius;
bbox[BOXBOTTOM] = y-mobj->radius;
bbox[BOXLEFT] = x-radius;
bbox[BOXRIGHT] = x+radius;
bbox[BOXTOP] = y+radius;
bbox[BOXBOTTOM] = y-radius;
for (i = 0; i < boundsec->linecount; i++) {
ld = boundsec->lines[i];
......@@ -1228,9 +1225,9 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed
continue;
if (lowest)
finalheight = min(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, true));
finalheight = min(finalheight, HighestOnLine(radius, x, y, ld, slope, true));
else
finalheight = max(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, false));
finalheight = max(finalheight, HighestOnLine(radius, x, y, ld, slope, false));
}
return finalheight;
......@@ -1241,165 +1238,11 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed
if (line == NULL)
return P_GetSlopeZAt(slope, x, y);
return HighestOnLine(mobj->radius, x, y, line, slope, lowest);
return HighestOnLine(radius, x, y, line, slope, lowest);
} else // Well, that makes it easy. Just get the ceiling height
return sector->ceilingheight;
}
// Now do the same as all above, but for cameras because apparently cameras are special?
fixed_t P_CameraFloorZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect)
{
I_Assert(mobj != NULL);
I_Assert(sector != NULL);
if (sector->f_slope) {
fixed_t testx, testy;
pslope_t *slope = sector->f_slope;
// Get the corner of the object that should be the highest on the slope
if (slope->d.x < 0)
testx = mobj->radius;
else
testx = -mobj->radius;
if (slope->d.y < 0)
testy = mobj->radius;
else
testy = -mobj->radius;
if ((slope->zdelta > 0) ^ !!(lowest)) {
testx = -testx;
testy = -testy;
}
testx += x;
testy += y;
// If the highest point is in the sector, then we have it easy! Just get the Z at that point
if (R_IsPointInSector(boundsec ? boundsec : sector, testx, testy))
return P_GetSlopeZAt(slope, testx, testy);
// If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point
if (perfect) {
size_t i;
line_t *ld;
fixed_t bbox[4];
fixed_t finalheight;
if (lowest)
finalheight = INT32_MAX;
else
finalheight = INT32_MIN;
bbox[BOXLEFT] = x-mobj->radius;
bbox[BOXRIGHT] = x+mobj->radius;
bbox[BOXTOP] = y+mobj->radius;
bbox[BOXBOTTOM] = y-mobj->radius;
for (i = 0; i < boundsec->linecount; i++) {
ld = boundsec->lines[i];
if (bbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || bbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
|| bbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || bbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
continue;
if (P_BoxOnLineSide(bbox, ld) != -1)
continue;
if (lowest)
finalheight = min(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, true));
else
finalheight = max(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, false));
}
return finalheight;
}
// If we're just testing for base sector location (no collision line), just go for the center's spot...
// It'll get fixed when we test for collision anyway, and the final result can't be lower than this
if (line == NULL)
return P_GetSlopeZAt(slope, x, y);
return HighestOnLine(mobj->radius, x, y, line, slope, lowest);
} else // Well, that makes it easy. Just get the floor height
return sector->floorheight;
}
fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect)
{
I_Assert(mobj != NULL);
I_Assert(sector != NULL);
if (sector->c_slope) {
fixed_t testx, testy;
pslope_t *slope = sector->c_slope;
// Get the corner of the object that should be the highest on the slope
if (slope->d.x < 0)
testx = mobj->radius;
else
testx = -mobj->radius;
if (slope->d.y < 0)
testy = mobj->radius;
else
testy = -mobj->radius;
if ((slope->zdelta > 0) ^ !!(lowest)) {
testx = -testx;
testy = -testy;
}
testx += x;
testy += y;
// If the highest point is in the sector, then we have it easy! Just get the Z at that point
if (R_IsPointInSector(boundsec ? boundsec : sector, testx, testy))
return P_GetSlopeZAt(slope, testx, testy);
// If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point
if (perfect) {
size_t i;
line_t *ld;
fixed_t bbox[4];
fixed_t finalheight;
if (lowest)
finalheight = INT32_MAX;
else
finalheight = INT32_MIN;
bbox[BOXLEFT] = x-mobj->radius;
bbox[BOXRIGHT] = x+mobj->radius;
bbox[BOXTOP] = y+mobj->radius;
bbox[BOXBOTTOM] = y-mobj->radius;
for (i = 0; i < boundsec->linecount; i++) {
ld = boundsec->lines[i];
if (bbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || bbox[BOXLEFT] >= ld->bbox[BOXRIGHT]
|| bbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || bbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
continue;
if (P_BoxOnLineSide(bbox, ld) != -1)
continue;
if (lowest)
finalheight = min(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, true));
else
finalheight = max(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, false));
}
return finalheight;
}
// If we're just testing for base sector location (no collision line), just go for the center's spot...
// It'll get fixed when we test for collision anyway, and the final result can't be lower than this
if (line == NULL)
return P_GetSlopeZAt(slope, x, y);
return HighestOnLine(mobj->radius, x, y, line, slope, lowest);
} else // Well, that makes it easy. Just get the ceiling height
return sector->ceilingheight;
}
static void P_PlayerFlip(mobj_t *mo)
{
if (!mo->player)
......@@ -1525,6 +1368,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
case MT_WATERDROP:
case MT_CYBRAKDEMON:
gravityadd >>= 1;
break;
default:
break;
}
......@@ -1892,7 +1736,7 @@ void P_XYMovement(mobj_t *mo)
{
P_BounceMove(mo);
xmove = ymove = 0;
S_StartSound(mo, mo->info->activesound);
S_StartSoundFromMobj(mo, mo->info->activesound);
// Bounce ring algorithm
if (mo->type == MT_THROWNBOUNCE)
......@@ -1918,7 +1762,7 @@ void P_XYMovement(mobj_t *mo)
}
else if (mo->flags & MF_STICKY)
{
S_StartSound(mo, mo->info->activesound);
S_StartSoundFromMobj(mo, mo->info->activesound);
mo->momx = mo->momy = mo->momz = 0; //Full stop!
mo->flags |= MF_NOGRAVITY; //Stay there!
mo->flags &= ~MF_STICKY; //Don't check again!
......@@ -2331,8 +2175,8 @@ boolean P_CheckDeathPitCollide(mobj_t *mo)
if (mo->player && mo->player->pflags & PF_GODMODE)
return false;
fixed_t sectorFloor = P_GetSectorFloorZAt(mo->subsector->sector, mo->x, mo->y);
fixed_t sectorCeiling = P_GetSectorCeilingZAt(mo->subsector->sector, mo->x, mo->y);
fixed_t sectorFloor = P_GetSpecialBottomZ(mo, mo->subsector->sector, mo->subsector->sector);
fixed_t sectorCeiling = P_GetSpecialTopZ(mo, mo->subsector->sector, mo->subsector->sector);
if (((mo->z <= sectorFloor
&& ((mo->subsector->sector->flags & MSF_TRIGGERSPECIAL_HEADBUMP) || !(mo->eflags & MFE_VERTICALFLIP)) && (mo->subsector->sector->flags & MSF_FLIPSPECIAL_FLOOR))
......@@ -2442,7 +2286,7 @@ boolean P_ZMovement(mobj_t *mo)
{
mo->momz = -mo->momz;
mo->z += mo->momz;
S_StartSound(mo, mo->info->activesound);
S_StartSoundFromMobj(mo, mo->info->activesound);
mo->threshold++;
// Be sure to change the XY one too if you change this.
......@@ -2468,7 +2312,7 @@ boolean P_ZMovement(mobj_t *mo)
mo->momx = mo->momy = mo->momz = 0;
mo->z = mo->floorz;
if (mo->info->painsound)
S_StartSound(mo, mo->info->painsound);
S_StartSoundFromMobj(mo, mo->info->painsound);
}
break;
case MT_RING: // Ignore still rings
......@@ -2607,7 +2451,7 @@ boolean P_ZMovement(mobj_t *mo)
// Otherwise bounce up at half speed.
else
mom.z = -mom.z/2;
S_StartSound(mo, mo->info->activesound);
S_StartSoundFromMobj(mo, mo->info->activesound);
}
}
// Hack over. Back to your regularly scheduled detonation. -SH
......@@ -2692,7 +2536,7 @@ boolean P_ZMovement(mobj_t *mo)
else if (mo->type == MT_FALLINGROCK)
{
if (P_MobjFlip(mo)*mom.z > FixedMul(2*FRACUNIT, mo->scale))
S_StartSound(mo, mo->info->activesound + P_RandomKey(mo->info->reactiontime));
S_StartSoundFromMobj(mo, mo->info->activesound + P_RandomKey(mo->info->reactiontime));
mom.z /= 2; // Rocks not so bouncy
......@@ -2756,7 +2600,7 @@ boolean P_ZMovement(mobj_t *mo)
if (P_MobjFlip(mo)*mo->momz >= 0)
{
mo->momz = -mo->momz;
S_StartSound(mo, mo->info->activesound);
S_StartSoundFromMobj(mo, mo->info->activesound);
}
}
else
......@@ -3054,7 +2898,7 @@ nightsdone:
// hit the ceiling
if (mariomode)
S_StartSound(mo, sfx_mario1);
S_StartSoundFromMobj(mo, sfx_mario1);
if (!mo->player->climbing)
mo->momz = 0;
......@@ -3113,12 +2957,11 @@ boolean P_SceneryZMovement(mobj_t *mo)
continue;
explodemo->momx += ((prandom & 0x0F) << (FRACBITS-2)) * (i & 2 ? -1 : 1);
explodemo->momy += ((prandom & 0xF0) << (FRACBITS-6)) * (i & 1 ? -1 : 1);
explodemo->destscale = mo->scale;
P_SetScale(explodemo, mo->scale);
P_SetScale(explodemo, mo->scale, true);
}
if (mo->threshold != 42) // Don't make pop sound if threshold is 42.
S_StartSound(explodemo, sfx_bubbl1 + P_RandomKey(5));
S_StartSoundFromMobj(explodemo, sfx_bubbl1 + P_RandomKey(5));
//note that we assign the bubble sound to one of the new bubbles.
// in other words, IT ACTUALLY GETS USED YAAAAAAAY
......@@ -3140,7 +2983,7 @@ boolean P_SceneryZMovement(mobj_t *mo)
mobj_t *flower = P_SpawnMobjFromMobj(mo, 0, 0, 0, flowertype);
if (flower)
{
P_SetScale(flower, mo->scale/16);
P_SetScale(flower, mo->scale/16, true);
flower->destscale = mo->scale;
flower->scalespeed = mo->scale/8;
}
......@@ -3380,10 +3223,7 @@ void P_MobjCheckWater(mobj_t *mobj)
else
splish = P_SpawnMobj(mobj->x, mobj->y, mobj->watertop, splishtype);
if (!P_MobjWasRemoved(splish))
{
splish->destscale = mobj->scale;
P_SetScale(splish, mobj->scale);
}
P_SetScale(splish, mobj->scale, true);
}
// skipping stone!
......@@ -3422,10 +3262,7 @@ void P_MobjCheckWater(mobj_t *mobj)
else
splish = P_SpawnMobj(mobj->x, mobj->y, mobj->watertop, splishtype);
if (!P_MobjWasRemoved(splish))
{
splish->destscale = mobj->scale;
P_SetScale(splish, mobj->scale);
}
P_SetScale(splish, mobj->scale, true);
}
}
......@@ -3438,11 +3275,11 @@ void P_MobjCheckWater(mobj_t *mobj)
mobjtype_t bubbletype;
if (mobj->eflags & MFE_GOOWATER || wasingoo)
S_StartSound(mobj, sfx_ghit);
S_StartSoundFromMobj(mobj, sfx_ghit);
else if (mobj->eflags & MFE_TOUCHLAVA)
S_StartSound(mobj, sfx_splash);
S_StartSoundFromMobj(mobj, sfx_splash);
else
S_StartSound(mobj, sfx_splish); // And make a sound!
S_StartSoundFromMobj(mobj, sfx_splish); // And make a sound!
bubblecount = FixedDiv(abs(mobj->momz), mobj->scale)>>(FRACBITS-1);
// Max bubble count
......@@ -3476,8 +3313,7 @@ void P_MobjCheckWater(mobj_t *mobj)
else
bubble->momz = 0;
bubble->destscale = mobj->scale;
P_SetScale(bubble, mobj->scale);
P_SetScale(bubble, mobj->scale, true);
}
}
}
......@@ -3602,7 +3438,7 @@ void P_DestroyRobots(void)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (think->removing)
continue;
mo = (mobj_t *)think;
......@@ -3675,7 +3511,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
if (!P_TryCameraMove(thiscam->x + thiscam->momx, thiscam->y + thiscam->momy, thiscam)) // Thanks for the greatly improved camera, Lach -- Sev
{ // Never fails for 2D mode.
mobj_t dummy;
dummy.thinker.function.acp1 = (actionf_p1)P_MobjThinker;
dummy.thinker.function = (actionf_p1)P_MobjThinker;
dummy.subsector = thiscam->subsector;
dummy.x = thiscam->x;
dummy.y = thiscam->y;
......@@ -3879,7 +3715,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
// momentum movement
mobj->eflags &= ~MFE_JUSTSTEPPEDDOWN;
if (mobj->state-states == S_PLAY_BOUNCE_LANDING)
if (P_IsPlayerInState(mobj->player, S_PLAY_BOUNCE_LANDING))
goto animonly; // no need for checkposition - doesn't move at ALL
// Zoom tube
......@@ -3929,7 +3765,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
// always do the gravity bit now, that's simpler
// BUT CheckPosition only if wasn't done before.
if (!(mobj->eflags & MFE_ONGROUND) || mobj->momz
if (mobj->momz
|| ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z + mobj->height != mobj->ceilingz)
|| (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z != mobj->floorz)
|| P_IsObjectInGoop(mobj))
......@@ -3942,22 +3778,11 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
}
else
{
#if 0 // i don't know why this is here, it's causing a few undesired state glitches, and disabling it doesn't appear to negatively affect the game, but i don't want it gone permanently just in case some obscure bug crops up
if (!(mobj->player->powers[pw_carry] == CR_NIGHTSMODE)) // used for drilling
mobj->player->pflags &= ~PF_STARTJUMP;
mobj->player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
if (mobj->player->secondjump || mobj->player->powers[pw_tailsfly])
{
mobj->player->secondjump = 0;
mobj->player->powers[pw_tailsfly] = 0;
P_SetMobjState(mobj, S_PLAY_WALK);
}
#endif
mobj->eflags &= ~MFE_JUSTHITFLOOR;
}
animonly:
P_CyclePlayerMobjState(mobj);
P_CycleMobjState(mobj);
}
static void CalculatePrecipFloor(precipmobj_t *mobj)
......@@ -4239,7 +4064,7 @@ static void P_GenericBossThinker(mobj_t *mobj)
// look for a new target
if (P_BossTargetPlayer(mobj, false) && mobj->info->seesound)
S_StartSound(mobj, mobj->info->seesound);
S_StartSoundFromMobj(mobj, mobj->info->seesound);
return;
}
......@@ -4279,7 +4104,7 @@ static void P_Boss1Thinker(mobj_t *mobj)
// look for a new target
if (P_BossTargetPlayer(mobj, false) && mobj->info->seesound)
S_StartSound(mobj, mobj->info->seesound);
S_StartSoundFromMobj(mobj, mobj->info->seesound);
return;
}
......@@ -4326,7 +4151,7 @@ static void P_Boss2Thinker(mobj_t *mobj)
// look for a new target
if (P_BossTargetPlayer(mobj, false) && mobj->info->seesound)
S_StartSound(mobj, mobj->info->seesound);
S_StartSoundFromMobj(mobj, mobj->info->seesound);
return;
}
......@@ -4412,7 +4237,7 @@ static void P_Boss3Thinker(mobj_t *mobj)
// this can happen if the boss was hurt earlier than expected
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -4610,7 +4435,7 @@ static void P_Boss3Thinker(mobj_t *mobj)
sprev = shock;
}
if (!P_MobjWasRemoved(shock))
S_StartSound(mobj, shock->info->seesound);
S_StartSoundFromMobj(mobj, shock->info->seesound);
// look for a new target
P_BossTargetPlayer(mobj, true);
......@@ -4834,7 +4659,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
mobj->movecount %= 360*FRACUNIT;
if (((oldmovecount>>FRACBITS)%120 >= 60) && !((mobj->movecount>>FRACBITS)%120 >= 60))
S_StartSound(NULL, sfx_mswing);
S_StartSoundFromEverywhere(sfx_mswing);
}
// movedir == battle stage:
......@@ -4921,7 +4746,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
{
mobj->momz = mobj->movefactor = 0;
mobj->threshold = 1110<<FRACBITS;
S_StartSound(NULL, sfx_s3k60);
S_StartSoundFromEverywhere(sfx_s3k60);
mobj->movedir++;
}
......@@ -5147,8 +4972,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
mobj_t *smoke = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height, MT_SMOKE);
if (!P_MobjWasRemoved(smoke))
{
smoke->destscale = mobj->destscale;
P_SetScale(smoke, smoke->destscale);
P_SetScale(smoke, mobj->destscale, true);
smoke->momz = FixedMul(FRACUNIT, smoke->scale);
}
}
......@@ -5170,7 +4994,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
if (mobj->health > 0)
mobj->health--;
S_StartSound(0, (mobj->health) ? sfx_behurt : sfx_bedie2);
S_StartSoundFromEverywhere((mobj->health) ? sfx_behurt : sfx_bedie2);
mobj->reactiontime /= 3;
......@@ -5210,7 +5034,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
INT32 i;
mobj->state->nextstate = mobj->info->painstate; // Reset
S_StartSound(0, sfx_bedeen);
S_StartSoundFromEverywhere(sfx_bedeen);
for (i = 0; i < MAXPLAYERS; i++)
{
......@@ -5234,7 +5058,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
mobj->state->nextstate = mobj->info->spawnstate;
// Laugh
S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
S_StartSoundFromEverywhere(sfx_bewar1 + P_RandomKey(4));
}
}
}
......@@ -5254,7 +5078,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
var2 = 2*TICRATE + (80<<16);
A_LobShot(mobj);
S_StartSound(0, sfx_begoop);
S_StartSoundFromEverywhere(sfx_begoop);
}
}
else if (mobj->state == &states[S_BLACKEGG_SHOOT2])
......@@ -5278,7 +5102,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
S_StopSound(missile);
if (leveltime & 1)
S_StartSound(0, sfx_beshot);
S_StartSoundFromEverywhere(sfx_beshot);
}
else if (mobj->state == &states[S_BLACKEGG_JUMP1] && mobj->tics == 1)
{
......@@ -5411,7 +5235,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
fixed_t x,y,z;
mobj_t *mo2;
S_StartSound(0, sfx_befall);
S_StartSoundFromEverywhere(sfx_befall);
z = mobj->floorz;
for (j = 0; j < 2; j++)
......@@ -5457,13 +5281,13 @@ static void P_Boss7Thinker(mobj_t *mobj)
P_DamageMobj(players[i].mo, mobj, mobj, 1, 0);
// Laugh
S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
S_StartSoundFromEverywhere(sfx_bewar1 + P_RandomKey(4));
}
P_SetMobjState(mobj, mobj->info->spawnstate);
}
else if (mobj->state == &states[mobj->info->deathstate] && mobj->tics == mobj->state->tics)
S_StartSound(0, sfx_bedie1 + (P_RandomFixed() & 1));
S_StartSoundFromEverywhere(sfx_bedie1 + (P_RandomFixed() & 1));
}
......@@ -5472,7 +5296,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
mobj->movedir = InvAngle(mobj->movedir);\
mobj->threshold = 6 + (FixedMul(24<<FRACBITS, FixedDiv((mobj->info->spawnhealth - mobj->health)<<FRACBITS, (mobj->info->spawnhealth-1)<<FRACBITS))>>FRACBITS);\
if (mobj->info->activesound)\
S_StartSound(mobj, mobj->info->activesound);\
S_StartSoundFromMobj(mobj, mobj->info->activesound);\
if (mobj->info->painchance)\
P_SetMobjState(mobj, mobj->info->painchance);\
mobj->flags2 &= ~MF2_INVERTAIMABLE;\
......@@ -5502,7 +5326,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
// Build a hoop linked list of 'em!
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -5599,7 +5423,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
if (mobj->hprev)
{
mobj->hprev->destscale = FRACUNIT + (2*TICRATE - mobj->fuse)*(FRACUNIT/2)/TICRATE + FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT),FRACUNIT/2);
P_SetScale(mobj->hprev, mobj->hprev->destscale);
P_SetScale(mobj->hprev, mobj->hprev->destscale, false);
P_MoveOrigin(mobj->hprev, mobj->x, mobj->y, mobj->z + mobj->height/2 - mobj->hprev->height/2);
mobj->hprev->momx = mobj->momx;
......@@ -5625,8 +5449,8 @@ static void P_Boss9Thinker(mobj_t *mobj)
{
S_StopSound(missile);
if (mobj->extravalue1 >= 2)
P_SetScale(missile, FRACUNIT>>1);
missile->destscale = missile->scale>>1;
P_SetScale(missile, FRACUNIT/2, true);
missile->destscale = missile->scale/2;
missile->fuse = TICRATE/2;
missile->scalespeed = abs(missile->destscale - missile->scale)/missile->fuse;
missile->z -= missile->height/2;
......@@ -5649,7 +5473,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
spread->angle = missile->angle+(ANGLE_11hh/2)*(i-2);
P_InstaThrust(spread,spread->angle,-spread->info->speed);
spread->momz = missile->momz;
P_SetScale(spread, missile->scale);
P_SetScale(spread, missile->scale, true);
spread->destscale = missile->destscale;
spread->scalespeed = missile->scalespeed;
spread->fuse = missile->fuse;
......@@ -5673,7 +5497,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
spread = P_SpawnMissile(mobj, mobj->target, missile->type);
if (P_MobjWasRemoved(spread))
continue;
P_SetScale(spread, missile->scale);
P_SetScale(spread, missile->scale, true);
spread->destscale = missile->destscale;
spread->fuse = missile->fuse;
spread->z -= spread->height/2;
......@@ -5697,7 +5521,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
missile->z -= missile->fuse*missile->momz;
P_SetThingPosition(missile);
S_StartSound(mobj, sfx_s3kb3);
S_StartSoundFromMobj(mobj, sfx_s3kb3);
}
}
}
......@@ -5730,12 +5554,12 @@ static void P_Boss9Thinker(mobj_t *mobj)
{
if (mobj->health > mobj->info->damage)
{
P_SetScale(missile, FRACUNIT/3);
P_SetScale(missile, FRACUNIT/3, true);
missile->color = SKINCOLOR_MAGENTA; // sonic OVA/4 purple power
}
else
{
P_SetScale(missile, FRACUNIT/5);
P_SetScale(missile, FRACUNIT/5, true);
missile->color = SKINCOLOR_SUNSET; // sonic cd electric power
}
missile->destscale = missile->scale*2;
......@@ -5765,7 +5589,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
if (!(mobj->threshold%4)) {
mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4);
if (!mobj->reactiontime)
S_StartSound(mobj, sfx_zoom); // zoom!
S_StartSoundFromMobj(mobj, sfx_zoom); // zoom!
}
}
// else -- Pausing between energy ball shots
......@@ -5786,7 +5610,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
{
mobj_t *missile;
if (mobj->info->seesound)
S_StartSound(mobj, mobj->info->seesound);
S_StartSoundFromMobj(mobj, mobj->info->seesound);
P_SetMobjState(mobj, mobj->info->missilestate);
if (mobj->extravalue1 == 3)
mobj->reactiontime = TICRATE/16;
......@@ -5798,10 +5622,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
if (!P_MobjWasRemoved(missile))
{
if (mobj->extravalue1 >= 2)
{
missile->destscale = FRACUNIT>>1;
P_SetScale(missile, missile->destscale);
}
P_SetScale(missile, FRACUNIT/2, true);
missile->fuse = 3*TICRATE;
missile->z -= missile->height/2;
......@@ -5820,8 +5641,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
spread->angle = missile->angle+(ANGLE_11hh/2)*(i-2);
P_InstaThrust(spread,spread->angle,spread->info->speed);
spread->momz = missile->momz;
spread->destscale = FRACUNIT>>1;
P_SetScale(spread, spread->destscale);
P_SetScale(spread, FRACUNIT/2, true);
spread->fuse = missile->fuse;
}
P_InstaThrust(missile,missile->angle,missile->info->speed);
......@@ -5838,8 +5658,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
spread = P_SpawnMissile(mobj, mobj->target, missile->type);
if (!P_MobjWasRemoved(spread))
{
spread->destscale = FRACUNIT>>1;
P_SetScale(spread, spread->destscale);
P_SetScale(spread, FRACUNIT/2, true);
spread->fuse = missile->fuse;
spread->z -= spread->height/2;
}
......@@ -5879,7 +5698,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
return;
mobj->threshold--;
if (!mobj->threshold) { // failed bounce!
S_StartSound(mobj, sfx_mspogo);
S_StartSoundFromMobj(mobj, sfx_mspogo);
P_BounceMove(mobj);
mobj->angle = R_PointToAngle2(mobj->momx, mobj->momy,0,0);
mobj->momz = 4*FRACUNIT;
......@@ -5892,13 +5711,13 @@ static void P_Boss9Thinker(mobj_t *mobj)
else if (!(mobj->threshold%4))
{ // We've decided to lock onto the player this bounce.
P_SetMobjState(mobj, mobj->state->nextstate);
S_StartSound(mobj, sfx_s3k5a);
S_StartSoundFromMobj(mobj, sfx_s3k5a);
mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4);
mobj->reactiontime = TICRATE - 5*(mobj->info->damage - mobj->health); // targetting time
}
else
{ // No homing, just use P_BounceMove
S_StartSound(mobj, sfx_s3kaa); // make the bounces distinct...
S_StartSoundFromMobj(mobj, sfx_s3kaa); // make the bounces distinct...
P_BounceMove(mobj);
mobj->angle = R_PointToAngle2(0,0,mobj->momx,mobj->momy);
mobj->reactiontime = 1; // TICRATE/4; // just a pause before you bounce away
......@@ -5919,7 +5738,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
{
if (P_MobjWasRemoved(mobj))
return;
S_StartSound(mobj, sfx_mspogo);
S_StartSoundFromMobj(mobj, sfx_mspogo);
P_BounceMove(mobj);
mobj->angle = R_PointToAngle2(mobj->momx, mobj->momy,0,0);
}
......@@ -5997,7 +5816,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
default:
// Fly up and prepare for an attack!
// We have to charge up first, so let's go up into the air
S_StartSound(mobj, sfx_beflap);
S_StartSoundFromMobj(mobj, sfx_beflap);
P_SetMobjState(mobj, mobj->info->raisestate);
if (mobj->floorz >= mobj->target->floorz)
mobj->watertop = mobj->floorz + 256*FRACUNIT;
......@@ -6055,7 +5874,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
mobj->fuse = 3*TICRATE;
mobj->flags |= MF_PAIN;
if (mobj->info->attacksound)
S_StartSound(mobj, mobj->info->attacksound);
S_StartSoundFromMobj(mobj, mobj->info->attacksound);
A_FaceTarget(mobj);
break;
......@@ -6077,7 +5896,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
else
mobj->movedir = 2;
if (mobj->info->seesound)
S_StartSound(mobj, mobj->info->seesound);
S_StartSoundFromMobj(mobj, mobj->info->seesound);
P_SetMobjState(mobj, mobj->info->seestate);
if (mobj->movedir == 2)
mobj->threshold = 12; // bounce 12 times
......@@ -6103,8 +5922,12 @@ static void P_Boss9Thinker(mobj_t *mobj)
whoosh->flags |= MF_NOCLIPHEIGHT;
#endif
if (!P_MobjWasRemoved(mobj->tracer))
{
P_SetMobjState(mobj->tracer, S_JETFUMEFLASH);
P_SetScale(mobj->tracer, mobj->scale << 1);
P_SetScale(mobj->tracer, 2*mobj->scale, false);
mobj->tracer->old_scale = mobj->tracer->scale;
}
}
else
{
......@@ -6205,7 +6028,7 @@ mobj_t *P_GetClosestAxis(mobj_t *source)
// scan the thinkers to find the closest axis point
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -6248,7 +6071,7 @@ static void P_MoveHoop(mobj_t *mobj)
{
const fixed_t fuse = (mobj->fuse*mobj->extravalue2);
const angle_t fa = mobj->movedir*(FINEANGLES/mobj->extravalue1);
matrix_t m;
oldmatrix_t m;
vector4_t v;
vector4_t res;
fixed_t finalx, finaly, finalz;
......@@ -6289,7 +6112,7 @@ void P_SpawnHoopOfSomething(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT
{
mobj_t *mobj;
INT32 i;
matrix_t m;
oldmatrix_t m;
vector4_t v;
vector4_t res;
fixed_t finalx, finaly, finalz;
......@@ -6358,7 +6181,7 @@ void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 numb
{
mobj_t *mobj;
INT32 i;
matrix_t m;
oldmatrix_t m;
vector4_t v;
vector4_t res;
fixed_t finalx, finaly, finalz, dist;
......@@ -6439,28 +6262,24 @@ void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 numb
//
// Sets the sprite scaling
//
void P_SetScale(mobj_t *mobj, fixed_t newscale)
void P_SetScale(mobj_t *mobj, fixed_t newscale, boolean instant)
{
player_t *player;
fixed_t oldscale;
if (!mobj)
return;
oldscale = mobj->scale; //keep for adjusting stuff below
mobj->scale = newscale;
mobj->radius = FixedMul(FixedDiv(mobj->radius, oldscale), newscale);
mobj->height = FixedMul(FixedDiv(mobj->height, oldscale), newscale);
player = mobj->player;
if (player)
if (mobj->player)
{
G_GhostAddScale(newscale);
player->viewheight = FixedMul(FixedDiv(player->viewheight, oldscale), newscale); // Nonono don't calculate viewheight elsewhere, this is the best place for it!
// Nonono don't calculate viewheight elsewhere, this is the best place for it!
mobj->player->viewheight = FixedMul(FixedDiv(mobj->player->viewheight, mobj->scale), newscale);
}
mobj->radius = FixedMul(FixedDiv(mobj->radius, mobj->scale), newscale);
mobj->height = FixedMul(FixedDiv(mobj->height, mobj->scale), newscale);
mobj->scale = newscale;
if (instant)
mobj->destscale = mobj->old_scale = newscale;
}
void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on your target
......@@ -6553,7 +6372,7 @@ static void P_NightsItemChase(mobj_t *thing)
//
void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot)
{
matrix_t m;
oldmatrix_t m;
vector4_t unit_lengthways, unit_sideways, pos_lengthways, pos_sideways;
vector4_t res;
fixed_t radius, dist = 0, zstore;
......@@ -6637,7 +6456,7 @@ void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot)
if (dosound && (mobj->flags2 & MF2_BOSSNOTRAP))
{
S_StartSound(mobj, mobj->info->activesound);
S_StartSoundFromMobj(mobj, mobj->info->activesound);
dosound = false;
}
......@@ -6815,10 +6634,22 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
thing->flags |= MF_NOCLIPHEIGHT;
thing->eflags = (thing->eflags & ~MFE_VERTICALFLIP)|(thing->target->eflags & MFE_VERTICALFLIP);
P_SetScale(thing, FixedMul(thing->target->scale, thing->target->player->shieldscale));
thing->destscale = thing->scale;
//Set the shield's scale based on shieldscale, hide it if we're too small!
fixed_t scale = FixedMul(thing->target->scale, thing->target->player->shieldscale);
if (scale < 1) {
P_SetScale(thing, thing->target->scale, true);
thing->old_scale = thing->target->old_scale;
thing->flags2 |= (MF2_DONTDRAW|MF2_JUSTATTACKED); //Hide and indicate we're hidden
} else {
P_SetScale(thing, scale, true);
thing->old_scale = FixedMul(thing->target->old_scale, thing->target->player->shieldscale);
//Only unhide if we were hidden by the above code
if (thing->flags2 & MF2_JUSTATTACKED)
thing->flags2 &= ~(MF2_DONTDRAW|MF2_JUSTATTACKED);
}
#define NewMH(mobj) mobj->height // Ugly mobj-height and player-height defines, for the sake of prettier code
#define NewPH(player) P_GetPlayerHeight(player)
#define OldMH(mobj) FixedMul(mobj->height, FixedDiv(mobj->old_scale, mobj->scale))
......@@ -6942,6 +6773,12 @@ void P_RunOverlays(void)
else
zoffs = 0;
// hide the overlay as well if we're part of a hidden shield
if ((mo->target->flags2 & (MF2_JUSTATTACKED|MF2_DONTDRAW)) == (MF2_JUSTATTACKED|MF2_DONTDRAW))
mo->flags2 |= (MF2_DONTDRAW|MF2_JUSTATTACKED);
else if (mo->flags2 & MF2_JUSTATTACKED)
mo->flags2 &= ~(MF2_DONTDRAW|MF2_JUSTATTACKED);
P_UnsetThingPosition(mo);
mo->x = mo->target->x;
mo->y = mo->target->y;
......@@ -7062,7 +6899,7 @@ static void P_KoopaThinker(mobj_t *koopa)
if (P_MobjWasRemoved(flame))
return;
flame->momx = -FixedMul(flame->info->speed, flame->scale);
S_StartSound(flame, sfx_koopfr);
S_StartSoundFromMobj(flame, sfx_koopfr);
}
else if (P_RandomChance(5*FRACUNIT/256))
{
......@@ -7164,24 +7001,16 @@ static void P_MobjScaleThink(mobj_t *mobj)
correctionType = 2; // Correct Z position by moving down
if (abs(mobj->scale - mobj->destscale) < mobj->scalespeed)
P_SetScale(mobj, mobj->destscale);
P_SetScale(mobj, mobj->destscale, false);
else if (mobj->scale < mobj->destscale)
P_SetScale(mobj, mobj->scale + mobj->scalespeed);
P_SetScale(mobj, mobj->scale + mobj->scalespeed, false);
else if (mobj->scale > mobj->destscale)
P_SetScale(mobj, mobj->scale - mobj->scalespeed);
P_SetScale(mobj, mobj->scale - mobj->scalespeed, false);
if (correctionType == 1)
mobj->z -= (mobj->height - oldheight)/2;
else if (correctionType == 2)
mobj->z -= mobj->height - oldheight;
if (mobj->scale == mobj->destscale)
/// \todo Lua hook for "reached destscale"?
switch (mobj->type)
{
default:
break;
}
}
static void P_MaceSceneryThink(mobj_t *mobj)
......@@ -7261,8 +7090,9 @@ static boolean P_DrownNumbersSceneryThink(mobj_t *mobj)
mobj->x = mobj->target->x;
mobj->y = mobj->target->y;
P_SetScale(mobj, mobj->target->scale, false);
mobj->destscale = mobj->target->destscale;
P_SetScale(mobj, mobj->target->scale);
mobj->old_scale = mobj->target->old_scale;
if (mobj->target->eflags & MFE_VERTICALFLIP)
{
......@@ -7320,7 +7150,7 @@ static void P_FlameJetSceneryThink(mobj_t *mobj)
strength = (mobj->movedir ? mobj->movedir : 80)<<(FRACBITS-2);
P_InstaThrust(flame, flame->angle, strength);
S_StartSound(flame, sfx_fire);
S_StartSoundFromMobj(flame, sfx_fire);
}
static void P_VerticalFlameJetSceneryThink(mobj_t *mobj)
......@@ -7363,7 +7193,7 @@ static void P_VerticalFlameJetSceneryThink(mobj_t *mobj)
P_SetMobjState(flame, S_FLAMEJETFLAME7);
}
P_InstaThrust(flame, mobj->angle, FixedDiv(mobj->fuse*FRACUNIT, 3*FRACUNIT));
S_StartSound(flame, sfx_fire);
S_StartSoundFromMobj(flame, sfx_fire);
}
static boolean P_ParticleGenSceneryThink(mobj_t *mobj)
......@@ -7423,10 +7253,10 @@ static boolean P_ParticleGenSceneryThink(mobj_t *mobj)
(mobjtype_t)mobj->threshold);
if (!P_MobjWasRemoved(spawn))
{
P_SetScale(spawn, mobj->scale);
spawn->momz = FixedMul(mobj->movefactor, spawn->scale);
P_SetScale(spawn, mobj->scale, true);
spawn->destscale = spawn->scale/100;
spawn->scalespeed = spawn->scale/mobj->health;
spawn->momz = FixedMul(mobj->movefactor, spawn->scale);
spawn->tics = (tic_t)mobj->health;
spawn->flags2 |= (mobj->flags2 & MF2_OBJECTFLIP);
spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones
......@@ -7469,7 +7299,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
player = &players[i];
}
if (stat == S_ROSY_JUMP || stat == S_ROSY_PAIN)
if (stat == S_ROSY_JUMP || stat == S_ROSY_FALL || stat == S_ROSY_PAIN)
{
if (P_IsObjectOnGround(mobj))
{
......@@ -7480,16 +7310,16 @@ static void P_RosySceneryThink(mobj_t *mobj)
stat = S_ROSY_WALK;
P_SetMobjState(mobj, stat);
}
else if (P_MobjFlip(mobj)*mobj->momz < 0)
mobj->frame = mobj->state->frame + mobj->state->var1;
else if (P_MobjFlip(mobj)*mobj->momz < 0 && stat == S_ROSY_JUMP)
P_SetMobjState(mobj, S_ROSY_FALL);
}
if (!player)
{
if ((stat < S_ROSY_IDLE1 || stat > S_ROSY_IDLE4) && stat != S_ROSY_JUMP)
if (stat != S_ROSY_IDLE && stat != S_ROSY_JUMP && stat != S_ROSY_FALL)
{
mobj->momx = mobj->momy = 0;
P_SetMobjState(mobj, S_ROSY_IDLE1);
P_SetMobjState(mobj, S_ROSY_IDLE);
}
}
else
......@@ -7503,13 +7333,11 @@ static void P_RosySceneryThink(mobj_t *mobj)
switch (stat)
{
case S_ROSY_IDLE1:
case S_ROSY_IDLE2:
case S_ROSY_IDLE3:
case S_ROSY_IDLE4:
case S_ROSY_IDLE:
dojump = true;
break;
case S_ROSY_JUMP:
case S_ROSY_FALL:
case S_ROSY_PAIN:
// handled above
break;
......@@ -7535,8 +7363,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
max = pdist;
if ((--mobj->extravalue1) <= 0)
{
if (++mobj->frame > mobj->state->frame + mobj->state->var1)
mobj->frame = mobj->state->frame;
P_SetMobjState(mobj, S_ROSY_WALK);
if (mom > 12*mobj->scale)
mobj->extravalue1 = 2;
else if (mom > 6*mobj->scale)
......@@ -7555,7 +7382,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
mobj->target->momx = mobj->momx;
mobj->target->momy = mobj->momy;
P_SetMobjState(mobj, (stat = S_ROSY_HUG));
S_StartSound(mobj, sfx_cdpcm6);
S_StartSoundFromMobj(mobj, sfx_cdpcm6);
mobj->angle = angletoplayer;
}
}
......@@ -7592,7 +7419,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
if (mobj->cvmem < (love ? 5*TICRATE : 0))
{
P_SetMobjState(mobj, (stat = S_ROSY_PAIN));
S_StartSound(mobj, sfx_cdpcm7);
S_StartSoundFromMobj(mobj, sfx_cdpcm7);
}
else
P_SetMobjState(mobj, (stat = S_ROSY_JUMP));
......@@ -7613,7 +7440,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
if (player->exiting || --mobj->cvmem < TICRATE)
{
P_SetMobjState(mobj, (stat = S_ROSY_HUG));
S_StartSound(mobj, sfx_cdpcm6);
S_StartSoundFromMobj(mobj, sfx_cdpcm6);
mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
mobj->target->momx = mobj->momx;
mobj->target->momy = mobj->momy;
......@@ -7638,7 +7465,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
mobj->z += P_MobjFlip(mobj);
mobj->momx = mobj->momy = 0;
P_SetObjectMomZ(mobj, 6 << FRACBITS, false);
S_StartSound(mobj, sfx_cdfm02);
S_StartSoundFromMobj(mobj, sfx_cdfm02);
}
if (makeheart)
......@@ -7646,8 +7473,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
mobj_t *cdlhrt = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_CDLHRT);
if (!P_MobjWasRemoved(cdlhrt))
{
cdlhrt->destscale = (5*mobj->scale) >> 4;
P_SetScale(cdlhrt, cdlhrt->destscale);
P_SetScale(cdlhrt, (5*mobj->scale) >> 4, true);
cdlhrt->fuse = (5*TICRATE) >> 1;
cdlhrt->momz = mobj->scale;
P_SetTarget(&cdlhrt->target, mobj);
......@@ -7864,7 +7690,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
{
mobj->health = 0;
P_SetMobjState(mobj, mobj->info->deathstate);
S_StartSound(mobj, mobj->info->deathsound + P_RandomKey(mobj->info->mass));
S_StartSoundFromMobj(mobj, mobj->info->deathsound + P_RandomKey(mobj->info->mass));
return;
}
break;
......@@ -7901,8 +7727,9 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->eflags |= (mobj->target->eflags & MFE_VERTICALFLIP);
P_SetScale(mobj, mobj->target->scale, false);
mobj->destscale = mobj->target->destscale;
P_SetScale(mobj, mobj->target->scale);
mobj->old_scale = mobj->target->old_scale;
if (!(mobj->eflags & MFE_VERTICALFLIP))
mobj->z = mobj->target->z + mobj->target->height + FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale);
......@@ -8052,7 +7879,9 @@ static void P_MobjSceneryThink(mobj_t *mobj)
}
P_SetThingPosition(mobj);
P_SetScale(mobj, mobj->target->scale);
P_SetScale(mobj, mobj->target->scale, false);
mobj->destscale = mobj->target->destscale;
mobj->old_scale = mobj->target->old_scale;
}
break;
case MT_TUTORIALFLOWER:
......@@ -8210,6 +8039,10 @@ static boolean P_MobjBossThink(mobj_t *mobj)
case MT_METALSONIC_BATTLE:
P_Boss9Thinker(mobj);
break;
case MT_OLDK:
if (mobj->health <= 0)
mobj->momz -= (2*FRACUNIT)/3;
break;
default: // Generic SOC-made boss
if (mobj->flags2 & MF2_SKULLFLY)
P_SpawnGhostMobj(mobj);
......@@ -8251,7 +8084,7 @@ static boolean P_MobjBossThink(mobj_t *mobj)
}
if (mobj->momz && mobj->z + mobj->momz <= mobj->floorz)
{
S_StartSound(mobj, sfx_befall);
S_StartSoundFromMobj(mobj, sfx_befall);
if (mobj->state != states + S_CYBRAKDEMON_DIE8)
P_SetMobjState(mobj, S_CYBRAKDEMON_DIE8);
}
......@@ -8281,7 +8114,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
{
if (!mobj->fuse)
{
S_StartSound(mobj, sfx_s3k77);
S_StartSoundFromMobj(mobj, sfx_s3k77);
mobj->flags2 |= MF2_DONTDRAW;
mobj->fuse = TICRATE;
}
......@@ -8335,7 +8168,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
mo2->angle = fa << ANGLETOFINESHIFT;
if (!i && !(mobj->fuse & 2))
S_StartSound(mo2, mobj->info->deathsound);
S_StartSoundFromMobj(mo2, mobj->info->deathsound);
flicky = P_InternalFlickySpawn(mo2, 0, 8*FRACUNIT, false, -1);
if (!flicky)
......@@ -8374,7 +8207,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS),
MT_SONIC3KBOSSEXPLODE);
if (!P_MobjWasRemoved(explosion))
S_StartSound(explosion, sfx_s3kb4);
S_StartSoundFromMobj(explosion, sfx_s3kb4);
}
if (mobj->movedir == DMG_DROWNED)
P_SetObjectMomZ(mobj, -FRACUNIT/2, true); // slower fall from drowning
......@@ -8393,7 +8226,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS),
MT_SONIC3KBOSSEXPLODE);
if (!P_MobjWasRemoved(explosion))
S_StartSound(explosion, sfx_s3kb4);
S_StartSoundFromMobj(explosion, sfx_s3kb4);
}
P_SetObjectMomZ(mobj, -2*FRACUNIT/3, true);
}
......@@ -8486,7 +8319,7 @@ static void P_ArrowThink(mobj_t *mobj)
if (!(mobj->extravalue1) && (mobj->momz < 0))
{
mobj->extravalue1 = 1;
S_StartSound(mobj, mobj->info->activesound);
S_StartSoundFromMobj(mobj, mobj->info->activesound);
}
if (leveltime & 1)
{
......@@ -8494,8 +8327,8 @@ static void P_ArrowThink(mobj_t *mobj)
if (!P_MobjWasRemoved(dust))
{
dust->tics = 18;
dust->scalespeed = 4096;
dust->destscale = FRACUNIT/32;
dust->scalespeed = FRACUNIT/16;
}
}
}
......@@ -8517,7 +8350,7 @@ static void P_BumbleboreThink(mobj_t *mobj)
mobj->momy >>= 1;
if (++mobj->movefactor == 4)
{
S_StartSound(mobj, mobj->info->seesound);
S_StartSoundFromMobj(mobj, mobj->info->seesound);
mobj->momx = mobj->momy = mobj->momz = 0;
mobj->flags = (mobj->flags|MF_PAIN) & ~MF_NOGRAVITY;
P_SetMobjState(mobj, mobj->info->meleestate);
......@@ -8531,7 +8364,7 @@ static void P_BumbleboreThink(mobj_t *mobj)
if (P_IsObjectOnGround(mobj))
{
S_StopSound(mobj);
S_StartSound(mobj, mobj->info->attacksound);
S_StartSoundFromMobj(mobj, mobj->info->attacksound);
mobj->flags = (mobj->flags | MF_NOGRAVITY) & ~MF_PAIN;
mobj->momx = mobj->momy = mobj->momz = 0;
P_SetMobjState(mobj, mobj->info->painstate);
......@@ -8702,7 +8535,7 @@ static boolean P_EggRobo1Think(mobj_t *mobj)
if (mobj->movecount)
{
if (!(--mobj->movecount))
S_StartSound(mobj, mobj->info->deathsound);
S_StartSoundFromMobj(mobj, mobj->info->deathsound);
}
else
{
......@@ -8780,7 +8613,7 @@ static boolean P_EggRobo1Think(mobj_t *mobj)
mobj->x - basex,
mobj->y - basey)
< mobj->scale)
S_StartSound(mobj, mobj->info->seesound);
S_StartSoundFromMobj(mobj, mobj->info->seesound);
P_MoveOrigin(mobj,
(15*(mobj->x >> 4)) + (basex >> 4) + P_ReturnThrustX(mobj, mobj->angle, SPECTATORRADIUS >> 4),
......@@ -9262,7 +9095,7 @@ static void P_PterabyteThink(mobj_t *mobj)
P_SetMobjState(mobj, S_PTERABYTE_SWOOPDOWN);
mobj->extravalue1++;
S_StartSound(mobj, mobj->info->attacksound);
S_StartSoundFromMobj(mobj, mobj->info->attacksound);
time = FixedDiv(hdist, hspeed);
mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
fa = (mobj->angle >> ANGLETOFINESHIFT) & FINEMASK;
......@@ -9328,7 +9161,7 @@ static void P_DragonbomberThink(mobj_t *mobj)
mine->angle = segment->angle;
P_InstaThrust(mine, mobj->angle, P_AproxDistance(mobj->momx, mobj->momy) >> 1);
P_SetObjectMomZ(mine, -2*FRACUNIT, true);
S_StartSound(mine, mine->info->seesound);
S_StartSoundFromMobj(mine, mine->info->seesound);
}
P_SetMobjState(segment, segment->info->raisestate);
mobj->threshold = mobj->info->painchance;
......@@ -9486,12 +9319,12 @@ static void P_PointPushThink(mobj_t *mobj)
radius = mobj->spawnpoint->args[0] << FRACBITS;
pushmobj = mobj;
xl = (unsigned)(mobj->x - radius - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
xh = (unsigned)(mobj->x + radius - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
yl = (unsigned)(mobj->y - radius - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
yh = (unsigned)(mobj->y + radius - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
xl = (unsigned)(mobj->x - radius - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(mobj->x + radius - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(mobj->y - radius - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(mobj->y + radius - bmaporgy)>>MAPBLOCKSHIFT;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing);
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing, pushmobj);
}
static boolean P_MobjRegularThink(mobj_t *mobj)
......@@ -9572,7 +9405,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj->z = mobj->floorz;
if (leveltime % mobj->info->painchance == 0)
S_StartSound(mobj, mobj->info->activesound);
S_StartSoundFromMobj(mobj, mobj->info->activesound);
if ((statenum_t)(mobj->state - states) != mobj->info->seestate)
P_SetMobjState(mobj, mobj->info->seestate);
......@@ -9666,7 +9499,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
if (mobj->state - states == S_SMASHSPIKE_FALL && P_IsObjectOnGround(mobj))
{
P_SetMobjState(mobj, S_SMASHSPIKE_STOMP1);
S_StartSound(mobj, sfx_spsmsh);
S_StartSoundFromMobj(mobj, sfx_spsmsh);
}
else if (mobj->state - states == S_SMASHSPIKE_RISE2 && P_MobjFlip(mobj)*(mobj->z - mobj->movecount) >= 0)
{
......@@ -9906,9 +9739,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
traindust->frame = P_RandomRange(0, 8)|FF_TRANS90;
traindust->angle = mobj->angle;
traindust->tics = TICRATE*4;
P_SetScale(traindust, FRACUNIT*6, true);
traindust->destscale = FRACUNIT*64;
traindust->scalespeed = FRACUNIT/24;
P_SetScale(traindust, FRACUNIT*6);
}
break;
case MT_TRAINSTEAMSPAWNER:
......@@ -9919,9 +9752,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
P_SetMobjState(steam, S_TRAINSTEAM);
steam->frame = P_RandomRange(0, 1)|FF_TRANS90;
steam->tics = TICRATE*8;
P_SetScale(steam, FRACUNIT*16, true);
steam->destscale = FRACUNIT*64;
steam->scalespeed = FRACUNIT/8;
P_SetScale(steam, FRACUNIT*16);
steam->momx = P_SignedRandom()*32;
steam->momy = -64*FRACUNIT;
steam->momz = 2*FRACUNIT;
......@@ -10038,13 +9871,13 @@ static void P_FiringThink(mobj_t *mobj)
if (mobj->health <= 0)
return;
if (mobj->state->action.acp1 == (actionf_p1)A_Boss1Laser)
if (mobj->state->action == (actionf_p1)A_Boss1Laser)
{
if (mobj->state->tics > 1)
{
var1 = mobj->state->var1;
var2 = mobj->state->var2 & 65535;
mobj->state->action.acp1(mobj);
mobj->state->action(mobj);
}
}
else if (leveltime & 1) // Fire mode
......@@ -10072,7 +9905,7 @@ static void P_FiringThink(mobj_t *mobj)
missile->flags2 |= MF2_SUPERFIRE;
if (mobj->info->attacksound)
S_StartSound(missile, mobj->info->attacksound);
S_StartSoundFromMobj(missile, mobj->info->attacksound);
}
}
else
......@@ -10157,9 +9990,9 @@ static void P_FlagFuseThink(mobj_t *mobj)
// Assumedly in splitscreen players will be on opposing teams
if (players[consoleplayer].ctfteam == 1 || splitscreen)
S_StartSound(NULL, sfx_hoop1);
S_StartSoundFromEverywhere(sfx_hoop1);
else if (players[consoleplayer].ctfteam == 2)
S_StartSound(NULL, sfx_hoop3);
S_StartSoundFromEverywhere(sfx_hoop3);
redflag = flagmo;
}
......@@ -10170,9 +10003,9 @@ static void P_FlagFuseThink(mobj_t *mobj)
// Assumedly in splitscreen players will be on opposing teams
if (players[consoleplayer].ctfteam == 2 || splitscreen)
S_StartSound(NULL, sfx_hoop1);
S_StartSoundFromEverywhere(sfx_hoop1);
else if (players[consoleplayer].ctfteam == 1)
S_StartSound(NULL, sfx_hoop3);
S_StartSoundFromEverywhere(sfx_hoop3);
blueflag = flagmo;
}
......@@ -10239,14 +10072,14 @@ static boolean P_FuseThink(mobj_t *mobj)
{
mobj->fuse = 30;
P_SetMobjState(mobj, S_LAVAFALL_TELL);
S_StartSound(mobj, mobj->info->seesound);
S_StartSoundFromMobj(mobj, mobj->info->seesound);
}
else if (mobj->state - states == S_LAVAFALL_TELL)
{
mobj->fuse = 40;
P_SetMobjState(mobj, S_LAVAFALL_SHOOT);
S_StopSound(mobj);
S_StartSound(mobj, mobj->info->attacksound);
S_StartSoundFromMobj(mobj, mobj->info->attacksound);
}
else
{
......@@ -10265,19 +10098,19 @@ static boolean P_FuseThink(mobj_t *mobj)
P_SetMobjState(mobj, mobj->info->spawnstate);
mobj->fuse = 100;
S_StopSound(mobj);
S_StartSound(mobj, sfx_s3k8c);
S_StartSoundFromMobj(mobj, sfx_s3k8c);
}
else if (mobj->extravalue2 == 1)
{
mobj->fuse = 50;
S_StartSound(mobj, sfx_s3ka3);
S_StartSoundFromMobj(mobj, sfx_s3ka3);
}
else
{
P_SetMobjState(mobj, mobj->info->meleestate);
mobj->fuse = 100;
S_StopSound(mobj);
S_StartSound(mobj, sfx_s3kc2l);
S_StartSoundFromMobj(mobj, sfx_s3kc2l);
}
return false;
case MT_PLAYER:
......@@ -10296,6 +10129,7 @@ static boolean P_FuseThink(mobj_t *mobj)
//
void P_MobjThinker(mobj_t *mobj)
{
boolean ispushable;
I_Assert(mobj != NULL);
I_Assert(!P_MobjWasRemoved(mobj));
......@@ -10321,8 +10155,10 @@ void P_MobjThinker(mobj_t *mobj)
tmfloorthing = tmhitthing = NULL;
ispushable = mobj->flags & MF_PUSHABLE || (mobj->info->flags & MF_PUSHABLE && mobj->fuse);
// Sector flag MSF_TRIGGERLINE_MOBJ allows ANY mobj to trigger a linedef exec
P_CheckMobjTrigger(mobj, false);
P_CheckMobjTrigger(mobj, ispushable);
if (mobj->scale != mobj->destscale)
P_MobjScaleThink(mobj); // Slowly scale up/down to reach your destscale.
......@@ -10366,7 +10202,7 @@ void P_MobjThinker(mobj_t *mobj)
// if it's pushable, or if it would be pushable other than temporary disablement, use the
// separate thinker
if (mobj->flags & MF_PUSHABLE || (mobj->info->flags & MF_PUSHABLE && mobj->fuse))
if (ispushable)
{
if (!P_MobjPushableThink(mobj))
return;
......@@ -10397,7 +10233,7 @@ void P_MobjThinker(mobj_t *mobj)
if (leveltime % mobj->health)
return;
if (mobj->threshold)
S_StartSound(mobj, mobj->threshold);
S_StartSoundFromMobj(mobj, mobj->threshold);
return;
}
......@@ -10475,9 +10311,6 @@ void P_MobjThinker(mobj_t *mobj)
}
// Can end up here if a player dies.
if (mobj->player)
P_CyclePlayerMobjState(mobj);
else
P_CycleMobjState(mobj);
if (P_MobjWasRemoved(mobj))
......@@ -10493,6 +10326,7 @@ void P_MobjThinker(mobj_t *mobj)
case MT_GRENADEPICKUP:
if (mobj->health == 0) // Fading tile
{
// TODO: Maybe use mobj->alpha instead of messing with frame flags
INT32 value = mobj->info->damage/10;
value = mobj->fuse/value;
value = 10-value;
......@@ -10544,8 +10378,6 @@ void P_PushableThinker(mobj_t *mobj)
I_Assert(mobj != NULL);
I_Assert(!P_MobjWasRemoved(mobj));
P_CheckMobjTrigger(mobj, true);
// it has to be pushable RIGHT NOW for this part to happen
if (mobj->flags & MF_PUSHABLE && !(mobj->momx || mobj->momy))
P_TryMove(mobj, mobj->x, mobj->y, true);
......@@ -10767,6 +10599,19 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
}
}
static INT32 P_SetupNPC(mobj_t *mobj, const char *name)
{
INT32 skinnum = R_SkinAvailable(name);
if (skinnum != -1)
{
mobj->skin = skins[skinnum];
mobj->color = skins[skinnum]->prefcolor;
}
return skinnum;
}
//
// P_SpawnMobj
//
......@@ -10794,7 +10639,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
}
// this is officially a mobj, declared as soon as possible.
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
mobj->thinker.function = (actionf_p1)P_MobjThinker;
mobj->type = type;
mobj->info = info;
......@@ -10838,6 +10683,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
// Sprite rendering
mobj->blendmode = AST_TRANSLUCENT;
mobj->alpha = FRACUNIT;
mobj->spritexscale = mobj->spriteyscale = mobj->scale;
mobj->spritexoffset = mobj->spriteyoffset = 0;
mobj->floorspriteslope = NULL;
......@@ -10898,6 +10744,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
// when spawning MT_PLAYER, set mobj->player before calling MobjSpawn hook to prevent P_RemoveMobj from succeeding on player mobj.
va_start(args, type);
mobj->player = va_arg(args, player_t *);
if (mobj->player)
mobj->player->mo = mobj;
va_end(args);
}
......@@ -10927,7 +10774,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
if (titlemapinaction) mobj->flags &= ~MF_NOTHINK;
break;
case MT_LOCKONINF:
P_SetScale(mobj, (mobj->destscale = 3*mobj->scale));
P_SetScale(mobj, 3*mobj->scale, true);
break;
case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE:
mobj->fuse = mobj->info->painchance;
......@@ -10938,8 +10785,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
if (P_MobjWasRemoved(spawn))
break;
spawn->destscale = mobj->scale;
P_SetScale(spawn, mobj->scale);
P_SetScale(spawn, mobj->scale, true);
P_SetTarget(&spawn->target, mobj);
}
break;
......@@ -10956,8 +10802,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
if (P_MobjWasRemoved(spawn))
break;
spawn->destscale = mobj->scale;
P_SetScale(spawn, mobj->scale);
P_SetScale(spawn, mobj->scale, true);
P_SetTarget(&mobj->tracer, spawn);
P_SetTarget(&spawn->target, mobj);
}
......@@ -10974,14 +10819,13 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
if (P_MobjWasRemoved(ball))
continue;
ball->destscale = mobj->scale;
P_SetScale(ball, mobj->scale);
P_SetScale(ball, mobj->scale, true);
P_SetTarget(&ball->target, mobj);
ball->movedir = FixedAngle(FixedMul(FixedDiv(i<<FRACBITS, mobj->info->damage<<FRACBITS), 360<<FRACBITS));
ball->threshold = ball->radius + mobj->radius + FixedMul(ball->info->painchance, ball->scale);
var1 = ball->state->var1, var2 = ball->state->var2;
ball->state->action.acp1(ball);
ball->state->action(ball);
}
}
break;
......@@ -10996,8 +10840,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
if (P_MobjWasRemoved(ball))
continue;
ball->destscale = mobj->scale;
P_SetScale(ball, mobj->scale);
P_SetScale(ball, mobj->scale, true);
P_SetTarget(&lastball->tracer, ball);
P_SetTarget(&ball->target, mobj);
lastball = ball;
......@@ -11122,17 +10965,14 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
nummaprings++;
break;
case MT_METALSONIC_RACE:
mobj->skin = skins[5];
/* FALLTHRU */
case MT_METALSONIC_BATTLE:
mobj->color = skins[5]->prefcolor;
sc = 5;
sc = P_SetupNPC(mobj, "metalsonic");
break;
case MT_FANG:
sc = 4;
sc = P_SetupNPC(mobj, "fang");
break;
case MT_ROSY:
sc = 3;
sc = P_SetupNPC(mobj, "amy");
break;
case MT_CORK:
mobj->flags2 |= MF2_SUPERFIRE;
......@@ -11162,13 +11002,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
mcsolid->angle = mobj->angle + ANGLE_90;
}
break;
case MT_TORCHFLOWER:
{
mobj_t *fire = P_SpawnMobjFromMobj(mobj, 0, 0, 46*FRACUNIT, MT_FLAME);
if (!P_MobjWasRemoved(fire))
P_SetTarget(&mobj->target, fire);
break;
}
case MT_PYREFLY:
mobj->extravalue1 = (FixedHypot(mobj->x, mobj->y)/FRACUNIT) % 360;
mobj->extravalue2 = 0;
......@@ -11209,12 +11042,12 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
if (mobj->skin) // correct inadequecies above.
{
mobj->sprite2 = P_GetSkinSprite2(mobj->skin, (mobj->frame & FF_FRAMEMASK), NULL);
mobj->sprite2 = P_GetSkinSprite2(mobj->skin, P_GetStateSprite2(mobj->state), NULL);
mobj->frame &= ~FF_FRAMEMASK;
}
// Call action functions when the state is set
if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC))
if (st->action && (mobj->flags & MF_RUNSPAWNFUNC))
{
if (levelloading)
{
......@@ -11229,7 +11062,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
var1 = st->var1;
var2 = st->var2;
astate = st;
st->action.acp1(mobj);
st->action(mobj);
// DANGER! This can cause P_SpawnMobj to return NULL!
// Avoid using MF_RUNSPAWNFUNC on mobjs whose spawn state expects target or tracer to already be set!
if (P_MobjWasRemoved(mobj))
......@@ -11277,7 +11110,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype
mobj->z = z;
mobj->momz = mobjinfo[type].speed;
mobj->thinker.function.acp1 = (actionf_p1)P_NullPrecipThinker;
mobj->thinker.function = (actionf_p1)P_NullPrecipThinker;
P_AddThinker(THINK_PRECIP, &mobj->thinker);
CalculatePrecipFloor(mobj);
......@@ -11298,14 +11131,14 @@ static inline precipmobj_t *P_SpawnRainMobj(fixed_t x, fixed_t y, fixed_t z, mob
{
precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type);
mo->precipflags |= PCF_RAIN;
//mo->thinker.function.acp1 = (actionf_p1)P_RainThinker;
//mo->thinker.function = (actionf_p1)P_RainThinker;
return mo;
}
static inline precipmobj_t *P_SpawnSnowMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
{
precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type);
//mo->thinker.function.acp1 = (actionf_p1)P_SnowThinker;
//mo->thinker.function = (actionf_p1)P_SnowThinker;
return mo;
}
......@@ -11333,17 +11166,16 @@ tic_t itemrespawntime[ITEMQUESIZE];
size_t iquehead, iquetail;
#ifdef PARANOIA
#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed
#define SCRAMBLE_REMOVED // Force debug build to crash when a removed mobj is accessed
#endif
void P_RemoveMobj(mobj_t *mobj)
{
I_Assert(mobj != NULL);
if (P_MobjWasRemoved(mobj))
return; // something already removing this mobj.
if (P_MobjWasRemoved(mobj) || mobj->thinker.removing)
return; // Something already removed or is removing this mobj.
mobj->thinker.function.acp1 = (actionf_p1)P_RemoveThinkerDelayed; // shh. no recursing.
mobj->thinker.removing = true; // Set earlier to avoid recursion.
LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved));
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work.
// Rings only, please!
if (mobj->spawnpoint &&
......@@ -11441,15 +11273,6 @@ void P_RemoveMobj(mobj_t *mobj)
#endif
}
// This does not need to be added to Lua.
// To test it in Lua, check mobj.valid
boolean P_MobjWasRemoved(mobj_t *mobj)
{
if (mobj && mobj->thinker.function.acp1 == (actionf_p1)P_MobjThinker)
return false;
return true;
}
void P_RemovePrecipMobj(precipmobj_t *mobj)
{
// unlink from sector and block lists
......@@ -11469,7 +11292,7 @@ void P_RemovePrecipMobj(precipmobj_t *mobj)
void P_RemoveSavegameMobj(mobj_t *mobj)
{
// unlink from sector and block lists
if (((thinker_t *)mobj)->function.acp1 == (actionf_p1)P_NullPrecipThinker)
if (((thinker_t *)mobj)->function == (actionf_p1)P_NullPrecipThinker)
{
P_UnsetPrecipThingPosition((precipmobj_t *)mobj);
......@@ -11677,7 +11500,7 @@ void P_PrecipitationEffects(void)
volume = 255;
if (sounds_rain && (!leveltime || leveltime % 80 == 1))
S_StartSoundAtVolume(players[displayplayer].mo, sfx_rainin, volume);
S_StartSoundFromMobjVol(players[displayplayer].mo, sfx_rainin, volume);
if (!sounds_thunder)
return;
......@@ -11685,7 +11508,7 @@ void P_PrecipitationEffects(void)
if (effects_lightning && lightningStrike && volume)
{
// Large, close thunder sounds to go with our lightning.
S_StartSoundAtVolume(players[displayplayer].mo, sfx_litng1 + M_RandomKey(4), volume);
S_StartSoundFromMobjVol(players[displayplayer].mo, sfx_litng1 + M_RandomKey(4), volume);
}
else if (thunderchance < 20)
{
......@@ -11693,7 +11516,7 @@ void P_PrecipitationEffects(void)
if (volume < 80)
volume = 80;
S_StartSoundAtVolume(players[displayplayer].mo, sfx_athun1 + M_RandomKey(2), volume);
S_StartSoundFromMobjVol(players[displayplayer].mo, sfx_athun1 + M_RandomKey(2), volume);
}
}
......@@ -11856,7 +11679,7 @@ void P_SpawnPlayer(INT32 playernum)
p->awayviewtics = 0;
// set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't.
P_SetScale(mobj, mobj->destscale);
P_SetScale(mobj, mobj->destscale, true);
P_FlashPal(p, 0, 0); // Resets
// Set bounds accurately.
......@@ -12030,7 +11853,7 @@ void P_MovePlayerToStarpost(INT32 playernum)
z = p->starpostz << FRACBITS;
P_SetScale(mobj, (mobj->destscale = abs(p->starpostscale)));
P_SetScale(mobj, abs(p->starpostscale), true);
if (p->starpostscale < 0)
{
......@@ -13013,7 +12836,7 @@ static boolean P_MapAlreadyHasStarPost(mobj_t *mobj)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -13138,8 +12961,8 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
if (P_MobjWasRemoved(corona))
break;
P_SetScale(corona, (corona->destscale = mobj->scale*3));
P_SetTarget(&mobj->tracer, corona);
P_SetScale(corona, 3*mobj->scale, true);
}
break;
case MT_FLAMEHOLDER:
......@@ -13157,11 +12980,18 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
if (P_MobjWasRemoved(corona))
break;
P_SetScale(corona, (corona->destscale = flame->scale*3));
P_SetTarget(&flame->tracer, corona);
P_SetScale(corona, 3*flame->scale, true);
}
}
break;
case MT_TORCHFLOWER:
{
mobj_t *fire = P_SpawnMobjFromMobj(mobj, 0, 0, 46*FRACUNIT, MT_FLAME);
if (!P_MobjWasRemoved(fire))
P_SetTarget(&mobj->target, fire);
break;
}
case MT_CANDLE:
case MT_CANDLEPRICKET:
if (mthing->args[0])
......@@ -13246,10 +13076,8 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
case MT_DSZSTALAGMITE:
case MT_DSZ2STALAGMITE:
case MT_KELP:
if (mthing->args[0]) { // make mobj twice as big as normal
P_SetScale(mobj, 2*mobj->scale); // not 2*FRACUNIT in case of something like the old ERZ3 mode
mobj->destscale = mobj->scale;
}
if (mthing->args[0]) // make mobj twice as big as normal
P_SetScale(mobj, 2*mobj->scale, true); // not 2*FRACUNIT in case of something like the old ERZ3 mode
break;
case MT_THZTREE:
{ // Spawn the branches
......@@ -13302,6 +13130,32 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
banner->angle = mobjangle + ANGLE_90;
}
break;
case MT_SSZTREE:
{ // Spawn the branches
INT32 i;
mobj_t *branch;
for (i = 0; i < 5; i++)
{
branch = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SSZTREE_BRANCH);
if (P_MobjWasRemoved(branch))
continue;
branch->angle = mobj->angle + FixedAngle(i*72*FRACUNIT);
}
}
break;
case MT_SSZTREE2:
{ // Spawn the branches
INT32 i;
mobj_t *branch;
for (i = 0; i < 5; i++)
{
branch = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SSZTREE2_BRANCH);
if (P_MobjWasRemoved(branch))
continue;
branch->angle = mobj->angle + FixedAngle(i*72*FRACUNIT);
}
}
break;
case MT_HHZTREE_TOP:
{ // Spawn the branches
angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS) & (ANGLE_90 - 1);
......@@ -13328,10 +13182,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
case MT_LAVAFALL:
mobj->fuse = 30 + mthing->args[0];
if (mthing->args[1])
{
P_SetScale(mobj, 2*mobj->scale);
mobj->destscale = mobj->scale;
}
P_SetScale(mobj, 2*mobj->scale, true);
break;
case MT_PYREFLY:
//start on fire if args[0], otherwise behave normally
......@@ -13339,7 +13190,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
{
P_SetMobjState(mobj, mobj->info->meleestate);
mobj->extravalue2 = 2;
S_StartSound(mobj, sfx_s3kc2l);
S_StartSoundFromMobj(mobj, sfx_s3kc2l);
}
break;
case MT_BIGFERN:
......@@ -13394,8 +13245,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
break;
P_SetTarget(&elecmobj->target, mobj);
elecmobj->angle = FixedAngle(mthing->angle << FRACBITS);
elecmobj->destscale = mobj->scale*2;
P_SetScale(elecmobj, elecmobj->destscale);
P_SetScale(elecmobj, 2*mobj->scale, true);
}
break;
case MT_STARPOST:
......@@ -13453,8 +13303,8 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
return false;
}
base->angle = mobjangle + ANGLE_90;
P_SetScale(base, mobj->scale, true);
base->destscale = mobj->destscale;
P_SetScale(base, mobj->scale);
P_SetTarget(&base->target, mobj);
P_SetTarget(&mobj->tracer, base);
}
......@@ -13631,8 +13481,9 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y,
return NULL;
mobj->spawnpoint = mthing;
P_SetScale(mobj, FixedMul(mobj->scale, mthing->scale));
P_SetScale(mobj, FixedMul(mobj->scale, mthing->scale), false);
mobj->destscale = FixedMul(mobj->destscale, mthing->scale);
mobj->old_scale = FixedMul(mobj->old_scale, mthing->scale);
mobj->spritexscale = mthing->spritexscale;
mobj->spriteyscale = mthing->spriteyscale;
......@@ -13714,7 +13565,7 @@ void P_SpawnHoop(mapthing_t *mthing)
mobj_t *mobj = NULL;
mobj_t *nextmobj = NULL;
mobj_t *hoopcenter;
matrix_t pitchmatrix, yawmatrix;
oldmatrix_t pitchmatrix, yawmatrix;
fixed_t radius = mthing->args[0] << FRACBITS;
fixed_t sizefactor = 4*FRACUNIT;
fixed_t hoopsize = radius/sizefactor;
......@@ -13897,7 +13748,7 @@ static void P_SpawnItemCircle(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 n
angle_t angle = FixedAngle(mthing->angle << FRACBITS);
angle_t fa;
INT32 i;
matrix_t m;
oldmatrix_t m;
vector4_t v, res;
for (i = 0; i < numitemtypes; i++)
......@@ -14086,8 +13937,7 @@ mobj_t *P_SpawnXYZMissile(mobj_t *source, mobj_t *dest, mobjtype_t type,
if (source->eflags & MFE_VERTICALFLIP)
th->flags2 |= MF2_OBJECTFLIP;
th->destscale = source->scale;
P_SetScale(th, source->scale);
P_SetScale(th, source->scale, true);
speed = FixedMul(th->info->speed, th->scale);
......@@ -14098,7 +13948,7 @@ mobj_t *P_SpawnXYZMissile(mobj_t *source, mobj_t *dest, mobjtype_t type,
}
if (th->info->seesound)
S_StartSound(th, th->info->seesound);
S_StartSoundFromMobj(th, th->info->seesound);
P_SetTarget(&th->target, source); // where it came from
an = R_PointToAngle2(x, y, dest->x, dest->y);
......@@ -14150,8 +14000,7 @@ mobj_t *P_SpawnAlteredDirectionMissile(mobj_t *source, mobjtype_t type, fixed_t
if (source->eflags & MFE_VERTICALFLIP)
th->flags2 |= MF2_OBJECTFLIP;
th->destscale = source->scale;
P_SetScale(th, source->scale);
P_SetScale(th, source->scale, true);
speed = FixedMul(th->info->speed, th->scale);
......@@ -14162,7 +14011,7 @@ mobj_t *P_SpawnAlteredDirectionMissile(mobj_t *source, mobjtype_t type, fixed_t
}
if (th->info->seesound)
S_StartSound(th, th->info->seesound);
S_StartSoundFromMobj(th, th->info->seesound);
P_SetTarget(&th->target, source->target); // where it came from
an = R_PointToAngle2(0, 0, source->momx, source->momy) + (ANG1*shiftingAngle);
......@@ -14217,8 +14066,7 @@ mobj_t *P_SpawnPointMissile(mobj_t *source, fixed_t xa, fixed_t ya, fixed_t za,
if (source->eflags & MFE_VERTICALFLIP)
th->flags2 |= MF2_OBJECTFLIP;
th->destscale = source->scale;
P_SetScale(th, source->scale);
P_SetScale(th, source->scale, true);
speed = FixedMul(th->info->speed, th->scale);
......@@ -14229,7 +14077,7 @@ mobj_t *P_SpawnPointMissile(mobj_t *source, fixed_t xa, fixed_t ya, fixed_t za,
}
if (th->info->seesound)
S_StartSound(th, th->info->seesound);
S_StartSoundFromMobj(th, th->info->seesound);
P_SetTarget(&th->target, source); // where it came from
an = R_PointToAngle2(x, y, xa, ya);
......@@ -14289,8 +14137,7 @@ mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type)
if (source->eflags & MFE_VERTICALFLIP)
th->flags2 |= MF2_OBJECTFLIP;
th->destscale = source->scale;
P_SetScale(th, source->scale);
P_SetScale(th, source->scale, true);
if (source->type == MT_METALSONIC_BATTLE && source->health < 4)
speed = FixedMul(FixedMul(th->info->speed, 3*FRACUNIT/2), th->scale);
......@@ -14304,7 +14151,7 @@ mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type)
}
if (th->info->seesound)
S_StartSound(source, th->info->seesound);
S_StartSoundFromMobj(source, th->info->seesound);
P_SetTarget(&th->target, source); // where it came from
......@@ -14392,14 +14239,13 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
if (source->eflags & MFE_VERTICALFLIP)
th->flags2 |= MF2_OBJECTFLIP;
th->destscale = source->scale;
P_SetScale(th, source->scale);
P_SetScale(th, source->scale, true);
th->flags2 |= flags2;
// The rail ring has no unique thrown object, so we must do this.
if (th->info->seesound && !(th->flags2 & MF2_RAILRING))
S_StartSound(source, th->info->seesound);
S_StartSoundFromMobj(source, th->info->seesound);
P_SetTarget(&th->target, source);
......@@ -14454,7 +14300,8 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
yofs = FixedMul(yofs, mobj->scale);
zofs = FixedMul(zofs, mobj->scale);
newmobj = P_SpawnMobj(mobj->x + xofs, mobj->y + yofs, mobj->z + zofs, type);
newmobj = P_SpawnMobj(mobj->x + xofs, mobj->y + yofs, mobj->z + zofs, type, NULL);
if (!newmobj)
return NULL;
......@@ -14475,8 +14322,8 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
newmobj->old_z2 = mobj->old_z2 + zofs;
}
P_SetScale(newmobj, mobj->scale, false);
newmobj->destscale = mobj->destscale;
P_SetScale(newmobj, mobj->scale);
newmobj->old_x2 = mobj->old_x2 + xofs;
newmobj->old_y2 = mobj->old_y2 + yofs;
......@@ -14505,10 +14352,22 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
newmobj->old_scale2 = mobj->old_scale2;
newmobj->old_scale = mobj->old_scale;
newmobj->old_spritexscale2 = mobj->old_spritexscale2;
newmobj->old_spritexscale = mobj->old_spritexscale;
newmobj->old_spriteyscale2 = mobj->old_spriteyscale2;
newmobj->old_spriteyscale = mobj->old_spriteyscale;
newmobj->old_spritexoffset2 = mobj->old_spritexoffset2;
newmobj->old_spritexoffset = mobj->old_spritexoffset;
newmobj->old_spriteyoffset2 = mobj->old_spriteyoffset2;
newmobj->old_spriteyoffset = mobj->old_spriteyoffset;
return newmobj;
}
boolean P_IsMobjInPainState(mobj_t *mobj)
{
if (mobj->player)
return P_IsPlayerInState(mobj->player, S_PLAY_PAIN);
else
return (mobj->state == &states[mobj->info->painstate]);
}
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -308,15 +308,16 @@ typedef struct mobj_s
angle_t spriteroll, old_spriteroll, old_spriteroll2;
spritenum_t sprite; // used to find patch_t and flip value
UINT32 frame; // frame number, plus bits see p_pspr.h
UINT8 sprite2; // player sprites
UINT16 sprite2; // player sprites
UINT16 anim_duration; // for FF_ANIMATE states
UINT32 renderflags; // render flags
INT32 blendmode; // blend mode
fixed_t alpha; // alpha
fixed_t spritexscale, spriteyscale;
fixed_t spritexoffset, spriteyoffset;
fixed_t old_spritexscale, old_spriteyscale;
fixed_t old_spritexoffset, old_spriteyoffset;
fixed_t old_spritexscale, old_spriteyscale, old_spritexscale2, old_spriteyscale2;
fixed_t old_spritexoffset, old_spriteyoffset, old_spritexoffset2, old_spriteyoffset2;
struct pslope_s *floorspriteslope; // The slope that the floorsprite is rotated by
struct msecnode_s *touching_sectorlist; // a linked list of sectors where this object appears
......@@ -451,15 +452,16 @@ typedef struct precipmobj_s
angle_t spriteroll, old_spriteroll, old_spriteroll2;
spritenum_t sprite; // used to find patch_t and flip value
UINT32 frame; // frame number, plus bits see p_pspr.h
UINT8 sprite2; // player sprites
UINT16 sprite2; // player sprites
UINT16 anim_duration; // for FF_ANIMATE states
UINT32 renderflags; // render flags
INT32 blendmode; // blend mode
fixed_t alpha; // alpha
fixed_t spritexscale, spriteyscale;
fixed_t spritexoffset, spriteyoffset;
fixed_t old_spritexscale, old_spriteyscale;
fixed_t old_spritexoffset, old_spriteyoffset;
fixed_t old_spritexscale, old_spriteyscale, old_spritexscale2, old_spriteyscale2;
fixed_t old_spritexoffset, old_spriteyoffset, old_spritexoffset2, old_spriteyoffset2;
struct pslope_s *floorspriteslope; // The slope that the floorsprite is rotated by
struct mprecipsecnode_s *touching_sectorlist; // a linked list of sectors where this object appears
......@@ -527,7 +529,7 @@ void P_SnowThinker(precipmobj_t *mobj);
void P_RainThinker(precipmobj_t *mobj);
void P_NullPrecipThinker(precipmobj_t *mobj);
void P_RemovePrecipMobj(precipmobj_t *mobj);
void P_SetScale(mobj_t *mobj, fixed_t newscale);
void P_SetScale(mobj_t *mobj, fixed_t newscale, boolean instant);
void P_XYMovement(mobj_t *mo);
void P_RingXYMovement(mobj_t *mo);
void P_SceneryXYMovement(mobj_t *mo);
......@@ -537,6 +539,8 @@ boolean P_SceneryZMovement(mobj_t *mo);
void P_PlayerZMovement(mobj_t *mo);
void P_EmeraldManager(void);
mobj_t *P_FindNewPosition(UINT32 oldposition);
extern INT32 modulothing;
#define MAXHUNTEMERALDS 64
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2006 by James Haley
// Copyright (C) 2006-2023 by Sonic Team Junior.
// Copyright (C) 2006-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -878,15 +878,15 @@ static void Polyobj_carryThings(polyobj_t *po, fixed_t dx, fixed_t dy)
{
mobj_t *mo;
blocknode_t *block;
blocknode_t *next = NULL;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue;
block = blocklinks[y * bmapwidth + x];
for (; block; block = block->mnext)
for (block = blocklinks[y * bmapwidth + x]; block != NULL; block = next)
{
mo = block->mobj;
next = block->mnext;
if (mo->lastlook == pomovecount)
continue;
......@@ -927,11 +927,11 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line)
if (!(po->flags & POF_SOLID))
return hitflags;
// adjust linedef bounding box to blockmap, extend by MAXRADIUS
linebox[BOXLEFT] = (unsigned)(line->bbox[BOXLEFT] - bmaporgx - MAXRADIUS) >> MAPBLOCKSHIFT;
linebox[BOXRIGHT] = (unsigned)(line->bbox[BOXRIGHT] - bmaporgx + MAXRADIUS) >> MAPBLOCKSHIFT;
linebox[BOXBOTTOM] = (unsigned)(line->bbox[BOXBOTTOM] - bmaporgy - MAXRADIUS) >> MAPBLOCKSHIFT;
linebox[BOXTOP] = (unsigned)(line->bbox[BOXTOP] - bmaporgy + MAXRADIUS) >> MAPBLOCKSHIFT;
// adjust linedef bounding box to blockmap
linebox[BOXLEFT] = (unsigned)(line->bbox[BOXLEFT] - bmaporgx) >> MAPBLOCKSHIFT;
linebox[BOXRIGHT] = (unsigned)(line->bbox[BOXRIGHT] - bmaporgx) >> MAPBLOCKSHIFT;
linebox[BOXBOTTOM] = (unsigned)(line->bbox[BOXBOTTOM] - bmaporgy) >> MAPBLOCKSHIFT;
linebox[BOXTOP] = (unsigned)(line->bbox[BOXTOP] - bmaporgy) >> MAPBLOCKSHIFT;
// check all mobj blockmap cells the line contacts
for (y = linebox[BOXBOTTOM]; y <= linebox[BOXTOP]; ++y)
......@@ -942,9 +942,11 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line)
{
mobj_t *mo = NULL;
blocknode_t *block = blocklinks[y * bmapwidth + x];
blocknode_t *next = NULL;
for (; block; block = block->mnext)
for (; block != NULL; block = next)
{
next = block->mnext;
mo = block->mobj;
// Don't scroll objects that aren't affected by gravity
......@@ -1115,15 +1117,15 @@ static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta,
{
mobj_t *mo;
blocknode_t *block;
blocknode_t *next = NULL;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue;
block = blocklinks[y * bmapwidth + x];
for (; block; block = block->mnext)
for (block = blocklinks[y * bmapwidth + x]; block != NULL; block = next)
{
mo = block->mobj;
next = block->mnext;
if (mo->lastlook == pomovecount)
continue;
......@@ -1316,7 +1318,7 @@ void Polyobj_InitLevel(void)
// the mobj_t pointers on a queue for use below.
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo = (mobj_t *)th;
......@@ -2032,7 +2034,7 @@ boolean EV_DoPolyObjRotate(polyrotdata_t *prdata)
// create a new thinker
th = Z_Malloc(sizeof(polyrotate_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjRotate;
th->thinker.function = (actionf_p1)T_PolyObjRotate;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
......@@ -2104,7 +2106,7 @@ boolean EV_DoPolyObjMove(polymovedata_t *pmdata)
// create a new thinker
th = Z_Malloc(sizeof(polymove_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjMove;
th->thinker.function = (actionf_p1)T_PolyObjMove;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
......@@ -2166,7 +2168,7 @@ boolean EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
// create a new thinker
th = Z_Malloc(sizeof(polywaypoint_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjWaypoint;
th->thinker.function = (actionf_p1)T_PolyObjWaypoint;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
......@@ -2234,7 +2236,7 @@ static void Polyobj_doSlideDoor(polyobj_t *po, polydoordata_t *doordata)
// allocate and add a new slide door thinker
th = Z_Malloc(sizeof(polyslidedoor_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyDoorSlide;
th->thinker.function = (actionf_p1)T_PolyDoorSlide;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
// point the polyobject to this thinker
......@@ -2285,7 +2287,7 @@ static void Polyobj_doSwingDoor(polyobj_t *po, polydoordata_t *doordata)
// allocate and add a new swing door thinker
th = Z_Malloc(sizeof(polyswingdoor_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyDoorSwing;
th->thinker.function = (actionf_p1)T_PolyDoorSwing;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
// point the polyobject to this thinker
......@@ -2370,7 +2372,7 @@ boolean EV_DoPolyObjDisplace(polydisplacedata_t *prdata)
// create a new thinker
th = Z_Malloc(sizeof(polydisplace_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjDisplace;
th->thinker.function = (actionf_p1)T_PolyObjDisplace;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
......@@ -2419,7 +2421,7 @@ boolean EV_DoPolyObjRotDisplace(polyrotdisplacedata_t *prdata)
// create a new thinker
th = Z_Malloc(sizeof(polyrotdisplace_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjRotDisplace;
th->thinker.function = (actionf_p1)T_PolyObjRotDisplace;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
......@@ -2524,7 +2526,7 @@ boolean EV_DoPolyObjFlag(polyflagdata_t *pfdata)
// create a new thinker
th = Z_Malloc(sizeof(polymove_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjFlag;
th->thinker.function = (actionf_p1)T_PolyObjFlag;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
......@@ -2672,12 +2674,12 @@ boolean EV_DoPolyObjFade(polyfadedata_t *pfdata)
if (po->translucency == pfdata->destvalue)
return true;
if (po->thinker && po->thinker->function.acp1 == (actionf_p1)T_PolyObjFade)
if (po->thinker && po->thinker->function == (actionf_p1)T_PolyObjFade)
P_RemoveThinker(po->thinker);
// create a new thinker
th = Z_Malloc(sizeof(polyfade_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjFade;
th->thinker.function = (actionf_p1)T_PolyObjFade;
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -35,11 +35,11 @@
#pragma interface
#endif
/// \brief Frame flags: only the frame number - 0 to 256 (Frames from 0 to 63, Sprite2 number uses 0 to 127 plus FF_SPR2SUPER)
/// \brief Frame flags: only the frame number - 0 to 256 (Frames from 0 to 255, Sprite2 number uses 0 to 127 plus FF_SPR2SUPER)
#define FF_FRAMEMASK 0xff
/// \brief Frame flags - SPR2: Super sprite2
#define FF_SPR2SUPER 0x80
#define FF_SPR2SUPER SPR2F_SUPER //TODO: 2.3: remove this backwards compat hack
/// \brief Frame flags - SPR2: A change of state at the end of Sprite2 animation
#define FF_SPR2ENDSTATE 0x100
/// \brief Frame flags - SPR2: 50% of starting in middle of Sprite2 animation
......@@ -97,6 +97,11 @@
/// \brief Frame flags - Animate: Start at a random place in the animation (mutually exclusive with above)
#define FF_RANDOMANIM 0x40000000
/// \brief Animation flags: Bits used for the animation ID
#define SPR2F_MASK 0x3FF
/// \brief Animation flags: "Super" flag
#define SPR2F_SUPER 0x400
/** \brief translucency tables
\todo add another asm routine which use the fg and bg indexes in the
......
Source diff could not be displayed: it is too large. Options to address this: view the blob.
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -18,17 +18,24 @@
#pragma interface
#endif
#include "tables.h"
#define NEWSKINSAVES (INT16_MAX) // TODO: 2.3: Delete (Purely for backwards compatibility)
// Persistent storage/archiving.
// These are the load / save game routines.
void P_SaveGame(INT16 mapnum);
void P_SaveNetGame(boolean resending);
boolean P_LoadGame(INT16 mapoverride);
boolean P_LoadNetGame(boolean reloading);
typedef struct
{
unsigned char *buf;
size_t size;
size_t pos;
} save_t;
mobj_t *P_FindNewPosition(UINT32 oldposition);
void P_SaveGame(save_t *save_p, INT16 mapnum);
void P_SaveNetGame(save_t *save_p, boolean resending);
boolean P_LoadGame(save_t *save_p, INT16 mapoverride);
boolean P_LoadNetGame(save_t *save_p, boolean reloading);
typedef struct
{
......@@ -42,6 +49,37 @@ typedef struct
} savedata_t;
extern savedata_t savedata;
extern UINT8 *save_p;
void P_WriteUINT8(save_t *p, UINT8 v);
void P_WriteSINT8(save_t *p, SINT8 v);
void P_WriteUINT16(save_t *p, UINT16 v);
void P_WriteINT16(save_t *p, INT16 v);
void P_WriteUINT32(save_t *p, UINT32 v);
void P_WriteINT32(save_t *p, INT32 v);
void P_WriteChar(save_t *p, char v);
void P_WriteFixed(save_t *p, fixed_t v);
void P_WriteAngle(save_t *p, angle_t v);
void P_WriteStringN(save_t *p, char const *s, size_t n);
void P_WriteStringL(save_t *p, char const *s, size_t n);
void P_WriteString(save_t *p, char const *s);
void P_WriteMem(save_t *p, void const *s, size_t n);
void P_SkipStringN(save_t *p, size_t n);
void P_SkipStringL(save_t *p, size_t n);
void P_SkipString(save_t *p);
UINT8 P_ReadUINT8(save_t *p);
SINT8 P_ReadSINT8(save_t *p);
UINT16 P_ReadUINT16(save_t *p);
INT16 P_ReadINT16(save_t *p);
UINT32 P_ReadUINT32(save_t *p);
INT32 P_ReadINT32(save_t *p);
char P_ReadChar(save_t *p);
fixed_t P_ReadFixed(save_t *p);
angle_t P_ReadAngle(save_t *p);
void P_ReadStringN(save_t *p, char *s, size_t n);
void P_ReadStringL(save_t *p, char *s, size_t n);
void P_ReadString(save_t *p, char *s);
void P_ReadMem(save_t *p, void *s, size_t n);
#endif
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -11,6 +11,9 @@
/// \file p_setup.c
/// \brief Do all the WAD I/O, get map description, set up initial state and misc. LUTs
#include <errno.h>
#include "doomdef.h"
#include "d_main.h"
#include "byteptr.h"
......@@ -108,7 +111,7 @@ vertex_t *vertexes;
seg_t *segs;
sector_t *sectors;
subsector_t *subsectors;
node_t *nodes;
bspnode_t *nodes;
line_t *lines;
side_t *sides;
mapthing_t *mapthings;
......@@ -358,6 +361,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
mapheaderinfo[num]->marathonnext = 0;
mapheaderinfo[num]->startrings = 0;
mapheaderinfo[num]->sstimer = 90;
for (UINT8 n = 0; n < 8; n++)
mapheaderinfo[num]->nightstimer[n] = 0;
mapheaderinfo[num]->ssspheres = 1;
mapheaderinfo[num]->gravity = FRACUNIT/2;
mapheaderinfo[num]->keywords[0] = '\0';
......@@ -525,6 +530,29 @@ UINT32 P_GetScoreForGradeOverall(INT16 map, UINT8 grade)
return score;
}
void P_AddNiGHTSTimes(INT16 i, char *gtext)
{
char *spos = gtext;
for (UINT8 n = 0; n < 8; n++)
{
if (spos != NULL)
{
mapheaderinfo[i]->nightstimer[n] = atoi(spos);
CONS_Debug(DBG_SETUP, "%u ", atoi(spos));
// Grab next comma
spos = strchr(spos, ',');
if (spos)
++spos;
}
else
{
mapheaderinfo[i]->nightstimer[n] = 0;
}
}
}
//
// levelflats
//
......@@ -584,15 +612,15 @@ Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize)
levelflat->type = LEVELFLAT_TEXTURE;
// Look for a flat
int texturenum = R_CheckFlatNumForName(levelflat->name);
int texturenum = R_CheckTextureNumForName(levelflat->name, TEXTURETYPE_FLAT);
if (texturenum < 0)
{
// If we can't find a flat, try looking for a texture!
texturenum = R_CheckTextureNumForName(levelflat->name);
texturenum = R_CheckTextureNumForName(levelflat->name, TEXTURETYPE_TEXTURE);
if (texturenum < 0)
{
// Use "not found" texture
texturenum = R_CheckTextureNumForName("REDWALL");
texturenum = R_CheckTextureNumForName("REDWALL", TEXTURETYPE_TEXTURE);
// Give up?
if (texturenum < 0)
......@@ -667,7 +695,7 @@ void P_ReloadRings(void)
// scan the thinkers to find rings/spheres/hoops to unset
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo = (mobj_t *)th;
......@@ -725,7 +753,7 @@ void P_SwitchSpheresBonusMode(boolean bonustime)
// scan the thinkers to find spheres to switch
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo = (mobj_t *)th;
......@@ -806,13 +834,15 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
static void P_SpawnEmeraldHunt(void)
{
INT32 emer[3], num[MAXHUNTEMERALDS], i, randomkey;
INT32 emer[3], num[MAXHUNTEMERALDS], i, amount, randomkey;
fixed_t x, y, z;
for (i = 0; i < numhuntemeralds; i++)
num[i] = i;
for (i = 0; i < 3; i++)
amount = min(numhuntemeralds, 3);
for (i = 0; i < amount; i++)
{
// generate random index, shuffle afterwards
randomkey = P_RandomKey(numhuntemeralds--);
......@@ -1050,6 +1080,7 @@ static void P_LoadSectors(UINT8 *data)
ss->triggerer = TO_PLAYER;
ss->friction = ORIG_FRICTION;
ss->customargs = NULL;
P_InitializeSector(ss);
}
......@@ -1088,6 +1119,8 @@ static void P_InitializeLinedef(line_t *ld)
ld->callcount = 0;
ld->secportal = UINT32_MAX;
ld->midtexslope = NULL;
// cph 2006/09/30 - fix sidedef errors right away.
// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
for (j = 0; j < 2; j++)
......@@ -1170,6 +1203,8 @@ static void P_LoadLinedefs(UINT8 *data)
if (ld->sidenum[1] == 0xffff)
ld->sidenum[1] = NO_SIDEDEF;
ld->customargs = NULL;
P_InitializeLinedef(ld);
}
}
......@@ -1338,6 +1373,11 @@ static void P_LoadSidedefs(UINT8 *data)
sd->scalex_top = sd->scalex_mid = sd->scalex_bottom = FRACUNIT;
sd->scaley_top = sd->scaley_mid = sd->scaley_bottom = FRACUNIT;
sd->light = sd->light_top = sd->light_mid = sd->light_bottom = 0;
sd->lightabsolute = sd->lightabsolute_top = sd->lightabsolute_mid = sd->lightabsolute_bottom = false;
sd->customargs = NULL;
P_SetSidedefSector(i, (UINT16)SHORT(msd->sector));
// Special info stored in texture fields!
......@@ -1521,15 +1561,46 @@ static void P_LoadThings(UINT8 *data)
mt->z = mt->options >> ZSHIFT;
mt->mobj = NULL;
mt->customargs = NULL;
}
}
// Stores positions for relevant map data spread through a TEXTMAP.
UINT32 mapthingsPos[UINT16_MAX];
UINT32 linesPos[UINT16_MAX];
UINT32 sidesPos[UINT16_MAX];
UINT32 vertexesPos[UINT16_MAX];
UINT32 sectorsPos[UINT16_MAX];
typedef struct textmap_block_s
{
UINT32 *pos;
size_t capacity;
} textmap_block_t;
static textmap_block_t mapthingBlocks;
static textmap_block_t linedefBlocks;
static textmap_block_t sidedefBlocks;
static textmap_block_t vertexBlocks;
static textmap_block_t sectorBlocks;
static void TextmapStorePos(textmap_block_t *blocks, size_t *count)
{
size_t locCount = (*count) + 1;
if (blocks->pos == NULL)
{
// Initial capacity (half of the former one.)
blocks->capacity = UINT16_MAX / 2;
Z_Calloc(sizeof(blocks->pos) * blocks->capacity, PU_LEVEL, &blocks->pos);
}
else if (locCount >= blocks->capacity)
{
// If we hit the list's capacity, make space for 1024 more blocks
blocks->capacity += 1024;
Z_Realloc(blocks->pos, sizeof(blocks->pos) * blocks->capacity, PU_LEVEL, &blocks->pos);
}
blocks->pos[locCount - 1] = M_TokenizerGetEndPos();
(*count) = locCount;
}
// Determine total amount of map data in TEXTMAP.
static boolean TextmapCount(size_t size)
......@@ -1573,15 +1644,15 @@ static boolean TextmapCount(size_t size)
brackets++;
// Check for valid fields.
else if (fastcmp(tkn, "thing"))
mapthingsPos[nummapthings++] = M_TokenizerGetEndPos();
TextmapStorePos(&mapthingBlocks, &nummapthings);
else if (fastcmp(tkn, "linedef"))
linesPos[numlines++] = M_TokenizerGetEndPos();
TextmapStorePos(&linedefBlocks, &numlines);
else if (fastcmp(tkn, "sidedef"))
sidesPos[numsides++] = M_TokenizerGetEndPos();
TextmapStorePos(&sidedefBlocks, &numsides);
else if (fastcmp(tkn, "vertex"))
vertexesPos[numvertexes++] = M_TokenizerGetEndPos();
TextmapStorePos(&vertexBlocks, &numvertexes);
else if (fastcmp(tkn, "sector"))
sectorsPos[numsectors++] = M_TokenizerGetEndPos();
TextmapStorePos(&sectorBlocks, &numsectors);
else
CONS_Alert(CONS_NOTICE, "Unknown field '%s'.\n", tkn);
}
......@@ -1595,6 +1666,89 @@ static boolean TextmapCount(size_t size)
return true;
}
static void ParseTextmapCustomFields(const char* param, const char* val, customargs_t** headptr)
{
if (val[0] == '\0')
return;
//
// GET latest node
//
customargs_t* newnode = Z_Malloc(sizeof(customargs_t), PU_LEVEL, NULL);
if (!newnode)
return;
newnode->next = NULL;
if (*headptr == NULL) {
*headptr = newnode;
}
else {
customargs_t* curr = *headptr;
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = newnode;
}
//
// Setup
//
newnode->name = Z_Malloc(strlen(param + 5) + 1, PU_LEVEL, NULL);
M_Memcpy(newnode->name, param + 5, strlen(param + 5) + 1);
if (fastcmp(val, "true"))
{
newnode->type = UDMF_TYPE_BOOLEAN;
newnode->value.vbool = true;
}
else if (fastcmp(val, "false"))
{
newnode->type = UDMF_TYPE_BOOLEAN;
newnode->value.vbool = false;
}
else
{
char* endptr;
long lval;
float fval;
// Eval integer
errno = 0;
lval = strtol(val, &endptr, 10);
if (*endptr == '\0' && endptr != val && errno == 0) {
newnode->type = UDMF_TYPE_NUMERIC;
newnode->value.vint = lval;
return;
}
// Eval float
errno = 0;
fval = strtof(val, &endptr);
if (*endptr == '\0' && endptr != val && errno == 0) {
newnode->type = UDMF_TYPE_FIXED;
newnode->value.vfloat = FLOAT_TO_FIXED(fval);
return;
}
// Just string
newnode->type = UDMF_TYPE_STRING;
newnode->value.vstring = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL);
M_Memcpy(newnode->value.vstring, val, strlen(val) + 1);
}
}
static void ParseTextmapVertexParameter(UINT32 i, const char *param, const char *val)
{
if (fastcmp(param, "x"))
......@@ -1694,6 +1848,8 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char
sectors[i].floorangle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
else if (fastcmp(param, "rotationceiling"))
sectors[i].ceilingangle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
else if (fastncmp(param, "user_", 5) && strlen(param) > 5)
ParseTextmapCustomFields(param, val, &sectors[i].customargs);
else if (fastcmp(param, "floorplane_a"))
{
textmap_planefloor.defined |= PD_A;
......@@ -1834,6 +1990,10 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char
sectors[i].specialflags |= SSF_JUMPFLIP;
else if (fastcmp(param, "gravityoverride") && fastcmp("true", val))
sectors[i].specialflags |= SSF_GRAVITYOVERRIDE;
else if (fastcmp(param, "nophysics_floor") && fastcmp("true", val))
sectors[i].specialflags |= SSF_NOPHYSICSFLOOR;
else if (fastcmp(param, "nophysics_ceiling") && fastcmp("true", val))
sectors[i].specialflags |= SSF_NOPHYSICSCEILING;
else if (fastcmp(param, "friction"))
sectors[i].friction = FLOAT_TO_FIXED(atof(val));
else if (fastcmp(param, "gravity"))
......@@ -1914,6 +2074,24 @@ static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char
P_SetSidedefSector(i, atol(val));
else if (fastcmp(param, "repeatcnt"))
sides[i].repeatcnt = atol(val);
else if (fastcmp(param, "light"))
sides[i].light = atol(val);
else if (fastcmp(param, "light_top"))
sides[i].light_top = atol(val);
else if (fastcmp(param, "light_mid"))
sides[i].light_mid = atol(val);
else if (fastcmp(param, "light_bottom"))
sides[i].light_bottom = atol(val);
else if (fastcmp(param, "lightabsolute") && fastcmp("true", val))
sides[i].lightabsolute = true;
else if (fastcmp(param, "lightabsolute_top") && fastcmp("true", val))
sides[i].lightabsolute_top = true;
else if (fastcmp(param, "lightabsolute_mid") && fastcmp("true", val))
sides[i].lightabsolute_mid = true;
else if (fastcmp(param, "lightabsolute_bottom") && fastcmp("true", val))
sides[i].lightabsolute_bottom = true;
else if (fastncmp(param, "user_", 5) && strlen(param) > 5)
ParseTextmapCustomFields(param, val, &sides[i].customargs);
}
static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char *val)
......@@ -2008,6 +2186,9 @@ static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char
lines[i].flags |= ML_BOUNCY;
else if (fastcmp(param, "transfer") && fastcmp("true", val))
lines[i].flags |= ML_TFERLINE;
else if (fastncmp(param, "user_", 5) && strlen(param) > 5)
ParseTextmapCustomFields(param, val, &lines[i].customargs);
}
static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *val)
......@@ -2067,6 +2248,8 @@ static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *
return;
mapthings[i].args[argnum] = atol(val);
}
else if (fastncmp(param, "user_", 5) && strlen(param) > 5)
ParseTextmapCustomFields(param, val, &mapthings[i].customargs);
}
/** From a given position table, run a specified parser function through a {}-encapsuled text.
......@@ -2641,6 +2824,22 @@ static void P_WriteTextmap(void)
fprintf(f, "texturemiddle = \"%.*s\";\n", 8, textures[wsides[i].midtexture]->name);
if (wsides[i].repeatcnt != 0)
fprintf(f, "repeatcnt = %d;\n", wsides[i].repeatcnt);
if (wsides[i].light != 0)
fprintf(f, "light = %d;\n", wsides[i].light);
if (wsides[i].light_top != 0)
fprintf(f, "light_top = %d;\n", wsides[i].light_top);
if (wsides[i].light_mid != 0)
fprintf(f, "light_mid = %d;\n", wsides[i].light_mid);
if (wsides[i].light_bottom != 0)
fprintf(f, "light_bottom = %d;\n", wsides[i].light_bottom);
if (wsides[i].lightabsolute)
fprintf(f, "lightabsolute = true;\n");
if (wsides[i].lightabsolute_top)
fprintf(f, "lightabsolute_top = true;\n");
if (wsides[i].lightabsolute_mid)
fprintf(f, "lightabsolute_mid = true;\n");
if (wsides[i].lightabsolute_bottom)
fprintf(f, "lightabsolute_bottom = true;\n");
fprintf(f, "}\n");
fprintf(f, "\n");
}
......@@ -2707,6 +2906,11 @@ static void P_WriteTextmap(void)
INT32 fadecolor = P_RGBAToColor(wsectors[i].extra_colormap->fadergba);
UINT8 fadealpha = R_GetRgbaA(wsectors[i].extra_colormap->fadergba);
// For now, convert alpha from new (0-255) to old 'A-Z' (0-25) range
// TODO: remove this limitation in a backwards-compatible way (UDMF versioning?)
lightalpha /= 10;
fadealpha /= 10;
if (lightcolor != 0)
fprintf(f, "lightcolor = %d;\n", lightcolor);
if (lightalpha != 25)
......@@ -2892,7 +3096,7 @@ static void P_LoadTextmap(void)
side_t *sd;
mapthing_t *mt;
CONS_Alert(CONS_NOTICE, "UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented.\n");
//CONS_Alert(CONS_NOTICE, "UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented.\n");
/// Given the UDMF specs, some fields are given a default value.
/// If an element's field has a default value set, it is omitted
......@@ -2906,7 +3110,7 @@ static void P_LoadTextmap(void)
vt->floorzset = vt->ceilingzset = false;
vt->floorz = vt->ceilingz = 0;
TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
TextmapParse(vertexBlocks.pos[i], i, ParseTextmapVertexParameter);
if (vt->x == INT32_MAX)
I_Error("P_LoadTextmap: vertex %s has no x value set!\n", sizeu1(i));
......@@ -2950,6 +3154,7 @@ static void P_LoadTextmap(void)
sc->triggerer = TO_PLAYER;
sc->friction = ORIG_FRICTION;
sc->customargs = NULL;
textmap_colormap.used = false;
textmap_colormap.lightcolor = 0;
......@@ -2963,12 +3168,13 @@ static void P_LoadTextmap(void)
textmap_planefloor.defined = 0;
textmap_planeceiling.defined = 0;
TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
TextmapParse(sectorBlocks.pos[i], i, ParseTextmapSectorParameter);
P_InitializeSector(sc);
if (textmap_colormap.used)
{
// Convert alpha values from old 0-25 (A-Z) range to 0-255 range
// Convert alpha values from old 'A-Z' (0-25) range to new UINT8 (0-255) range
// TODO: remove this limitation in a backwards-compatible way (UDMF versioning?)
UINT8 lightalpha = (textmap_colormap.lightalpha * 102) / 10;
UINT8 fadealpha = (textmap_colormap.fadealpha * 102) / 10;
......@@ -2981,12 +3187,16 @@ static void P_LoadTextmap(void)
{
sc->f_slope = P_MakeSlopeViaEquationConstants(textmap_planefloor.a, textmap_planefloor.b, textmap_planefloor.c, textmap_planefloor.d);
sc->hasslope = true;
if (sc->specialflags & SSF_NOPHYSICSFLOOR)
sc->f_slope->flags |= SL_NOPHYSICS;
}
if (textmap_planeceiling.defined == (PD_A|PD_B|PD_C|PD_D))
{
sc->c_slope = P_MakeSlopeViaEquationConstants(textmap_planeceiling.a, textmap_planeceiling.b, textmap_planeceiling.c, textmap_planeceiling.d);
sc->hasslope = true;
if (sc->specialflags & SSF_NOPHYSICSCEILING)
sc->c_slope->flags |= SL_NOPHYSICS;
}
TextmapFixFlatOffsets(sc);
......@@ -3006,8 +3216,9 @@ static void P_LoadTextmap(void)
ld->executordelay = 0;
ld->sidenum[0] = NO_SIDEDEF;
ld->sidenum[1] = NO_SIDEDEF;
ld->customargs = NULL;
TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
TextmapParse(linedefBlocks.pos[i], i, ParseTextmapLinedefParameter);
if (!ld->v1)
I_Error("P_LoadTextmap: linedef %s has no v1 value set!\n", sizeu1(i));
......@@ -3033,8 +3244,11 @@ static void P_LoadTextmap(void)
sd->bottomtexture = R_TextureNumForName("-");
sd->sector = NULL;
sd->repeatcnt = 0;
sd->light = sd->light_top = sd->light_mid = sd->light_bottom = 0;
sd->lightabsolute = sd->lightabsolute_top = sd->lightabsolute_mid = sd->lightabsolute_bottom = false;
sd->customargs = NULL;
TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);
TextmapParse(sidedefBlocks.pos[i], i, ParseTextmapSidedefParameter);
if (!sd->sector)
I_Error("P_LoadTextmap: sidedef %s has no sector value set!\n", sizeu1(i));
......@@ -3057,8 +3271,9 @@ static void P_LoadTextmap(void)
memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
mt->mobj = NULL;
mt->customargs = NULL;
TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter);
TextmapParse(mapthingBlocks.pos[i], i, ParseTextmapThingParameter);
}
}
......@@ -3257,7 +3472,7 @@ static void P_LoadNodes(UINT8 *data)
{
UINT8 j, k;
mapnode_t *mn = (mapnode_t*)data;
node_t *no = nodes;
bspnode_t *no = nodes;
size_t i;
for (i = 0; i < numnodes; i++, no++, mn++)
......@@ -3328,8 +3543,6 @@ static void P_InitializeSeg(seg_t *seg)
seg->lightmaps = NULL; // list of static lightmap for this seg
#endif
seg->numlights = 0;
seg->rlights = NULL;
seg->polyseg = NULL;
seg->dontrenderme = false;
}
......@@ -3378,13 +3591,13 @@ typedef enum {
} nodetype_t;
// Find out the BSP format.
static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata, char signature[4 + 1])
{
boolean supported[NUMNODETYPES] = {0};
nodetype_t nodetype = NT_UNSUPPORTED;
char signature[4 + 1];
*nodedata = NULL;
signature[0] = signature[4] = '\0';
if (udmf)
{
......@@ -3393,7 +3606,7 @@ static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
if (virtznodes && virtznodes->size)
{
*nodedata = virtznodes->data;
supported[NT_XGLN] = supported[NT_XGL3] = true;
supported[NT_XGLN] = supported[NT_XGL2] = supported[NT_XGL3] = true;
}
}
else
......@@ -3415,9 +3628,9 @@ static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
virtssectors = vres_Find(virt, "SSECTORS");
if (virtssectors && virtssectors->size)
{ // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature.
{ // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump (it is confusing, yeah), and has a signature.
*nodedata = virtssectors->data;
supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL3] = true;
supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL2] = supported[NT_XGL3] = true;
}
else
{ // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature.
......@@ -3437,19 +3650,42 @@ static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
}
M_Memcpy(signature, *nodedata, 4);
signature[4] = '\0';
(*nodedata) += 4;
if (!strcmp(signature, "XNOD"))
nodetype = NT_XNOD;
else if (!strcmp(signature, "ZNOD"))
nodetype = NT_ZNOD;
else if (!strcmp(signature, "XGLN"))
nodetype = NT_XGLN;
else if (!strcmp(signature, "ZGLN"))
nodetype = NT_ZGLN;
else if (!strcmp(signature, "XGL3"))
nodetype = NT_XGL3;
// Identify node format from its starting signature.
if (memcmp(&signature[1], "NOD", 3) == 0) // ZDoom extended nodes
{
if (signature[0] == 'X')
{
nodetype = NT_XNOD; // Uncompressed
}
else if (signature[0] == 'Z')
{
nodetype = NT_ZNOD; // Compressed
}
}
else if (memcmp(&signature[1], "GL", 2) == 0) // GL nodes
{
switch (signature[0])
{
case 'X': // Uncompressed
switch (signature[3])
{
case 'N': nodetype = NT_XGLN; break; // GL nodes
case '2': nodetype = NT_XGL2; break; // Version 2 GL nodes
case '3': nodetype = NT_XGL3; break; // Version 3 GL nodes
}
break;
case 'Z': // Compressed
switch (signature[3])
{
case 'N': nodetype = NT_ZGLN; break; // GL nodes (compressed)
case '2': nodetype = NT_ZGL2; break; // Version 2 GL nodes (compressed)
case '3': nodetype = NT_ZGL3; break; // Version 3 GL nodes (compressed)
}
break;
}
}
return supported[nodetype] ? nodetype : NT_UNSUPPORTED;
}
......@@ -3461,7 +3697,6 @@ static boolean P_LoadExtraVertices(UINT8 **data)
UINT32 xtrvrtx = READUINT32((*data));
line_t* ld = lines;
vertex_t *oldpos = vertexes;
ssize_t offset;
size_t i;
if (numvertexes != origvrtx) // If native vertex count doesn't match node original vertex count, bail out (broken data?).
......@@ -3476,12 +3711,11 @@ static boolean P_LoadExtraVertices(UINT8 **data)
// If extra vertexes were generated, reallocate the vertex array and fix the pointers.
numvertexes += xtrvrtx;
vertexes = Z_Realloc(vertexes, numvertexes*sizeof(*vertexes), PU_LEVEL, NULL);
offset = (size_t)(vertexes - oldpos);
for (i = 0, ld = lines; i < numlines; i++, ld++)
{
ld->v1 += offset;
ld->v2 += offset;
ld->v1 = &vertexes[ld->v1 - oldpos];
ld->v2 = &vertexes[ld->v2 - oldpos];
}
// Read extra vertex data.
......@@ -3519,6 +3753,7 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
switch (nodetype)
{
case NT_XGLN:
case NT_XGL2:
case NT_XGL3:
for (m = 0; m < (size_t)subsectors[i].numlines; m++, k++)
{
......@@ -3530,7 +3765,7 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
READUINT32((*data)); // partner, can be ignored by software renderer
if (nodetype == NT_XGL3)
if (nodetype != NT_XGLN)
{
UINT32 linenum = READUINT32((*data));
if (linenum != 0xFFFFFFFF && linenum >= numlines)
......@@ -3613,7 +3848,7 @@ static UINT16 ShrinkNodeID(UINT32 x) {
static void P_LoadExtendedNodes(UINT8 **data, nodetype_t nodetype)
{
node_t *mn;
bspnode_t *mn;
size_t i, j, k;
boolean xgl3 = (nodetype == NT_XGL3);
......@@ -3641,8 +3876,9 @@ static void P_LoadExtendedNodes(UINT8 **data, nodetype_t nodetype)
static void P_LoadMapBSP(const virtres_t *virt)
{
char signature[4 + 1];
UINT8 *nodedata = NULL;
nodetype_t nodetype = P_GetNodetype(virt, &nodedata);
nodetype_t nodetype = P_GetNodetype(virt, &nodedata, signature);
switch (nodetype)
{
......@@ -3674,6 +3910,7 @@ static void P_LoadMapBSP(const virtres_t *virt)
}
case NT_XNOD:
case NT_XGLN:
case NT_XGL2:
case NT_XGL3:
if (!P_LoadExtraVertices(&nodedata))
return;
......@@ -3682,10 +3919,13 @@ static void P_LoadMapBSP(const virtres_t *virt)
P_LoadExtendedNodes(&nodedata, nodetype);
break;
default:
CONS_Alert(CONS_WARNING, "Unsupported BSP format detected.\n");
if (isprint(signature[0]) && isprint(signature[1]) && isprint(signature[2]) && isprint(signature[3]))
{
I_Error("Unsupported BSP format '%s' detected!\n", signature);
return;
}
return;
I_Error("Unknown BSP format detected!\n");
}
}
// Split from P_LoadBlockMap for convenience
......@@ -7198,7 +7438,7 @@ void P_RespawnThings(void)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (think->removing)
continue;
P_RemoveMobj((mobj_t *)think);
}
......@@ -7234,11 +7474,11 @@ static void P_RunLevelScript(const char *scriptname)
return;
}
COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE));
COM_BufInsertTextEx(W_CacheLumpNum(lumpnum, PU_CACHE), COM_LUA);
}
else
{
COM_BufAddText(va("exec %s\n", scriptname));
COM_ExecFile(scriptname, COM_LUA, false);
}
COM_BufExecute(); // Run it!
}
......@@ -7510,7 +7750,6 @@ static void P_InitCamera(void)
CV_SetValue(&cv_analog[1], 0);
displayplayer = consoleplayer; // Start with your OWN view, please!
}
if (twodlevel)
{
......@@ -7526,6 +7765,7 @@ static void P_InitCamera(void)
CV_SetValue(&cv_analog[1], true);
}
}
}
static void P_RunSpecialStageWipe(void)
{
......@@ -7533,7 +7773,7 @@ static void P_RunSpecialStageWipe(void)
tic_t endtime = starttime + (3*TICRATE)/2;
tic_t nowtime;
S_StartSound(NULL, sfx_s3kaf);
S_StartSoundFromEverywhere(sfx_s3kaf);
// Fade music! Time it to S3KAF: 0.25 seconds is snappy.
if (RESETMUSIC ||
......@@ -7718,6 +7958,10 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
maptol = mapheaderinfo[gamemap-1]->typeoflevel;
gametyperules = gametypedefaultrules[gametype];
// clear the target on map change, since the object will be invalidated
P_SetTarget(&ticcmd_ztargetfocus[0], NULL);
P_SetTarget(&ticcmd_ztargetfocus[1], NULL);
CON_Drawer(); // let the user know what we are going to do
I_FinishUpdate(); // page flip or blit buffer
......@@ -7822,7 +8066,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
if (ranspecialwipe == 2)
{
pausedelay = -3; // preticker plus one
S_StartSound(NULL, sfx_s3k73);
S_StartSoundFromEverywhere(sfx_s3k73);
}
// Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
......@@ -7867,6 +8111,9 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
// Free GPU textures before freeing patches.
if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
HWR_ClearAllTextures();
// Delete light table textures
HWR_ClearLightTables();
#endif
Patch_FreeTag(PU_PATCH_LOWPRIORITY);
......@@ -7913,7 +8160,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
{
S_StartSound(NULL, sfx_s3k68);
S_StartSoundFromEverywhere(sfx_s3k68);
G_SaveGameData(clientGamedata);
}
else if (!reloadinggamestate)
......@@ -7978,6 +8225,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
R_PrecacheLevel();
nextmapoverride = 0;
keepcutscene = false;
skipstats = 0;
levelloading = false;
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -87,5 +87,6 @@ UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare);
UINT8 P_HasGrades(INT16 map, UINT8 mare);
UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade);
UINT32 P_GetScoreForGradeOverall(INT16 map, UINT8 grade);
void P_AddNiGHTSTimes(INT16 i, char *gtext);
#endif
......@@ -355,7 +355,7 @@ static boolean P_CrossBSPNode(INT32 bspnum, register los_t *los)
{
while (!(bspnum & NF_SUBSECTOR))
{
register node_t *bsp = nodes + bspnum;
register bspnode_t *bsp = nodes + bspnum;
INT32 side = P_DivlineSide(los->strace.x,los->strace.y,(divline_t *)bsp)&1;
if (side == P_DivlineSide(los->t2x, los->t2y, (divline_t *) bsp))
bspnum = bsp->children[side]; // doesn't touch the other side
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2009 by Stephen McGranahan.
// Copyright (C) 2015-2023 by Sonic Team Junior.
// Copyright (C) 2015-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -28,6 +28,8 @@
pslope_t *slopelist = NULL;
UINT16 slopecount = 0;
static void P_UpdateMidtextureSlopesForSector(sector_t *sector);
// Calculate line normal
void P_CalculateSlopeNormal(pslope_t *slope)
{
......@@ -180,7 +182,7 @@ void T_DynamicSlopeLine (dynlineplanethink_t* th)
pslope_t* slope = th->slope;
line_t* srcline = th->sourceline;
fixed_t zdelta;
fixed_t zdelta, oldoz = slope->o.z;
switch(th->type) {
case DP_FRONTFLOOR:
......@@ -207,11 +209,14 @@ void T_DynamicSlopeLine (dynlineplanethink_t* th)
return;
}
if (slope->zdelta != FixedDiv(zdelta, th->extent)) {
if (slope->zdelta != FixedDiv(zdelta, th->extent) || oldoz != slope->o.z) {
slope->zdelta = FixedDiv(zdelta, th->extent);
slope->zangle = R_PointToAngle2(0, 0, th->extent, -zdelta);
slope->moved = true;
P_CalculateSlopeNormal(slope);
P_UpdateMidtextureSlopesForSector(srcline->frontsector);
if (srcline->backsector)
P_UpdateMidtextureSlopesForSector(srcline->backsector);
}
}
......@@ -232,12 +237,18 @@ void T_DynamicSlopeVert (dynvertexplanethink_t* th)
}
ReconfigureViaVertexes(th->slope, th->vex[0], th->vex[1], th->vex[2]);
for (i = 0; i < 3; i++)
{
if (th->secs[i])
P_UpdateMidtextureSlopesForSector(th->secs[i]);
}
}
static inline void P_AddDynLineSlopeThinker (pslope_t* slope, dynplanetype_t type, line_t* sourceline, fixed_t extent)
{
dynlineplanethink_t* th = Z_Calloc(sizeof (*th), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_DynamicSlopeLine;
th->thinker.function = (actionf_p1)T_DynamicSlopeLine;
th->slope = slope;
th->type = type;
th->sourceline = sourceline;
......@@ -253,7 +264,7 @@ static inline void P_AddDynVertexSlopeThinker (pslope_t* slope, const INT16 tags
dynvertexplanethink_t* th = Z_Calloc(sizeof (*th), PU_LEVSPEC, NULL);
size_t i;
INT32 l;
th->thinker.function.acp1 = (actionf_p1)T_DynamicSlopeVert;
th->thinker.function = (actionf_p1)T_DynamicSlopeVert;
th->slope = slope;
for (i = 0; i < 3; i++) {
......@@ -758,6 +769,111 @@ pslope_t *P_MakeSlopeViaEquationConstants(const double a, const double b, const
return ret;
}
static pslope_t *P_GetReferenceSlopeForMidtexture(line_t *line)
{
if (line->flags & ML_MIDPEG)
{
// Line has ML_MIDPEG, so use the floor slope
fixed_t frontheight = P_GetSectorFloorZAt(line->frontsector, line->v1->x, line->v1->y);
fixed_t backheight = P_GetSectorFloorZAt(line->backsector, line->v1->x, line->v1->y);
if (frontheight > backheight)
{
return line->frontsector->f_slope;
}
else
{
return line->backsector->f_slope;
}
}
else
{
// Line does not have ML_MIDPEG, so use the ceiling slope
fixed_t frontheight = P_GetSectorCeilingZAt(line->frontsector, line->v1->x, line->v1->y);
fixed_t backheight = P_GetSectorCeilingZAt(line->backsector, line->v1->x, line->v1->y);
if (frontheight < backheight)
{
return line->frontsector->c_slope;
}
else
{
return line->backsector->c_slope;
}
}
return NULL;
}
// Updates a slope for a solid midtexture based on the slope of the sector it's in.
static void P_UpdateSolidMidtextureSlope(line_t *line, pslope_t *ref)
{
pslope_t *slope = line->midtexslope;
if (ref == NULL)
return;
// Set origin
vector3_t origin;
origin.x = line->v1->x;
origin.y = line->v1->y;
origin.z = P_GetSlopeZAt(ref, origin.x, origin.y);
FV3_Copy(&slope->o, &origin);
// Get where the line ends
vector3_t point;
point.x = line->v2->x;
point.y = line->v2->y;
point.z = P_GetSlopeZAt(ref, point.x, point.y);
// Get length of the line
fixed_t extent = R_PointToDist2(0, 0, line->dx, line->dy);
// Precalculate variables
slope->zdelta = FixedDiv(origin.z - point.z, extent);
slope->zangle = R_PointToAngle2(0, origin.z, extent, point.z);
slope->xydirection = line->angle;
// Precalculate the direction
vector2_t dir;
dir.x = FixedMul(FINECOSINE(slope->zangle >> ANGLETOFINESHIFT), FINECOSINE((slope->xydirection+ANGLE_180) >> ANGLETOFINESHIFT));
dir.y = FixedMul(FINECOSINE(slope->zangle >> ANGLETOFINESHIFT), FINESINE((slope->xydirection+ANGLE_180) >> ANGLETOFINESHIFT));
FV2_Copy(&slope->d, &dir);
P_CalculateSlopeNormal(slope);
// Calling P_CalculateSlopeVectors is not necessary.
slope->moved = true;
}
// Goes through every line in the sector and updates the midtexture slope if it is present
static void P_UpdateMidtextureSlopesForSector(sector_t *sector)
{
for (size_t i = 0; i < sector->linecount; i++)
{
if (sector->lines[i]->midtexslope != NULL)
P_UpdateSolidMidtextureSlope(sector->lines[i], P_GetReferenceSlopeForMidtexture(sector->lines[i]));
}
}
// Creates a solid midtexture slope for the line if possible
static void P_CreateSolidMidtextureSlope(line_t *line)
{
if (line->backsector == NULL) // Ignore single-sided lines (of course)
return;
if ((line->flags & ML_MIDSOLID) == 0) // Ignore if the midtexture is not solid
return;
pslope_t *ref = P_GetReferenceSlopeForMidtexture(line);
if (ref)
{
line->midtexslope = Slope_Add(ref->flags & SL_NOPHYSICS);
P_UpdateSolidMidtextureSlope(line, ref);
}
}
/// Initializes and reads the slopes from the map data.
void P_SpawnSlopes(const boolean fromsave) {
size_t i;
......@@ -785,14 +901,21 @@ void P_SpawnSlopes(const boolean fromsave) {
/// Copies slopes from tagged sectors via line specials.
/// \note Doesn't actually copy, but instead they share the same pointers.
// Also, creates midtexture slopes.
for (i = 0; i < numlines; i++)
switch (lines[i].special)
{
line_t *line = &lines[i];
switch (line->special)
{
case 720:
P_CopySectorSlope(&lines[i]);
P_CopySectorSlope(line);
default:
break;
}
P_CreateSolidMidtextureSlope(line);
}
}
/// Initializes slopes.
......@@ -810,10 +933,10 @@ void P_InitSlopes(void)
// Returns the height of the sloped plane at (x, y) as a fixed_t
fixed_t P_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y)
{
fixed_t dist = FixedMul(x - slope->o.x, slope->d.x) +
FixedMul(y - slope->o.y, slope->d.y);
fixed_t dist = FixedMul(x - slope->o.x, slope->d.x) / 2 +
FixedMul(y - slope->o.y, slope->d.y) / 2;
return slope->o.z + FixedMul(dist, slope->zdelta);
return slope->o.z + FixedMul(dist, slope->zdelta) * 2;
}
// Like P_GetSlopeZAt but falls back to z if slope is NULL
......@@ -923,15 +1046,37 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope)
{
vector3_t slopemom, axis;
angle_t ang;
angle_t advanceAng = ANG15;
const boolean upwards = (slope->zangle < ANGLE_180);
if (slope->flags & SL_NOPHYSICS)
return 0;
// If there's physics, time for launching.
// Doesn't kill the vertical momentum as much as P_SlopeLaunch does.
ang = slope->zangle + ANG15*((slope->zangle > 0) ? 1 : -1);
if (ang > ANGLE_90 && ang < ANGLE_180)
ang = ((slope->zangle > 0) ? ANGLE_90 : InvAngle(ANGLE_90)); // hard cap of directly upwards
ang = slope->zangle;
// for the time being, let's pretend the slope inclines upwards only
if (!upwards)
{
ang += ANGLE_180;
}
// angles past 90 degrees need to shrink to get closer to 90 degrees
if (ang > ANGLE_90)
{
advanceAng = InvAngle(advanceAng);
}
// now we set the actual final angle
if ((ang > ANGLE_90) != (ang + advanceAng > ANGLE_90)) // does advancing the angle push it past directly upwards?
{
ang = (upwards ? ANGLE_90 : InvAngle(ANGLE_90)); // hard cap of directly upwards
}
else
{
ang = slope->zangle + advanceAng;
}
slopemom.x = mo->momx;
slopemom.y = mo->momy;
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2009 by Stephen McGranahan.
// Copyright (C) 2015-2023 by Sonic Team Junior.
// Copyright (C) 2015-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -135,22 +135,24 @@ void P_ParseAnimationDefintion(SINT8 istexture);
static boolean P_FindTextureForAnimation(anim_t *anim, animdef_t *animdef)
{
if (R_CheckTextureNumForName(animdef->startname) == -1)
INT32 start = R_CheckTextureNumForName(animdef->startname, TEXTURETYPE_TEXTURE);
if (start == -1)
return false;
anim->picnum = R_TextureNumForName(animdef->endname);
anim->basepic = R_TextureNumForName(animdef->startname);
anim->basepic = start;
anim->picnum = R_CheckTextureNumForName(animdef->endname, TEXTURETYPE_TEXTURE);
return true;
}
static boolean P_FindFlatForAnimation(anim_t *anim, animdef_t *animdef)
{
if (R_CheckFlatNumForName(animdef->startname) == -1)
INT32 start = R_CheckTextureNumForName(animdef->startname, TEXTURETYPE_FLAT);
if (start == -1)
return false;
anim->picnum = R_CheckFlatNumForName(animdef->endname);
anim->basepic = R_CheckFlatNumForName(animdef->startname);
anim->basepic = start;
anim->picnum = R_CheckTextureNumForName(animdef->endname, TEXTURETYPE_FLAT);
return true;
}
......@@ -363,7 +365,7 @@ void P_ParseAnimationDefintion(SINT8 istexture)
// Increase the size to make room for the new animation definition
maxanims++;
animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL);
strncpy(animdefs[i].startname, animdefsToken, 9);
strncpy(animdefs[i].startname, animdefsToken, sizeof(animdefs[i].startname)-1);
}
// animdefs[i].startname is now set to animdefsToken either way.
......@@ -1042,7 +1044,7 @@ static boolean PolyFade(line_t *line)
// Prevent continuous execs from interfering on an existing fade
if (!(line->args[3] & TMPF_OVERRIDE)
&& po->thinker
&& po->thinker->function.acp1 == (actionf_p1)T_PolyObjFade)
&& po->thinker->function == (actionf_p1)T_PolyObjFade)
{
CONS_Debug(DBG_POLYOBJ, "Line type 492 Executor: Fade PolyObject thinker already exists\n");
return 0;
......@@ -1273,7 +1275,7 @@ static void P_AddExecutorDelay(line_t *line, mobj_t *mobj, sector_t *sector)
e = Z_Calloc(sizeof (*e), PU_LEVSPEC, NULL);
e->thinker.function.acp1 = (actionf_p1)T_ExecutorDelay;
e->thinker.function = (actionf_p1)T_ExecutorDelay;
e->line = line;
e->sector = sector;
e->timer = delay;
......@@ -1970,23 +1972,23 @@ static void P_PlaySFX(INT32 sfxnum, mobj_t *mo, sector_t *callsec, INT16 tag, te
{
case TMSS_TRIGGERMOBJ: // play the sound from mobj that triggered it
if (mo)
S_StartSound(mo, sfxnum);
S_StartSoundFromMobj(mo, sfxnum);
break;
case TMSS_TRIGGERSECTOR: // play the sound from calling sector's soundorg
if (callsec)
S_StartSound(&callsec->soundorg, sfxnum);
S_StartSoundFromSector(callsec, sfxnum);
else if (mo)
S_StartSound(&mo->subsector->sector->soundorg, sfxnum);
S_StartSoundFromSector(mo->subsector->sector, sfxnum);
break;
case TMSS_NOWHERE: // play the sound from nowhere
S_StartSound(NULL, sfxnum);
S_StartSoundFromEverywhere(sfxnum);
break;
case TMSS_TAGGEDSECTOR: // play the sound from tagged sectors' soundorgs
{
INT32 secnum;
TAG_ITER_SECTORS(tag, secnum)
S_StartSound(&sectors[secnum].soundorg, sfxnum);
S_StartSoundFromSector(&sectors[secnum], sfxnum);
break;
}
default:
......@@ -2033,7 +2035,7 @@ void P_SwitchWeather(INT32 weathernum)
for (think = thlist[THINK_PRECIP].next; think != &thlist[THINK_PRECIP]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker)
if (think->function != (actionf_p1)P_NullPrecipThinker)
continue; // not a precipmobj thinker
precipmobj = (precipmobj_t *)think;
......@@ -2049,7 +2051,7 @@ void P_SwitchWeather(INT32 weathernum)
for (think = thlist[THINK_PRECIP].next; think != &thlist[THINK_PRECIP]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker)
if (think->function != (actionf_p1)P_NullPrecipThinker)
continue; // not a precipmobj thinker
precipmobj = (precipmobj_t *)think;
......@@ -2066,7 +2068,7 @@ void P_SwitchWeather(INT32 weathernum)
precipmobj->precipflags &= ~PCF_INVISIBLE;
precipmobj->precipflags |= PCF_RAIN;
//think->function.acp1 = (actionf_p1)P_RainThinker;
//think->function = (actionf_p1)P_RainThinker;
}
else if (weathernum == PRECIP_SNOW) // Rain To Snow
{
......@@ -2091,11 +2093,11 @@ void P_SwitchWeather(INT32 weathernum)
precipmobj->precipflags &= ~(PCF_INVISIBLE|PCF_RAIN);
//think->function.acp1 = (actionf_p1)P_SnowThinker;
//think->function = (actionf_p1)P_SnowThinker;
}
else // Remove precip, but keep it around for reuse.
{
//think->function.acp1 = (actionf_p1)P_NullPrecipThinker;
//think->function = (actionf_p1)P_NullPrecipThinker;
precipmobj->precipflags |= PCF_INVISIBLE;
}
......@@ -2405,11 +2407,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
y = line->args[3] << FRACBITS;
z = line->args[4] << FRACBITS;
P_UnsetThingPosition(mo);
mo->x += x;
mo->y += y;
mo->z += z;
P_SetThingPosition(mo);
P_SetOrigin(mo, mo->x + x, mo->y + y, mo->z + z);
if (mo->player)
{
......@@ -2417,6 +2415,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
P_SetOrigin(bot, bot->x + x, bot->y + y, bot->z + z);
if (splitscreen && mo->player == &players[secondarydisplayplayer] && camera2.chase)
{
camera2.reset = true;
camera2.x += x;
camera2.y += y;
camera2.z += z;
......@@ -2424,6 +2423,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
}
else if (camera.chase && mo->player == &players[displayplayer])
{
camera.reset = true;
camera.x += x;
camera.y += y;
camera.z += z;
......@@ -2448,7 +2448,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
P_Teleport(bot, dest->x, dest->y, dest->z, angle, !silent, keepmomentum);
P_Teleport(mo, dest->x, dest->y, dest->z, angle, !silent, keepmomentum);
if (!silent)
S_StartSound(dest, sfx_mixup); // Play the 'bowrwoosh!' sound
S_StartSoundFromMobj(dest, sfx_mixup); // Play the 'bowrwoosh!' sound
}
}
break;
......@@ -2548,7 +2548,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
char *text = Z_Malloc(len + 1, PU_CACHE, NULL);
memcpy(text, lump, len);
text[len] = '\0';
COM_BufInsertText(text);
COM_BufInsertTextEx(text, COM_LUA);
Z_Free(text);
}
}
......@@ -2761,7 +2761,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)T_Scroll)
if (th->function != (actionf_p1)T_Scroll)
continue;
scroller = (scroll_t *)th;
......@@ -2893,7 +2893,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
{
S_StartSound(NULL, sfx_s3k68);
S_StartSoundFromEverywhere(sfx_s3k68);
G_SaveGameData(clientGamedata); // only save if unlocked something
}
}
......@@ -3644,7 +3644,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
if (mo2->type != MT_EGGTRAP)
continue;
if (mo2->thinker.function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (mo2->thinker.removing)
continue;
P_KillMobj(mo2, NULL, mo, 0);
......@@ -3684,7 +3684,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
case 466: // Set level failure state
{
if (line->args[1])
if (line->args[0])
{
stagefailed = false;
CONS_Debug(DBG_GAMELOGIC, "Stage can be completed successfully!\n");
......@@ -3844,7 +3844,7 @@ void P_SetupSignExit(player_t *player)
P_SetObjectMomZ(thing, 12*FRACUNIT, false);
P_SetMobjState(thing, S_SIGNSPIN1);
if (thing->info->seesound)
S_StartSound(thing, thing->info->seesound);
S_StartSoundFromMobj(thing, thing->info->seesound);
++numfound;
}
......@@ -3856,7 +3856,7 @@ void P_SetupSignExit(player_t *player)
// spin all signposts in the level then.
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (think->removing)
continue;
thing = (mobj_t *)think;
......@@ -3875,7 +3875,7 @@ void P_SetupSignExit(player_t *player)
P_SetObjectMomZ(thing, 12*FRACUNIT, false);
P_SetMobjState(thing, S_SIGNSPIN1);
if (thing->info->seesound)
S_StartSound(thing, thing->info->seesound);
S_StartSoundFromMobj(thing, thing->info->seesound);
++numfound;
}
......@@ -3894,7 +3894,7 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (think->removing)
continue;
mo = (mobj_t *)think;
......@@ -4397,7 +4397,7 @@ static void P_ProcessEggCapsule(player_t *player, sector_t *sector)
// The chimps are my friends.. heeheeheheehehee..... - LouisJM
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
if (mo2->type != MT_EGGTRAP)
......@@ -4511,7 +4511,7 @@ static void P_ProcessSpeedPad(player_t *player, sector_t *sector, sector_t *rove
if (!sfxnum)
sfxnum = sfx_spdpad;
S_StartSound(player->mo, sfxnum);
S_StartSoundFromMobj(player->mo, sfxnum);
}
static void P_ProcessSpecialStagePit(player_t* player)
......@@ -4575,6 +4575,10 @@ static void P_ProcessExitSector(player_t *player, mtag_t sectag)
if (lines[lineindex].args[1] & TMEF_SKIPTALLY)
skipstats = 1;
//skip stats actually skips post-level cutscenes.
if (lines[lineindex].args[1] & TMEF_KEEPCUTSCENE)
keepcutscene = true;
}
static void P_ProcessTeamBase(player_t *player, boolean redteam)
......@@ -4603,9 +4607,9 @@ static void P_ProcessTeamBase(player_t *player, boolean redteam)
HU_DoCEcho(va(M_GetText("%s%s\200\\CAPTURED THE %s%s FLAG\200.\\\\\\\\"), redteam ? "\205" : "\204", player_names[player-players], redteam ? "\204" : "\205", redteam ? "BLUE" : "RED"));
if (splitscreen || players[consoleplayer].ctfteam == (redteam ? 1 : 2))
S_StartSound(NULL, sfx_flgcap);
S_StartSoundFromEverywhere(sfx_flgcap);
else if (players[consoleplayer].ctfteam == (redteam ? 2 : 1))
S_StartSound(NULL, sfx_lose);
S_StartSoundFromEverywhere(sfx_lose);
mo = P_SpawnMobj(player->mo->x,player->mo->y,player->mo->z, redteam ? MT_BLUEFLAG : MT_REDFLAG);
player->gotflag &= ~(redteam ? GF_BLUEFLAG : GF_REDFLAG);
......@@ -4677,10 +4681,10 @@ static void P_ProcessZoomTube(player_t *player, mtag_t sectag, boolean end)
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY);
player->climbing = 0;
if (player->mo->state-states != S_PLAY_ROLL)
if (!P_IsPlayerInState(player, S_PLAY_ROLL))
{
P_SetMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_spin);
S_StartSoundFromMobj(player->mo, sfx_spin);
}
}
......@@ -4712,13 +4716,13 @@ static void P_ProcessFinishLine(player_t *player)
P_ResetStarposts();
// Play the starpost sound for 'consistency'
S_StartSound(player->mo, sfx_strpst);
S_StartSoundFromMobj(player->mo, sfx_strpst);
}
else if (player->starpostnum)
{
// blatant reuse of a variable that's normally unused in circuit
if (!player->tossdelay)
S_StartSound(player->mo, sfx_lose);
S_StartSoundFromMobj(player->mo, sfx_lose);
player->tossdelay = 3;
}
......@@ -4758,7 +4762,7 @@ static void P_ProcessRopeHang(player_t *player, mtag_t sectag)
if (player->cmd.buttons & BT_SPIN)
return;
if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate])
if (!(player->pflags & PF_SLIDING) && P_IsPlayerInState(player, S_PLAY_PAIN))
return;
if (player->exiting)
......@@ -4888,7 +4892,7 @@ static void P_ProcessRopeHang(player_t *player, mtag_t sectag)
player->powers[pw_carry] = CR_ROPEHANG;
player->speed = speed;
S_StartSound(player->mo, sfx_s3k4a);
S_StartSoundFromMobj(player->mo, sfx_s3k4a);
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY);
player->climbing = 0;
......@@ -5073,7 +5077,7 @@ static void P_EvaluateOldSectorSpecial(player_t *player, sector_t *sector, secto
if (leveltime % (TICRATE/2) == 0 && player->rings > 0)
{
player->rings--;
S_StartSound(player->mo, sfx_antiri);
S_StartSoundFromMobj(player->mo, sfx_antiri);
}
break;
}
......@@ -5563,7 +5567,7 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, I
break;
// Should this FOF have friction?
if(th->function.acp1 == (actionf_p1)T_Friction)
if(th->function == (actionf_p1)T_Friction)
{
f = (friction_t *)th;
......@@ -5571,7 +5575,7 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, I
Add_Friction(f->friction, f->movefactor, (INT32)(sec-sectors), f->affectee);
}
// Should this FOF have wind/current/pusher?
else if(th->function.acp1 == (actionf_p1)T_Pusher)
else if(th->function == (actionf_p1)T_Pusher)
{
p = (pusher_t *)th;
......@@ -5658,7 +5662,7 @@ static void P_AddFloatThinker(sector_t *sec, UINT16 tag, line_t *sourceline)
floater = Z_Calloc(sizeof (*floater), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &floater->thinker);
floater->thinker.function.acp1 = (actionf_p1)T_FloatSector;
floater->thinker.function = (actionf_p1)T_FloatSector;
floater->sector = sec;
floater->tag = (INT16)tag;
......@@ -5689,7 +5693,7 @@ static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control,
displace = Z_Calloc(sizeof (*displace), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &displace->thinker);
displace->thinker.function.acp1 = (actionf_p1)T_PlaneDisplace;
displace->thinker.function = (actionf_p1)T_PlaneDisplace;
displace->affectee = affectee;
displace->control = control;
displace->last_height = sectors[control].floorheight;
......@@ -5719,7 +5723,7 @@ static void P_AddBlockThinker(sector_t *sec, line_t *sourceline)
block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &block->thinker);
block->thinker.function.acp1 = (actionf_p1)T_MarioBlockChecker;
block->thinker.function = (actionf_p1)T_MarioBlockChecker;
block->sourceline = sourceline;
block->sector = sec;
......@@ -5744,7 +5748,7 @@ static void P_AddRaiseThinker(sector_t *sec, INT16 tag, fixed_t speed, fixed_t c
raise = Z_Calloc(sizeof (*raise), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &raise->thinker);
raise->thinker.function.acp1 = (actionf_p1)T_RaiseSector;
raise->thinker.function = (actionf_p1)T_RaiseSector;
raise->tag = tag;
raise->sector = sec;
......@@ -5771,7 +5775,7 @@ static void P_AddAirbob(sector_t *sec, INT16 tag, fixed_t dist, boolean raise, b
airbob = Z_Calloc(sizeof (*airbob), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &airbob->thinker);
airbob->thinker.function.acp1 = (actionf_p1)T_RaiseSector;
airbob->thinker.function = (actionf_p1)T_RaiseSector;
airbob->tag = tag;
airbob->sector = sec;
......@@ -5813,7 +5817,7 @@ static inline void P_AddThwompThinker(sector_t *sec, line_t *sourceline, fixed_t
thwomp = Z_Calloc(sizeof (*thwomp), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &thwomp->thinker);
thwomp->thinker.function.acp1 = (actionf_p1)T_ThwompSector;
thwomp->thinker.function = (actionf_p1)T_ThwompSector;
// set up the fields according to the type of elevator action
thwomp->sourceline = sourceline;
......@@ -5852,7 +5856,7 @@ static inline void P_AddNoEnemiesThinker(line_t *sourceline)
nobaddies = Z_Calloc(sizeof (*nobaddies), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &nobaddies->thinker);
nobaddies->thinker.function.acp1 = (actionf_p1)T_NoEnemiesSector;
nobaddies->thinker.function = (actionf_p1)T_NoEnemiesSector;
nobaddies->sourceline = sourceline;
}
......@@ -5872,7 +5876,7 @@ static void P_AddEachTimeThinker(line_t *sourceline, boolean triggerOnExit)
eachtime = Z_Calloc(sizeof (*eachtime), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &eachtime->thinker);
eachtime->thinker.function.acp1 = (actionf_p1)T_EachTimeThinker;
eachtime->thinker.function = (actionf_p1)T_EachTimeThinker;
eachtime->sourceline = sourceline;
eachtime->triggerOnExit = triggerOnExit;
......@@ -5894,7 +5898,7 @@ static inline void P_AddCameraScanner(sector_t *sourcesec, sector_t *actionsecto
elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL);
P_AddThinker(THINK_MAIN, &elevator->thinker);
elevator->thinker.function.acp1 = (actionf_p1)T_CameraScanner;
elevator->thinker.function = (actionf_p1)T_CameraScanner;
elevator->type = elevateBounce;
// set up the fields according to the type of elevator action
......@@ -5940,7 +5944,7 @@ void T_LaserFlash(laserthink_t *flash)
top = P_GetFFloorTopZAt (fflr, sector->soundorg.x, sector->soundorg.y);
bottom = P_GetFFloorBottomZAt(fflr, sector->soundorg.x, sector->soundorg.y);
sector->soundorg.z = (top + bottom)/2;
S_StartSound(&sector->soundorg, sfx_laser);
S_StartSoundFromSector(sector, sfx_laser);
// Seek out objects to DESTROY! MUAHAHHAHAHAA!!!*cough*
for (node = sector->touching_thinglist; node && node->m_thing; node = node->m_thinglist_next)
......@@ -5978,7 +5982,7 @@ static inline void P_AddLaserThinker(INT16 tag, line_t *line, boolean nobosses)
P_AddThinker(THINK_MAIN, &flash->thinker);
flash->thinker.function.acp1 = (actionf_p1)T_LaserFlash;
flash->thinker.function = (actionf_p1)T_LaserFlash;
flash->tag = tag;
flash->sourceline = line;
flash->nobosses = nobosses;
......@@ -6238,7 +6242,7 @@ static void P_DoPortalCopyFromLine(sector_t *dest_sector, int plane_type, int ta
}
}
static sectorportal_t *P_SectorGetPortalOrCreate(sector_t *sector, UINT32 *num, UINT32 *result)
static sectorportal_t *P_SectorGetPortalOrCreate(sector_t *sector, UINT32 *num, UINT32 *result, boolean ceiling)
{
sectorportal_t *secportal = NULL;
......@@ -6246,8 +6250,8 @@ static sectorportal_t *P_SectorGetPortalOrCreate(sector_t *sector, UINT32 *num,
{
*num = P_NewSectorPortal();
secportal = &secportals[*num];
secportal->origin.x = sector->soundorg.x;
secportal->origin.y = sector->soundorg.y;
secportal->target = sector;
secportal->ceiling = ceiling;
*result = *num;
}
else
......@@ -6261,12 +6265,12 @@ static sectorportal_t *P_SectorGetPortalOrCreate(sector_t *sector, UINT32 *num,
static sectorportal_t *P_SectorGetFloorPortalOrCreate(sector_t *sector, UINT32 *result)
{
return P_SectorGetPortalOrCreate(sector, &sector->portal_floor, result);
return P_SectorGetPortalOrCreate(sector, &sector->portal_floor, result, false);
}
static sectorportal_t *P_SectorGetCeilingPortalOrCreate(sector_t *sector, UINT32 *result)
{
return P_SectorGetPortalOrCreate(sector, &sector->portal_ceiling, result);
return P_SectorGetPortalOrCreate(sector, &sector->portal_ceiling, result, true);
}
static void P_CopySectorPortalToLines(UINT32 portal_num, int sector_tag)
......@@ -6379,9 +6383,9 @@ void P_SpawnSpecials(boolean fromnetsave)
// Firstly, find out how many there are in each sector
for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)T_Friction)
if (th->function == (actionf_p1)T_Friction)
secthinkers[((friction_t *)th)->affectee].count++;
else if (th->function.acp1 == (actionf_p1)T_Pusher)
else if (th->function == (actionf_p1)T_Pusher)
secthinkers[((pusher_t *)th)->affectee].count++;
}
......@@ -6399,9 +6403,9 @@ void P_SpawnSpecials(boolean fromnetsave)
{
size_t secnum = (size_t)-1;
if (th->function.acp1 == (actionf_p1)T_Friction)
if (th->function == (actionf_p1)T_Friction)
secnum = ((friction_t *)th)->affectee;
else if (th->function.acp1 == (actionf_p1)T_Pusher)
else if (th->function == (actionf_p1)T_Pusher)
secnum = ((pusher_t *)th)->affectee;
if (secnum != (size_t)-1)
......@@ -7763,7 +7767,7 @@ static boolean IsSector3DBlock(sector_t* sec)
static void Add_Scroller(INT32 type, fixed_t dx, fixed_t dy, INT32 control, INT32 affectee, INT32 accel, INT32 exclusive)
{
scroll_t *s = Z_Calloc(sizeof *s, PU_LEVSPEC, NULL);
s->thinker.function.acp1 = (actionf_p1)T_Scroll;
s->thinker.function = (actionf_p1)T_Scroll;
s->type = type;
s->dx = dx;
s->dy = dy;
......@@ -7905,7 +7909,7 @@ static void Add_MasterDisappearer(tic_t appeartime, tic_t disappeartime, tic_t o
{
disappear_t *d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL);
d->thinker.function.acp1 = (actionf_p1)T_Disappear;
d->thinker.function = (actionf_p1)T_Disappear;
d->appeartime = appeartime;
d->disappeartime = disappeartime;
d->offset = offset;
......@@ -7952,7 +7956,7 @@ void T_Disappear(disappear_t *d)
if (!(lines[d->sourceline].args[5]))
{
sectors[s].soundorg.z = P_GetFFloorTopZAt(rover, sectors[s].soundorg.x, sectors[s].soundorg.y);
S_StartSound(&sectors[s].soundorg, sfx_appear);
S_StartSoundFromSector(&sectors[s], sfx_appear);
}
}
}
......@@ -8298,7 +8302,7 @@ static void P_AddFakeFloorFader(ffloor_t *rover, size_t sectornum, size_t ffloor
d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL);
d->thinker.function.acp1 = (actionf_p1)T_Fade;
d->thinker.function = (actionf_p1)T_Fade;
d->rover = rover;
d->sectornum = (UINT32)sectornum;
d->ffloornum = (UINT32)ffloornum;
......@@ -8450,7 +8454,7 @@ static void Add_ColormapFader(sector_t *sector, extracolormap_t *source_exc, ext
}
d = Z_Malloc(sizeof *d, PU_LEVSPEC, NULL);
d->thinker.function.acp1 = (actionf_p1)T_FadeColormap;
d->thinker.function = (actionf_p1)T_FadeColormap;
d->sector = sector;
d->source_exc = source_exc;
d->dest_exc = dest_exc;
......@@ -8574,7 +8578,7 @@ static void Add_Friction(INT32 friction, INT32 movefactor, INT32 affectee, INT32
{
friction_t *f = Z_Calloc(sizeof *f, PU_LEVSPEC, NULL);
f->thinker.function.acp1 = (actionf_p1)T_Friction;
f->thinker.function = (actionf_p1)T_Friction;
f->friction = friction;
f->movefactor = movefactor;
f->affectee = affectee;
......@@ -8712,7 +8716,7 @@ static void Add_Pusher(pushertype_e type, fixed_t x_mag, fixed_t y_mag, fixed_t
{
pusher_t *p = Z_Calloc(sizeof *p, PU_LEVSPEC, NULL);
p->thinker.function.acp1 = (actionf_p1)T_Pusher;
p->thinker.function = (actionf_p1)T_Pusher;
p->type = type;
p->x_mag = x_mag;
p->y_mag = y_mag;
......@@ -8805,7 +8809,7 @@ void T_Pusher(pusher_t *p)
if (thing->player && thing->player->powers[pw_carry] == CR_ROPEHANG)
continue;
if (thing->player && (thing->state == &states[thing->info->painstate]) && (thing->player->powers[pw_flashing] > (flashingtics/4)*3 && thing->player->powers[pw_flashing] <= flashingtics))
if (thing->player && P_IsPlayerInState(thing->player, S_PLAY_PAIN) && (thing->player->powers[pw_flashing] > (flashingtics/4)*3 && thing->player->powers[pw_flashing] <= flashingtics))
continue;
inFOF = touching = moved = false;
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -130,6 +130,7 @@ typedef enum
{
TMEF_SKIPTALLY = 1,
TMEF_EMERALDCHECK = 1<<1,
TMEF_KEEPCUTSCENE = 1<<2,
} textmapexitflags_t;
typedef enum
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2024 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -124,7 +124,7 @@ void Command_Numthinkers_f(void)
{
for (think = thlist[i].next; think != &thlist[i]; think = think->next)
{
if (think->function.acp1 != action)
if (think->function != action)
continue;
count++;
......@@ -162,7 +162,7 @@ void Command_CountMobjs_f(void)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
if (((mobj_t *)th)->type == i)
......@@ -182,7 +182,7 @@ void Command_CountMobjs_f(void)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
if (((mobj_t *)th)->type == i)
......@@ -228,7 +228,7 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
static const char *MobjTypeName(const mobj_t *mobj)
{
mobjtype_t type;
actionf_p1 p1 = mobj->thinker.function.acp1;
actionf_p1 p1 = mobj->thinker.function;
if (p1 == (actionf_p1)P_MobjThinker)
type = mobj->type;
......@@ -247,7 +247,7 @@ static const char *MobjTypeName(const mobj_t *mobj)
static const char *MobjThinkerName(const mobj_t *mobj)
{
actionf_p1 p1 = mobj->thinker.function.acp1;
actionf_p1 p1 = mobj->thinker.function;
if (p1 == (actionf_p1)P_MobjThinker)
{
......@@ -348,7 +348,8 @@ void P_RemoveThinkerDelayed(thinker_t *thinker)
void P_RemoveThinker(thinker_t *thinker)
{
LUA_InvalidateUserdata(thinker);
thinker->function.acp1 = (actionf_p1)P_RemoveThinkerDelayed;
thinker->removing = true;
thinker->function = (actionf_p1)P_RemoveThinkerDelayed;
}
/*
......@@ -436,9 +437,9 @@ static inline void P_RunThinkers(void)
for (currentthinker = thlist[i].next; currentthinker != &thlist[i]; currentthinker = currentthinker->next)
{
#ifdef PARANOIA
I_Assert(currentthinker->function.acp1 != NULL);
I_Assert(currentthinker->function != NULL);
#endif
currentthinker->function.acp1(currentthinker);
currentthinker->function(currentthinker);
}
PS_STOP_TIMING(ps_thlist_times[i]);
}
......@@ -564,6 +565,12 @@ void P_DoTeamscrambling(void)
CV_SetValue(&cv_teamscramble, 0);
}
//
// P_DoSpecialStageStuff()
//
// For old-style (non-NiGHTS) special stages
//
static inline void P_DoSpecialStageStuff(void)
{
boolean stillalive = false;
......@@ -601,7 +608,15 @@ static inline void P_DoSpecialStageStuff(void)
if (--players[i].nightstime > 6)
{
if (P_IsLocalPlayer(&players[i]) && oldnightstime > 10*TICRATE && players[i].nightstime <= 10*TICRATE)
{
if (mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
{
S_FadeMusic(0, 10*MUSICRATE);
S_StartSoundFromEverywhere(sfx_timeup); // that creepy "out of time" music from NiGHTS.
}
else
S_ChangeMusicInternal("_drown", false);
}
stillalive = true;
}
else if (!players[i].exiting)
......@@ -610,7 +625,7 @@ static inline void P_DoSpecialStageStuff(void)
players[i].pflags &= ~(PF_GLIDING|PF_BOUNCING);
players[i].nightstime = 0;
if (P_IsLocalPlayer(&players[i]))
S_StartSound(NULL, sfx_s3k66);
S_StartSoundFromEverywhere(sfx_s3k66);
}
}
......@@ -712,6 +727,7 @@ void P_Ticker(boolean run)
{
P_MapStart();
R_UpdateMobjInterpolators();
R_UpdateLevelInterpolators();
OP_ObjectplaceMovement(&players[0]);
P_MoveChaseCamera(&players[0], &camera, false);
R_UpdateViewInterpolation();
......
......@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior.
// Copyright (C) 1999-2025 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
......@@ -47,6 +47,7 @@
#include "m_cheat.h"
// Thok camera snap (ctrl-f "chalupa")
#include "g_input.h"
#include "simple_hashmap.h"
#ifdef HW3SOUND
#include "hardware/hw3sound.h"
......@@ -326,7 +327,7 @@ void P_GiveEmerald(boolean spawnObj)
{
UINT8 em = P_GetNextEmerald();
S_StartSound(NULL, sfx_cgot); // Got the emerald!
S_StartSoundFromEverywhere(sfx_cgot); // Got the emerald!
emeralds |= (1 << em);
stagefailed = false;
......@@ -435,7 +436,7 @@ UINT8 P_FindLowestMare(void)
// to find the egg capsule with the lowest mare
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -488,7 +489,7 @@ boolean P_TransferToNextMare(player_t *player)
// to find the closest axis point
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -539,7 +540,7 @@ static mobj_t *P_FindAxis(INT32 mare, INT32 axisnum)
// to find the closest axis point
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -574,7 +575,7 @@ static mobj_t *P_FindAxisTransfer(INT32 mare, INT32 axisnum, mobjtype_t type)
// to find the closest axis point
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -615,7 +616,7 @@ void P_TransferToAxis(player_t *player, INT32 axisnum)
// to find the closest axis point
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -669,7 +670,7 @@ static void P_DeNightserizePlayer(player_t *player)
player->powers[pw_carry] = CR_NIGHTSFALL;
player->powers[pw_underwater] = 0;
player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_SHIELDDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST);
player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST);
player->secondjump = 0;
player->homing = 0;
player->climbing = 0;
......@@ -716,7 +717,7 @@ static void P_DeNightserizePlayer(player_t *player)
// Check to see if the player should be killed.
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -776,7 +777,7 @@ static void P_DeNightserizePlayer(player_t *player)
// NiGHTS Time!
void P_NightserizePlayer(player_t *player, INT32 nighttime)
{
UINT8 oldmare, oldmarelap, oldmarebonuslap;
UINT8 oldmare, oldmarelap, oldmarebonuslap, newmare;
// Bots can't be NiGHTSerized, silly!1 :P
if (player->bot)
......@@ -797,7 +798,12 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
}
}
player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_SHIELDDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING);
// Use mare-specific time limit if specified
newmare = P_FindLowestMare();
if (mapheaderinfo[gamemap-1]->nightstimer[newmare] > 0)
nighttime = mapheaderinfo[gamemap-1]->nightstimer[newmare];
player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING);
player->homing = 0;
player->mo->fuse = 0;
player->speed = 0;
......@@ -867,7 +873,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
continue;
players[i].texttimer = (3 * TICRATE) - 10;
players[i].textvar = 4; // Score and grades
players[i].textvar = NTV_BONUSTIMEEND; // Score and grades
players[i].lastmare = players[i].mare;
players[i].lastmarelap = players[i].marelap;
players[i].lastmarebonuslap = players[i].marebonuslap;
......@@ -885,7 +891,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
}
// Add score to leaderboards now
G_AddTempNightsRecords(player, players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1);
player->lastmaretime = leveltime - player->marebegunat;
G_AddTempNightsRecords(player, players[i].marescore, player->lastmaretime, players[i].mare + 1);
// transfer scores anyway
players[i].totalmarescore += players[i].marescore;
......@@ -906,12 +913,13 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
player->lastmarelap = oldmarelap;
player->lastmarebonuslap = oldmarebonuslap;
player->texttimer = 4*TICRATE;
player->textvar = 4; // Score and grades
player->textvar = NTV_BONUSTIMEEND; // Score and grades
player->finishedspheres = (INT16)(player->spheres);
player->finishedrings = (INT16)(player->rings);
// Add score to temp leaderboards
G_AddTempNightsRecords(player, player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1));
player->lastmaretime = leveltime - player->marebegunat;
G_AddTempNightsRecords(player, player->marescore, player->lastmaretime, (UINT8)(oldmare + 1));
// Starting a new mare, transfer scores
player->totalmarescore += player->marescore;
......@@ -924,7 +932,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
}
else
{
player->textvar = 5; // Nothing, just tells it to go to the GET n RINGS/SPHERES text in a bit
player->textvar = NTV_NONE; // Nothing, just tells it to go to the GET n RINGS/SPHERES text in a bit
player->texttimer = 40;
// Don't show before title card
......@@ -961,6 +969,33 @@ pflags_t P_GetJumpFlags(player_t *player)
return PF_JUMPED;
}
// If the state is a custom state for the player's skin, retrieve its "canonical" state
// e.g. S_SKIN_BIGTHECAT_WALK => S_PLAY_WALK
statenum_t P_GetCanonicalPlayerState(player_t *player, statenum_t state)
{
skin_t *skin = skins[player->skin];
statenum_t mappedstate;
SIMPLEHASH_FIND_INT(skin->customtodefaultstate, hashentry_int32_int32_t, state, state, mappedstate)
return mappedstate;
}
boolean P_IsPlayerInState(player_t *player, statenum_t state)
{
return (P_GetCanonicalPlayerState(player, player->mo->state - states) == state);
}
boolean P_IsPlayerInSuperTransformationState(player_t *player)
{
statenum_t state = player->mo->state - states;
return (state >= S_PLAY_SUPER_TRANS1 && state <= S_PLAY_SUPER_TRANS6);
}
boolean P_IsPlayerInNightsTransformationState(player_t *player)
{
statenum_t state = player->mo->state - states;
return (state >= S_PLAY_NIGHTS_TRANS1 && state <= S_PLAY_NIGHTS_TRANS6);
}
//
// P_PlayerInPain
//
......@@ -969,11 +1004,14 @@ pflags_t P_GetJumpFlags(player_t *player)
//
boolean P_PlayerInPain(player_t *player)
{
if (P_MobjWasRemoved(player->mo))
return false;
// no silly, sliding isn't pain
if (!(player->pflags & PF_SLIDING) && player->mo->state == &states[player->mo->info->painstate] && player->powers[pw_flashing])
return true;
if (player->mo->state == &states[S_PLAY_STUN])
if (P_IsPlayerInState(player, S_PLAY_STUN))
return true;
return false;
......@@ -997,7 +1035,7 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
fixed_t fallbackspeed;
P_ResetPlayer(player);
P_SetMobjState(player->mo, player->mo->info->painstate);
P_SetMobjState(player->mo, S_PLAY_PAIN);
if (player->mo->eflags & MFE_VERTICALFLIP)
player->mo->z--;
......@@ -1082,6 +1120,13 @@ void P_ResetPlayer(player_t *player)
if (player->mo->tracer && !P_MobjWasRemoved(player->mo->tracer))
{
player->mo->tracer->flags |= MF_PUSHABLE;
// goose the mom a little bit to trigger gravity to process for a tic
if (player->mo->tracer->eflags & MFE_VERTICALFLIP)
player->mo->tracer->momz -= 1;
else
player->mo->tracer->momz += 1;
P_SetTarget(&player->mo->tracer->tracer, NULL);
}
P_SetTarget(&player->mo->tracer, NULL);
......@@ -1345,6 +1390,8 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC) && P_IsLocalPlayer(player))
P_PlayJingle(player, JT_SUPER);
S_StartSoundFromEverywhere(sfx_supert); //let all players hear it -mattw_cfi
player->mo->momx = player->mo->momy = player->mo->momz = player->cmomx = player->cmomy = player->rmomx = player->rmomy = 0;
// Transformation animation
......@@ -1361,11 +1408,8 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
player->powers[pw_sneakers] = 0;
}
if (G_CoopGametype())
S_StartSound(player->mo, sfx_supert); //only hear it near yourself in co-op
else
if (!G_CoopGametype())
{
S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi
HU_SetCEchoFlags(0);
HU_SetCEchoDuration(5);
HU_DoCEcho(va("%s\\is now super.\\\\\\\\", player_names[player-players]));
......@@ -1378,7 +1422,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
// P_DoSuperDetransformation
//
// Detransform into regular Sonic!
static void P_DoSuperDetransformation(player_t *player)
void P_DoSuperDetransformation(player_t *player)
{
player->powers[pw_emeralds] = 0; // lost the power stones
P_SpawnGhostMobj(player->mo);
......@@ -1406,7 +1450,7 @@ static void P_DoSuperDetransformation(player_t *player)
// Inform the netgame that the champion has fallen in the heat of battle.
if (!G_CoopGametype())
{
S_StartSound(NULL, sfx_s3k66); //let all players hear it.
S_StartSoundFromEverywhere(sfx_s3k66); //let all players hear it.
HU_SetCEchoFlags(0);
HU_SetCEchoDuration(5);
HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
......@@ -1457,7 +1501,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
players[i].continues += 1;
players[i].gotcontinue = true;
if (P_IsLocalPlayer(player))
S_StartSound(NULL, sfx_s3kac);
S_StartSoundFromEverywhere(sfx_s3kac);
} */
}
}
......@@ -1477,7 +1521,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
player->continues += 1;
player->gotcontinue = true;
if (P_IsLocalPlayer(player))
S_StartSound(NULL, sfx_s3kac);
S_StartSoundFromEverywhere(sfx_s3kac);
}
}
......@@ -1558,9 +1602,9 @@ void P_PlayLivesJingle(player_t *player)
return;
if (mariomode)
S_StartSound(NULL, sfx_marioa);
S_StartSoundFromEverywhere(sfx_marioa);
else if (use1upSound || cv_1upsound.value)
S_StartSound(NULL, sfx_oneup);
S_StartSoundFromEverywhere(sfx_oneup);
else
{
P_PlayJingle(player, JT_1UP);
......@@ -1882,7 +1926,7 @@ void P_SpawnShieldOrb(player_t *player)
// blaze through the thinkers to see if an orb already exists!
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
shieldobj = (mobj_t *)th;
......@@ -2038,8 +2082,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
P_SetTarget(&ghost->target, mobj);
P_SetTarget(&ghost->dontdrawforviewmobj, mobj); // Hide the ghost in first-person
P_SetScale(ghost, mobj->scale);
ghost->destscale = mobj->scale;
P_SetScale(ghost, mobj->scale, true);
if (mobj->eflags & MFE_VERTICALFLIP)
{
......@@ -2065,6 +2108,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
ghost->renderflags = mobj->renderflags;
ghost->blendmode = mobj->blendmode;
ghost->alpha = mobj->alpha;
ghost->spritexscale = mobj->spritexscale;
ghost->spriteyscale = mobj->spriteyscale;
......@@ -2098,6 +2142,11 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
ghost->old_pitch = mobj->old_pitch2;
ghost->old_roll = mobj->old_roll2;
ghost->old_spriteroll = mobj->old_spriteroll2;
ghost->old_spritexscale = mobj->old_spritexscale2;
ghost->old_spriteyscale = mobj->old_spriteyscale2;
ghost->old_spritexoffset = mobj->old_spritexoffset2;
ghost->old_spriteyoffset = mobj->old_spriteyoffset2;
ghost->old_scale = mobj->old_scale2;
return ghost;
}
......@@ -2153,7 +2202,7 @@ void P_SpawnThokMobj(player_t *player)
mobj->eflags |= (player->mo->eflags & MFE_VERTICALFLIP);
// scale
P_SetScale(mobj, (mobj->destscale = player->mo->scale));
P_SetScale(mobj, player->mo->scale, true);
if (type == MT_THOK) // spintrail-specific modification for MT_THOK
{
......@@ -2217,8 +2266,7 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type)
mobj->eflags |= (player->mo->eflags & MFE_VERTICALFLIP);
// scale
P_SetScale(mobj, player->mo->scale);
mobj->destscale = player->mo->scale;
P_SetScale(mobj, player->mo->scale, true);
if (type == MT_THOK) // spintrail-specific modification for MT_THOK
{
......@@ -2362,7 +2410,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
if (dorollstuff)
{
if ((player->charability2 == CA2_SPINDASH) && !((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_THOKKED) && !(player->charability == CA_THOK && player->secondjump)
&& (player->cmd.buttons & BT_SPIN) && (FixedHypot(player->mo->momx, player->mo->momy) > (5*player->mo->scale)))
&& (player->cmd.buttons & BT_SPIN) && (FixedHypot(player->mo->momx, player->mo->momy) >= (5*player->mo->scale)))
player->pflags = (player->pflags|PF_SPINNING) & ~PF_THOKKED;
else if (!(player->pflags & PF_STARTDASH))
player->pflags &= ~PF_SPINNING;
......@@ -2370,7 +2418,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
if (player->pflags & PF_BOUNCING)
{
if (dorollstuff && player->mo->state-states != S_PLAY_BOUNCE_LANDING)
if (dorollstuff && !P_IsPlayerInState(player, S_PLAY_BOUNCE_LANDING))
{
P_MobjCheckWater(player->mo);
player->mo->momz *= -1;
......@@ -2391,7 +2439,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
&& player->panim != PA_ABILITY && player->panim != PA_ABILITY2)
{
P_SetMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_spin);
S_StartSoundFromMobj(player->mo, sfx_spin);
}
}
else if (player->pflags & PF_GLIDING) // ground gliding
......@@ -2407,9 +2455,9 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
player->pflags &= ~PF_GLIDING;
}
else if (player->charability == CA_GLIDEANDCLIMB && player->pflags & PF_THOKKED && !(player->pflags & (PF_JUMPED|PF_SHIELDABILITY))
&& (player->mo->floorz != player->mo->watertop) && player->mo->state-states == S_PLAY_FALL)
&& (player->mo->floorz != player->mo->watertop) && P_IsPlayerInState(player, S_PLAY_FALL))
{
if (player->mo->state-states != S_PLAY_GLIDE_LANDING)
if (!P_IsPlayerInState(player, S_PLAY_GLIDE_LANDING))
{
P_ResetPlayer(player);
P_SetMobjState(player->mo, S_PLAY_GLIDE_LANDING);
......@@ -2421,21 +2469,21 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
if (player->powers[pw_super])
{
P_Earthquake(player->mo, player->mo, 256*FRACUNIT);
S_StartSound(player->mo, sfx_s3k49);
S_StartSoundFromMobj(player->mo, sfx_s3k49);
}
else
S_StartSound(player->mo, sfx_s3k4c);
S_StartSoundFromMobj(player->mo, sfx_s3k4c);
}
}
else if (player->charability2 == CA2_MELEE
&& ((player->panim == PA_ABILITY2) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY && player->cmd.buttons & (BT_JUMP|BT_SPIN))))
{
if (player->mo->state-states != S_PLAY_MELEE_LANDING)
if (!P_IsPlayerInState(player, S_PLAY_MELEE_LANDING))
{
mobjtype_t type = player->revitem;
P_SetMobjState(player->mo, S_PLAY_MELEE_LANDING);
player->mo->tics = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
S_StartSound(player->mo, sfx_s3k8b);
S_StartSoundFromMobj(player->mo, sfx_s3k8b);
player->pflags |= PF_FULLSTASIS;
player->powers[pw_strong] = STR_MELEE;
......@@ -2474,11 +2522,11 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
throwang += ANG30;
}
if (mobjinfo[type].seesound && missile)
S_StartSound(missile, missile->info->seesound);
S_StartSoundFromMobj(missile, missile->info->seesound);
}
}
}
else if (player->charability == CA_GLIDEANDCLIMB && (player->mo->state-states == S_PLAY_GLIDE_LANDING))
else if (player->charability == CA_GLIDEANDCLIMB && (P_IsPlayerInState(player, S_PLAY_GLIDE_LANDING)))
;
else if (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2)
;
......@@ -2501,10 +2549,10 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH)
P_SetMobjState(player->mo, S_PLAY_DASH);
else if (player->speed >= runspd
&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
&& (player->panim != PA_RUN || P_IsPlayerInState(player, S_PLAY_FLOAT_RUN)))
P_SetMobjState(player->mo, S_PLAY_RUN);
else if ((player->rmomx || player->rmomy)
&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
&& (player->panim != PA_WALK || P_IsPlayerInState(player, S_PLAY_FLOAT)))
P_SetMobjState(player->mo, S_PLAY_WALK);
else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE)
P_SetMobjState(player->mo, S_PLAY_STND);
......@@ -2514,10 +2562,10 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH)
P_SetMobjState(player->mo, S_PLAY_DASH);
else if (player->speed >= runspd
&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
&& (player->panim != PA_RUN || P_IsPlayerInState(player, S_PLAY_FLOAT_RUN)))
P_SetMobjState(player->mo, S_PLAY_RUN);
else if ((player->mo->momx || player->mo->momy)
&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
&& (player->panim != PA_WALK || P_IsPlayerInState(player, S_PLAY_FLOAT)))
P_SetMobjState(player->mo, S_PLAY_WALK);
else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE)
P_SetMobjState(player->mo, S_PLAY_STND);
......@@ -2539,10 +2587,10 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack.
{
if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound
S_StartSound(player->mo, sfx_s3k4c);
S_StartSoundFromMobj(player->mo, sfx_s3k4c);
else // create a fire pattern on the ground
{
S_StartSound(player->mo, sfx_s3k47);
S_StartSoundFromMobj(player->mo, sfx_s3k47);
P_ElementalFire(player, true);
}
P_SetObjectMomZ(player->mo,
......@@ -3029,11 +3077,11 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
if (player->charflags & SF_MACHINE)
{
S_StartSound(player->mo, sfx_buzz1);
S_StartSoundFromMobj(player->mo, sfx_buzz1);
timeleft += 6;
}
else
S_StartSound(player->mo, sfx_dwnind);
S_StartSoundFromMobj(player->mo, sfx_dwnind);
if (!P_MobjWasRemoved(numbermobj))
{
......@@ -3041,9 +3089,8 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
P_SetMobjState(numbermobj, numbermobj->info->spawnstate+timeleft);
P_SetTarget(&numbermobj->target, player->mo);
P_SetScale(numbermobj, player->mo->scale, true);
numbermobj->threshold = 40;
numbermobj->destscale = player->mo->scale;
P_SetScale(numbermobj, player->mo->scale);
}
}
}
......@@ -3079,7 +3126,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
if ((player->powers[pw_underwater] == 25*TICRATE + 1)
|| (player->powers[pw_underwater] == 20*TICRATE + 1)
|| (player->powers[pw_underwater] == 15*TICRATE + 1))
S_StartSound(NULL, sfx_wtrdng);
S_StartSoundFromEverywhere(sfx_wtrdng);
if (player->powers[pw_underwater] == 11*TICRATE + 1
&& player == &players[consoleplayer])
......@@ -3105,10 +3152,7 @@ static void P_CheckInvincibilityTimer(player_t *player)
{
mobj_t *sparkle = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_IVSP);
if (!P_MobjWasRemoved(sparkle))
{
sparkle->destscale = player->mo->scale;
P_SetScale(sparkle, player->mo->scale);
}
P_SetScale(sparkle, player->mo->scale, true);
}
// Resume normal music stuff.
......@@ -3164,7 +3208,7 @@ static void P_DoBubbleBreath(player_t *player)
z += (P_RandomKey(player->mo->height>>FRACBITS)<<FRACBITS);
bubble = P_SpawnMobj(x, y, z, MT_WATERZAP);
if (!P_MobjWasRemoved(bubble))
S_StartSound(bubble, sfx_beelec);
S_StartSoundFromMobj(bubble, sfx_beelec);
}
}
else
......@@ -3183,8 +3227,7 @@ static void P_DoBubbleBreath(player_t *player)
if (bubble)
{
bubble->threshold = 42;
bubble->destscale = player->mo->scale;
P_SetScale(bubble, bubble->destscale);
P_SetScale(bubble, player->mo->scale, true);
}
// Tails stirs up the water while flying in it
......@@ -3206,20 +3249,14 @@ static void P_DoBubbleBreath(player_t *player)
player->mo->y + stirwatery,
stirwaterz, MT_SMALLBUBBLE);
if (!P_MobjWasRemoved(bubble))
{
bubble->destscale = player->mo->scale;
P_SetScale(bubble,bubble->destscale);
}
P_SetScale(bubble, player->mo->scale, true);
bubble = P_SpawnMobj(
player->mo->x - stirwaterx,
player->mo->y - stirwatery,
stirwaterz, MT_SMALLBUBBLE);
if (!P_MobjWasRemoved(bubble))
{
bubble->destscale = player->mo->scale;
P_SetScale(bubble,bubble->destscale);
}
P_SetScale(bubble, player->mo->scale, true);
}
}
......@@ -3301,6 +3338,7 @@ static void P_DoPlayerHeadSigns(player_t *player)
sign->frame = 2|FF_FULLBRIGHT;
}
}
}
if (!P_MobjWasRemoved(sign) && splitscreen) // Hide the sign from yourself in splitscreen - In single-screen, it wouldn't get spawned if it shouldn't be visible
{
......@@ -3339,7 +3377,6 @@ static void P_DoPlayerHeadSigns(player_t *player)
#endif
}
}
}
//
// P_DoClimbing
......@@ -3648,7 +3685,7 @@ static void P_DoClimbing(player_t *player)
for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)T_Scroll)
if (think->function != (actionf_p1)T_Scroll)
continue;
scroller = (scroll_t *)think;
......@@ -3702,9 +3739,9 @@ static void P_DoClimbing(player_t *player)
climb = false;
if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz)
&& player->mo->state-states != S_PLAY_CLIMB)
&& !P_IsPlayerInState(player, S_PLAY_CLIMB))
P_SetMobjState(player->mo, S_PLAY_CLIMB);
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING)
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && !P_IsPlayerInState(player, S_PLAY_CLING))
P_SetMobjState(player->mo, S_PLAY_CLING);
if (!floorclimb)
......@@ -3737,9 +3774,9 @@ static void P_DoClimbing(player_t *player)
climb = false;
if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz)
&& player->mo->state-states != S_PLAY_CLIMB)
&& !P_IsPlayerInState(player, S_PLAY_CLIMB))
P_SetMobjState(player->mo, S_PLAY_CLIMB);
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING)
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && !P_IsPlayerInState(player, S_PLAY_CLING))
P_SetMobjState(player->mo, S_PLAY_CLING);
if (cmd->buttons & BT_SPIN && !(player->pflags & PF_JUMPSTASIS))
......@@ -4116,7 +4153,7 @@ static void P_DoTeeter(player_t *player)
teeteryl = teeteryh = player->mo->y;
couldteeter = false;
solidteeter = teeter;
if (!P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_CheckSolidsTeeter))
if (!P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_CheckSolidsTeeter, tmthing))
goto teeterdone; // we've found something that stops us teetering at all
teeterdone:
teeter = solidteeter;
......@@ -4190,16 +4227,6 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
I_Assert(player != NULL);
I_Assert(!P_MobjWasRemoved(player->mo));
// Toss a flag
if (cmd->buttons & BT_TOSSFLAG && G_GametypeHasTeams()
&& !(player->powers[pw_super]) && !(player->tossdelay))
{
if (!(player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
P_PlayerEmeraldBurst(player, true); // Toss emeralds
else
P_PlayerFlagBurst(player, true);
}
if (!(cmd->buttons & (BT_ATTACK|BT_FIRENORMAL)))
{
// Not holding any firing buttons anymore.
......@@ -4208,23 +4235,21 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
return;
}
if (player->pflags & PF_ATTACKDOWN || player->climbing)
if (player->pflags & PF_ATTACKDOWN || player->climbing || (G_TagGametype() && !(player->pflags & PF_TAGIT)))
return;
// Fire a fireball if we have the Fire Flower powerup!
if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->weapondelay))
{
player->pflags |= PF_ATTACKDOWN;
mo = P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0);
if (mo)
P_InstaThrust(mo, player->mo->angle, ((mo->info->speed>>FRACBITS)*player->mo->scale) + player->speed);
S_StartSound(player->mo, sfx_mario7);
S_StartSoundFromMobj(player->mo, sfx_mario7);
P_SetWeaponDelay(player, TICRATE); // Short delay between fireballs so you can't spam them everywhere
return;
}
// No ringslinging outside of ringslinger!
if (!G_RingSlingerGametype() || player->weapondelay || (G_TagGametype() && !(player->pflags & PF_TAGIT)))
if (!G_RingSlingerGametype() || player->weapondelay)
return;
player->pflags |= PF_ATTACKDOWN;
......@@ -4251,7 +4276,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW);
// Rail has no unique thrown object, therefore its sound plays here.
S_StartSound(player->mo, sfx_rail1);
S_StartSoundFromMobj(player->mo, sfx_rail1);
}
// Automatic
else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring])
......@@ -4376,7 +4401,7 @@ firenormal:
}
// Other rail sound plays at contact point.
S_StartSound(mo, sfx_rail2);
S_StartSoundFromMobj(mo, sfx_rail2);
}
}
}
......@@ -4402,7 +4427,34 @@ static void P_DoSuperStuff(player_t *player)
// If you're super and not Sonic, de-superize!
if (!(ALL7EMERALDS(emeralds) && player->charflags & SF_SUPER))
{
P_DoSuperDetransformation(player);
player->powers[pw_super] = 0;
P_SetMobjState(player->mo, S_PLAY_STND);
if (P_IsLocalPlayer(player))
{
music_stack_noposition = true; // HACK: Do not reposition next music
music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
}
P_RestoreMusic(player);
P_SpawnShieldOrb(player);
// Restore color
if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
}
else
{
player->mo->color = P_GetPlayerColor(player);
G_GhostAddColor(GHC_NORMAL);
}
if (!G_CoopGametype())
{
HU_SetCEchoFlags(0);
HU_SetCEchoDuration(5);
HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
}
return;
}
......@@ -4412,7 +4464,7 @@ static void P_DoSuperStuff(player_t *player)
G_GhostAddColor(GHC_SUPER);
if (player->mo->state == &states[S_PLAY_SUPER_TRANS6]) // stop here for now
if (P_IsPlayerInState(player, S_PLAY_SUPER_TRANS6)) // stop here for now
return;
// Deplete one ring every second while super
......@@ -4424,45 +4476,33 @@ static void P_DoSuperStuff(player_t *player)
{
spark = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SUPERSPARK);
if (!P_MobjWasRemoved(spark))
{
spark->destscale = player->mo->scale;
P_SetScale(spark, player->mo->scale);
}
P_SetScale(spark, player->mo->scale, true);
}
// Ran out of rings while super!
if (player->rings <= 0 || player->exiting)
{
P_DoSuperDetransformation(player);
}
}
}
//
// P_SuperReady
//
// Returns true if player is ready to transform or detransform
// Returns true if player is ready to turn super, duh
//
boolean P_SuperReady(player_t *player, boolean transform)
boolean P_SuperReady(player_t *player)
{
if (!transform &&
(player->powers[pw_super] < TICRATE*3/2
|| !G_CoopGametype())) // No turning back in competitive!
return false;
else if (transform
&& (player->powers[pw_super]
|| !ALL7EMERALDS(emeralds)
|| !(player->rings >= 50)))
return false;
if (player->mo
if (!player->powers[pw_super]
&& !player->powers[pw_invulnerability]
&& !player->powers[pw_tailsfly]
&& !player->powers[pw_carry]
&& (player->charflags & SF_SUPER)
&& !P_PlayerInPain(player)
&& !player->climbing
&& !(player->pflags & (PF_FULLSTASIS|PF_THOKKED|PF_STARTDASH|PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY))
&& ((player->pflags & PF_JUMPED) || (P_IsObjectOnGround(player->mo) && (player->panim == PA_IDLE || player->panim == PA_EDGE
|| player->panim == PA_WALK || player->panim == PA_RUN || (player->charflags & SF_DASHMODE && player->panim == PA_DASH))))
&& !(maptol & TOL_NIGHTS))
&& (player->pflags & PF_JUMPED)
&& !(player->powers[pw_shield] & SH_NOSTACK)
&& !(maptol & TOL_NIGHTS)
&& ALL7EMERALDS(emeralds)
&& (player->rings >= 50))
return true;
return false;
......@@ -4520,7 +4560,7 @@ void P_DoJump(player_t *player, boolean soundandstate, boolean allowflip)
if (player->powers[pw_carry] == CR_PTERABYTE)
{
S_StartSound(player->mo, sfx_s3kd7s);
S_StartSoundFromMobj(player->mo, sfx_s3kd7s);
player->mo->tracer->cusval += 10; // attempting to break free
player->mo->tracer->watertop = P_RandomRange(-player->mo->tracer->cusval, player->mo->tracer->cusval) << (FRACBITS - 1);
player->mo->tracer->waterbottom = P_RandomRange(-player->mo->tracer->cusval, player->mo->tracer->cusval) << (FRACBITS - 1);
......@@ -4560,6 +4600,13 @@ void P_DoJump(player_t *player, boolean soundandstate, boolean allowflip)
player->mo->momz += player->mo->tracer->momz;
if (!P_IsObjectOnGround(player->mo->tracer))
P_SetObjectMomZ(player->mo->tracer, -9*FRACUNIT, true);
// goose the mom a little bit to trigger gravity to process for a tic
if (player->mo->tracer->eflags & MFE_VERTICALFLIP)
player->mo->tracer->momz -= 1;
else
player->mo->tracer->momz += 1;
player->mo->tracer->flags |= MF_PUSHABLE;
P_SetTarget(&player->mo->tracer->tracer, NULL);
}
......@@ -4649,13 +4696,13 @@ void P_DoJump(player_t *player, boolean soundandstate, boolean allowflip)
if (allowflip && P_InJumpFlipSector(player->mo)) // Flip gravity on jump?
{
player->mo->flags2 ^= MF2_OBJECTFLIP;
S_StartSound(player->mo, sfx_s3k73); // Play gravity flip sound
S_StartSoundFromMobj(player->mo, sfx_s3k73); // Play gravity flip sound
}
if (soundandstate)
{
if (!player->spectator)
S_StartSound(player->mo, sfx_jump); // Play jump sound!
S_StartSoundFromMobj(player->mo, sfx_jump); // Play jump sound!
P_SetMobjState(player->mo, S_PLAY_JUMP);
}
......@@ -4677,8 +4724,7 @@ void P_DoSpinDashDust(player_t *player)
P_SetMobjState(particle, S_SPINDUST_FIRE1);
P_SetTarget(&particle->target, player->mo);
particle->destscale = (2*player->mo->scale)/3;
P_SetScale(particle, particle->destscale);
P_SetScale(particle, (2*player->mo->scale)/3, true);
if (player->mo->eflags & MFE_VERTICALFLIP) // readjust z position if needed
particle->z = player->mo->z + player->mo->height - particle->height;
prandom[0] = P_RandomFixed()<<2; // P_RandomByte()<<10
......@@ -4701,7 +4747,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
{
boolean canstand = true; // can we stand on the ground? (mostly relevant for slopes)
if (player->pflags & PF_STASIS
&& (player->pflags & PF_JUMPSTASIS || player->mo->state-states != S_PLAY_GLIDE_LANDING))
&& (player->pflags & PF_JUMPSTASIS || !P_IsPlayerInState(player, S_PLAY_GLIDE_LANDING)))
return;
if (cmd->buttons & BT_SPIN)
......@@ -4721,7 +4767,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
{
case CA2_SPINDASH: // Spinning and Spindashing
// Start revving
if ((cmd->buttons & BT_SPIN) && (player->speed < FixedMul(5<<FRACBITS, player->mo->scale) || player->mo->state - states == S_PLAY_GLIDE_LANDING)
if ((cmd->buttons & BT_SPIN) && (player->speed < FixedMul(5<<FRACBITS, player->mo->scale) || P_IsPlayerInState(player, S_PLAY_GLIDE_LANDING))
&& !player->mo->momz && onground && !(player->pflags & (PF_SPINDOWN|PF_SPINNING))
&& canstand)
{
......@@ -4731,16 +4777,16 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
player->dashspeed = player->mindash;
P_SetMobjState(player->mo, S_PLAY_SPINDASH);
if (!player->spectator)
S_StartSound(player->mo, sfx_spndsh); // Make the rev sound!
S_StartSoundFromMobj(player->mo, sfx_spndsh); // Make the rev sound!
}
// Revving
else if ((cmd->buttons & BT_SPIN) && (player->pflags & PF_STARTDASH))
{
if (player->speed > 5*player->mo->scale)
if (player->speed >= 5*player->mo->scale)
{
player->pflags &= ~PF_STARTDASH;
P_SetMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_spin);
S_StartSoundFromMobj(player->mo, sfx_spin);
break;
}
if (player->dashspeed < player->mindash)
......@@ -4755,7 +4801,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
fixed_t soundcalculation = chargecalculation;
player->dashspeed += FRACUNIT;
if (!player->spectator && soundcalculation != chargecalculation)
S_StartSound(player->mo, sfx_spndsh); // Make the rev sound!
S_StartSoundFromMobj(player->mo, sfx_spndsh); // Make the rev sound!
#undef chargecalculation
}
if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
......@@ -4774,11 +4820,10 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
player->pflags |= (PF_SPINDOWN|PF_SPINNING);
P_SetMobjState(player->mo, S_PLAY_ROLL);
if (!player->spectator)
S_StartSound(player->mo, sfx_spin);
S_StartSoundFromMobj(player->mo, sfx_spin);
}
else
// Catapult the player from a spindash rev!
if (onground && !(player->pflags & PF_SPINDOWN) && (player->pflags & PF_STARTDASH) && (player->pflags & PF_SPINNING))
else if (onground && !(player->pflags & PF_SPINDOWN) && (player->pflags & PF_STARTDASH) && (player->pflags & PF_SPINNING))
{
player->pflags &= ~PF_STARTDASH;
if (player->powers[pw_carry] == CR_BRAKGOOP)
......@@ -4798,7 +4843,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
}
if (!player->spectator)
S_StartSound(player->mo, sfx_zoom);
S_StartSoundFromMobj(player->mo, sfx_zoom);
}
player->dashspeed = 0;
......@@ -4871,7 +4916,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
player->pflags &= ~PF_STARTJUMP;
player->mo->momz = FixedMul(player->mo->momz, 3*FRACUNIT/2); // NOT 1.5 times the jump height, but 2.25 times.
P_SetMobjState(player->mo, S_PLAY_TWINSPIN);
S_StartSound(player->mo, sfx_s3k8b);
S_StartSoundFromMobj(player->mo, sfx_s3k8b);
}
else
#endif
......@@ -4898,7 +4943,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
player->mo->momy += player->cmomy;
P_SetMobjState(player->mo, S_PLAY_MELEE);
player->powers[pw_strong] = STR_MELEE;
S_StartSound(player->mo, sfx_s3k42);
S_StartSoundFromMobj(player->mo, sfx_s3k42);
}
player->pflags |= PF_SPINDOWN;
}
......@@ -4973,13 +5018,13 @@ void P_DoJumpShield(player_t *player)
#undef numangles
player->pflags &= ~PF_NOJUMPDAMAGE;
P_SetMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_s3k45);
S_StartSoundFromMobj(player->mo, sfx_s3k45);
}
else
{
player->pflags |= PF_NOJUMPDAMAGE;
P_SetMobjState(player->mo, S_PLAY_FALL);
S_StartSound(player->mo, sfx_wdjump);
S_StartSoundFromMobj(player->mo, sfx_wdjump);
}
}
......@@ -4991,7 +5036,7 @@ void P_DoJumpShield(player_t *player)
void P_DoBubbleBounce(player_t *player)
{
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SHIELDABILITY);
S_StartSound(player->mo, sfx_s3k44);
S_StartSoundFromMobj(player->mo, sfx_s3k44);
P_MobjCheckWater(player->mo);
P_DoJump(player, false, false);
if (player->charflags & SF_NOJUMPSPIN)
......@@ -5011,7 +5056,7 @@ void P_DoBubbleBounce(player_t *player)
//
void P_DoAbilityBounce(player_t *player, boolean changemomz)
{
if (player->mo->state-states == S_PLAY_BOUNCE_LANDING)
if (P_IsPlayerInState(player, S_PLAY_BOUNCE_LANDING))
return;
if (changemomz)
......@@ -5033,7 +5078,7 @@ void P_DoAbilityBounce(player_t *player, boolean changemomz)
player->mo->momz = max(minmomz, (minmomz + prevmomz)/2);
}
S_StartSound(player->mo, sfx_boingf);
S_StartSoundFromMobj(player->mo, sfx_boingf);
P_SetMobjState(player->mo, S_PLAY_BOUNCE_LANDING);
player->pflags |= PF_BOUNCING|PF_THOKKED;
}
......@@ -5077,7 +5122,7 @@ void P_TwinSpinRejuvenate(player_t *player, mobjtype_t type)
if (!P_MobjWasRemoved(missile))
{
P_SetTarget(&missile->target, player->mo);
P_SetScale(missile, (missile->destscale >>= 1));
P_SetScale(missile, missile->destscale/2, true);
missile->angle = ang + movang;
missile->fuse = TICRATE/2;
missile->extravalue2 = (99*FRACUNIT)/100;
......@@ -5110,7 +5155,7 @@ void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -5148,7 +5193,7 @@ static void P_DoTwinSpin(player_t *player)
{
player->pflags &= ~(PF_NOJUMPDAMAGE|PF_SPINNING);
player->pflags |= P_GetJumpFlags(player) | PF_THOKKED;
S_StartSound(player->mo, sfx_s3k42);
S_StartSoundFromMobj(player->mo, sfx_s3k42);
player->mo->frame = 0;
P_SetMobjState(player->mo, S_PLAY_TWINSPIN);
player->powers[pw_strong] = STR_TWINSPIN;
......@@ -5162,7 +5207,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
{
mobj_t *lockonshield = NULL;
if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SHIELDDOWN)
if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN)
&& ((!(player->pflags & PF_THOKKED) || (((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP || (player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) && player->secondjump == UINT8_MAX) ))) // thokked is optional if you're bubblewrapped / 3dblasted
{
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT && !(player->charflags & SF_NOSHIELDABILITY))
......@@ -5192,7 +5237,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
}
}
}
if ((!(player->charflags & SF_NOSHIELDABILITY)) && (cmd->buttons & BT_SHIELD && !LUA_HookPlayer(player, HOOK(ShieldSpecial)))) // Shield button effects
if ((!(player->charflags & SF_NOSHIELDABILITY)) && (cmd->buttons & BT_SPIN && !LUA_HookPlayer(player, HOOK(ShieldSpecial)))) // Spin button effects
{
// Force stop
if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE)
......@@ -5200,7 +5245,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
player->pflags &= ~PF_SPINNING;
player->mo->momx = player->mo->momy = player->mo->momz = 0;
S_StartSound(player->mo, sfx_ngskid);
S_StartSoundFromMobj(player->mo, sfx_ngskid);
}
else
{
......@@ -5228,11 +5273,11 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockonshield->x, lockonshield->y);
player->pflags &= ~PF_NOJUMPDAMAGE;
P_SetMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_s3k40);
S_StartSoundFromMobj(player->mo, sfx_s3k40);
player->homing = 3*TICRATE;
}
else
S_StartSound(player->mo, sfx_s3ka6);
S_StartSoundFromMobj(player->mo, sfx_s3ka6);
break;
// Elemental stomp/Bubble bounce
case SH_ELEMENTAL:
......@@ -5244,7 +5289,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
if (elem)
{
player->mo->momx = player->mo->momy = 0;
S_StartSound(player->mo, sfx_s3k43);
S_StartSoundFromMobj(player->mo, sfx_s3k43);
}
else
{
......@@ -5252,7 +5297,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
player->mo->momy -= (player->mo->momy/3);
player->pflags &= ~PF_NOJUMPDAMAGE;
P_SetMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_s3k44);
S_StartSoundFromMobj(player->mo, sfx_s3k44);
}
player->secondjump = 0;
P_SetObjectMomZ(player->mo, -24*FRACUNIT, false);
......@@ -5265,7 +5310,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
player->drawangle = player->mo->angle;
player->pflags &= ~(PF_NOJUMPDAMAGE|PF_SPINNING);
P_SetMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_s3k43);
S_StartSoundFromMobj(player->mo, sfx_s3k43);
default:
break;
}
......@@ -5314,8 +5359,15 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
;
else if (P_PlayerShieldThink(player, cmd, lockonthok, visual))
;
else if ((cmd->buttons & BT_SPIN) && !LUA_HookPlayer(player, HOOK(JumpSpinSpecial)))
else if ((cmd->buttons & BT_SPIN))
{
if (!(player->pflags & PF_SPINDOWN) && P_SuperReady(player))
{
// If you can turn super and aren't already,
// and you don't have a shield, do it!
P_DoSuperTransformation(player, false);
}
else if (!LUA_HookPlayer(player, HOOK(JumpSpinSpecial)))
switch (player->charability)
{
case CA_THOK:
......@@ -5415,6 +5467,12 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
}
else if (player->pflags & PF_SLIDING || ((gametyperules & GTR_TEAMFLAGS) && player->gotflag) || player->pflags & PF_SHIELDABILITY)
;
/*else if (P_SuperReady(player))
{
// If you can turn super and aren't already,
// and you don't have a shield, do it!
P_DoSuperTransformation(player, false);
}*/
else if (player->pflags & PF_JUMPED)
{
if (!LUA_HookPlayer(player, HOOK(AbilitySpecial)))
......@@ -5476,7 +5534,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
player->drawangle = player->mo->angle;
if (player->mo->info->attacksound && !player->spectator)
S_StartSound(player->mo, player->mo->info->attacksound); // Play the THOK sound
S_StartSoundFromMobj(player->mo, player->mo->info->attacksound); // Play the THOK sound
P_SpawnThokMobj(player);
......@@ -5591,7 +5649,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
player->flyangle = 56 + (60-(player->actionspd>>FRACBITS))/3;
player->pflags |= PF_THOKKED;
player->pflags &= ~PF_SPINNING;
S_StartSound(player->mo, sfx_spndsh);
S_StartSoundFromMobj(player->mo, sfx_spndsh);
}
break;
case CA_BOUNCE:
......@@ -6079,7 +6137,7 @@ static void P_3dMovement(player_t *player)
// When sliding, don't allow forward/back
if (player->pflags & PF_SLIDING)
cmd->forwardmove = 0;
else if (onground && player->mo->state == states+S_PLAY_PAIN)
else if (onground && P_IsPlayerInState(player, S_PLAY_PAIN))
P_SetMobjState(player->mo, S_PLAY_WALK);
player->aiming = cmd->aiming<<FRACBITS;
......@@ -6132,7 +6190,7 @@ static void P_3dMovement(player_t *player)
{
if (player->pflags & PF_BOUNCING)
{
if (player->mo->state-states == S_PLAY_BOUNCE_LANDING)
if (P_IsPlayerInState(player, S_PLAY_BOUNCE_LANDING))
{
thrustfactor = player->thrustfactor*8;
acceleration = player->accelstart/8 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration/8;
......@@ -6469,7 +6527,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
// Find next waypoint
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -6505,7 +6563,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
{
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -6534,7 +6592,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
{
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -6626,7 +6684,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
HU_SetCEchoDuration(1);
HU_DoCEcho("transfer!");
HU_SetCEchoDuration(5);
S_StartSound(NULL, sfx_strpst);
S_StartSoundFromEverywhere(sfx_strpst);
}
if (player->pflags & PF_TRANSFERTOCLOSEST)
{
......@@ -6682,7 +6740,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
HU_SetCEchoDuration(1);
HU_DoCEcho("transfer!");
HU_SetCEchoDuration(5);
S_StartSound(NULL, sfx_strpst);
S_StartSoundFromEverywhere(sfx_strpst);
}
if (player->mo->target->health < transfer1->health)
{
......@@ -6748,7 +6806,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
HU_SetCEchoDuration(1);
HU_DoCEcho("transfer!");
HU_SetCEchoDuration(5);
S_StartSound(NULL, sfx_strpst);
S_StartSoundFromEverywhere(sfx_strpst);
}
if (player->pflags & PF_TRANSFERTOCLOSEST)
{
......@@ -6814,7 +6872,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
HU_SetCEchoDuration(1);
HU_DoCEcho("transfer!");
HU_SetCEchoDuration(5);
S_StartSound(NULL, sfx_strpst);
S_StartSoundFromEverywhere(sfx_strpst);
}
if (player->mo->target->health < transfer2->health)
{
......@@ -6885,12 +6943,12 @@ static void P_DoNiGHTSCapsule(player_t *player)
{
if (player->mo->momx || player->mo->momy || player->mo->momz)
{
if (player->mo->state != &states[S_PLAY_NIGHTS_PULL])
if (!P_IsPlayerInState(player, S_PLAY_NIGHTS_PULL))
P_SetMobjState(player->mo, S_PLAY_NIGHTS_PULL);
}
else if (player->mo->state != &states[S_PLAY_NIGHTS_ATTACK])
else if (!P_IsPlayerInState(player, S_PLAY_NIGHTS_ATTACK))
{
S_StartSound(player->mo, sfx_spin);
S_StartSoundFromMobj(player->mo, sfx_spin);
P_SetMobjState(player->mo, S_PLAY_NIGHTS_ATTACK);
}
}
......@@ -6904,7 +6962,7 @@ static void P_DoNiGHTSCapsule(player_t *player)
if (!(player->charflags & SF_NONIGHTSROTATION))
{
if ((player->mo->state == &states[S_PLAY_NIGHTS_PULL])
if ((P_IsPlayerInState(player, S_PLAY_NIGHTS_PULL))
&& (player->mo->sprite2 == SPR2_NPUL))
player->mo->spriteroll -= ANG30;
else
......@@ -6988,7 +7046,7 @@ static void P_DoNiGHTSCapsule(player_t *player)
player->capsule->z + (player->capsule->height/2) + ((P_SignedRandom()/2)<<FRACBITS),
MT_SONIC3KBOSSEXPLODE);
if (!P_MobjWasRemoved(explodemo))
S_StartSound(explodemo,sfx_s3kb4);
S_StartSoundFromMobj(explodemo,sfx_s3kb4);
}
}
else
......@@ -7019,7 +7077,7 @@ static void P_DoNiGHTSCapsule(player_t *player)
{
players[i].bonustime = true;
players[i].texttimer = 4*TICRATE;
players[i].textvar = 1; // Time Bonus
players[i].textvar = NTV_BONUSTIMESTART; // Time Bonus
players[i].finishedtime = players[i].nightstime;
if (!G_IsSpecialStage(gamemap))
P_AddPlayerScore(&players[i], (players[i].finishedtime/TICRATE) * 100);
......@@ -7103,12 +7161,12 @@ static void P_DoNiGHTSCapsule(player_t *player)
{
S_StartScreamSound(player->mo, sfx_lose);
player->texttimer = 4*TICRATE;
player->textvar = 3; // Get more rings!
player->textvar = NTV_GETMORESPHERES; // Get more spheres/chips!
player->capsule->reactiontime = 0;
player->capsule->extravalue1 = player->capsule->cvmem =\
player->capsule->cusval = player->capsule->movecount =\
player->capsule->lastlook = player->capsule->extravalue2 = -1;
P_RunNightsCapsuleTouchExecutors(player->mo, false, false); // run capsule exit executors, and we lacked rings
P_RunNightsCapsuleTouchExecutors(player->mo, false, false); // run capsule exit executors, and we lacked spheres/chips
}
}
}
......@@ -7219,15 +7277,11 @@ static void P_NiGHTSMovement(player_t *player)
if (playeringame[i] /*&& players[i].powers[pw_carry] == CR_NIGHTSMODE*/
&& (players[i].capsule && players[i].capsule->reactiontime))
capsule = true;
if (!capsule
&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
&& !player->exiting)
if (!capsule && !P_IsPlayerInNightsTransformationState(player) && !player->exiting)
player->nightstime--;
}
else if (!(gametyperules & GTR_RACE)
&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
&& !P_IsPlayerInNightsTransformationState(player)
&& !(player->capsule && player->capsule->reactiontime)
&& !player->exiting)
player->nightstime--;
......@@ -7243,7 +7297,7 @@ static void P_NiGHTSMovement(player_t *player)
if (mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
{
S_FadeMusic(0, 10*MUSICRATE);
S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS.
S_StartSoundFromEverywhere(sfx_timeup); // that creepy "out of time" music from NiGHTS.
}
else
P_PlayJingle(player, ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? JT_NIGHTSTIMEOUT : JT_SSTIMEOUT);
......@@ -7266,7 +7320,7 @@ static void P_NiGHTSMovement(player_t *player)
// to find the closest axis point
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -7367,8 +7421,7 @@ static void P_NiGHTSMovement(player_t *player)
return;
}
if (player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
if (P_IsPlayerInNightsTransformationState(player))
{
player->mo->momx = player->mo->momy = player->mo->momz = 0;
player->mo->spriteroll = 0;
......@@ -7387,14 +7440,14 @@ static void P_NiGHTSMovement(player_t *player)
#if 0//def ROTSPRITE
if (!(player->charflags & SF_NONIGHTSROTATION) && player->mo->momz)
{
if (player->mo->state != &states[S_PLAY_NIGHTS_DRILL])
if (!P_IsPlayerInState(player, S_PLAY_NIGHTS_DRILL))
P_SetMobjState(player->mo, S_PLAY_NIGHTS_DRILL);
player->mo->spriteroll = ANGLE_90;
}
else
#endif
{
if (player->mo->state != &states[S_PLAY_NIGHTS_FLOAT])
if (!P_IsPlayerInState(player, S_PLAY_NIGHTS_FLOAT))
P_SetMobjState(player->mo, S_PLAY_NIGHTS_FLOAT);
player->drawangle += ANGLE_22h;
}
......@@ -7417,9 +7470,8 @@ static void P_NiGHTSMovement(player_t *player)
firstmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle+ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle+ANGLE_90, spawndist), z, MT_NIGHTSPARKLE);
if (!P_MobjWasRemoved(firstmobj))
{
firstmobj->destscale = player->mo->scale;
P_SetTarget(&firstmobj->target, player->mo);
P_SetScale(firstmobj, player->mo->scale);
P_SetScale(firstmobj, player->mo->scale, true);
// Superloop turns sparkles red
if (player->powers[pw_nights_superloop])
P_SetMobjState(firstmobj, mobjinfo[MT_NIGHTSPARKLE].seestate);
......@@ -7427,10 +7479,8 @@ static void P_NiGHTSMovement(player_t *player)
secondmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle-ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle-ANGLE_90, spawndist), z, MT_NIGHTSPARKLE);
if (!P_MobjWasRemoved(secondmobj))
{
secondmobj->destscale = player->mo->scale;
P_SetTarget(&secondmobj->target, player->mo);
P_SetScale(secondmobj, player->mo->scale);
P_SetScale(secondmobj, player->mo->scale, true);
// Superloop turns sparkles red
if (player->powers[pw_nights_superloop])
P_SetMobjState(secondmobj, mobjinfo[MT_NIGHTSPARKLE].seestate);
......@@ -7445,7 +7495,7 @@ static void P_NiGHTSMovement(player_t *player)
{
helpermobj->fuse = player->mo->fuse = leveltime;
P_SetTarget(&helpermobj->target, player->mo);
P_SetScale(helpermobj, player->mo->scale);
P_SetScale(helpermobj, player->mo->scale, false);
}
}
......@@ -7571,7 +7621,7 @@ static void P_NiGHTSMovement(player_t *player)
|| (cmd->buttons & BT_SPIN)))
{
if (!(player->pflags & PF_STARTDASH))
S_StartSound(player->mo, sfx_ngskid);
S_StartSoundFromMobj(player->mo, sfx_ngskid);
// You can tap the button to only slow down a bit,
// or hold it to slow to a crawl as before, your choice.
......@@ -7635,18 +7685,17 @@ static void P_NiGHTSMovement(player_t *player)
if (!P_MobjWasRemoved(water))
{
if (player->mo->eflags & MFE_GOOWATER)
S_StartSound(water, sfx_ghit);
S_StartSoundFromMobj(water, sfx_ghit);
else if (player->mo->eflags & MFE_TOUCHLAVA)
S_StartSound(water, sfx_splash);
S_StartSoundFromMobj(water, sfx_splash);
else
S_StartSound(water, sfx_wslap);
S_StartSoundFromMobj(water, sfx_wslap);
if (player->mo->eflags & MFE_VERTICALFLIP)
{
water->flags2 |= MF2_OBJECTFLIP;
water->eflags |= MFE_VERTICALFLIP;
}
water->destscale = player->mo->scale;
P_SetScale(water, player->mo->scale);
P_SetScale(water, player->mo->scale, true);
}
}
......@@ -7767,7 +7816,7 @@ static void P_NiGHTSMovement(player_t *player)
{
if (firstdrill)
{
S_StartSound(player->mo, sfx_drill1);
S_StartSoundFromMobj(player->mo, sfx_drill1);
player->drilltimer = 32;
}
else if (player->drilltimer == 32)
......@@ -7784,7 +7833,7 @@ static void P_NiGHTSMovement(player_t *player)
else if (player->drilltimer <= 0)
{
player->drilltimer = 10;
S_StartSound(player->mo, sfx_drill2);
S_StartSoundFromMobj(player->mo, sfx_drill2);
}
}
......@@ -7845,7 +7894,7 @@ static void P_PlayerDropWeapon(player_t *player)
void P_BlackOw(player_t *player)
{
INT32 i;
S_StartSound (player->mo, sfx_bkpoof); // Sound the BANG!
S_StartSoundFromMobj(player->mo, sfx_bkpoof); // Sound the BANG!
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && P_AproxDistance(player->mo->x - players[i].mo->x,
......@@ -7889,8 +7938,7 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
P_SetTarget(&flame->target, player->mo);
flame->angle = travelangle + i*(ANGLE_MAX/numangles);
flame->fuse = TICRATE*7; // takes about an extra second to hit the ground
flame->destscale = player->mo->scale;
P_SetScale(flame, player->mo->scale);
P_SetScale(flame, player->mo->scale, true);
if (!(player->mo->flags2 & MF2_OBJECTFLIP) != !(player->powers[pw_gravityboots])) // take gravity boots into account
flame->flags2 |= MF2_OBJECTFLIP;
flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
......@@ -7927,8 +7975,7 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
P_SetTarget(&flame->target, player->mo);
flame->angle = travelangle;
flame->fuse = TICRATE*6;
flame->destscale = player->mo->scale;
P_SetScale(flame, player->mo->scale);
P_SetScale(flame, player->mo->scale, true);
if (!(player->mo->flags2 & MF2_OBJECTFLIP) != !(player->powers[pw_gravityboots])) // take gravity boots into account
flame->flags2 |= MF2_OBJECTFLIP;
flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
......@@ -7976,8 +8023,7 @@ void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound)
}
particle->tics = 10;
particle->destscale = (2*mo->scale)/3;
P_SetScale(particle, particle->destscale);
P_SetScale(particle, (2*mo->scale)/3, true);
P_SetObjectMomZ(particle, FRACUNIT, false);
if (mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)) // overrides fire version
......@@ -7986,7 +8032,7 @@ void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound)
P_SetMobjState(particle, S_SPINDUST_FIRE1);
if (sound)
S_StartSound(mo, sfx_s3k7e); // the proper "Knuckles eats dirt" sfx.
S_StartSoundFromMobj(mo, sfx_s3k7e); // the proper "Knuckles eats dirt" sfx.
}
static void P_SkidStuff(player_t *player)
......@@ -8033,7 +8079,7 @@ static void P_SkidStuff(player_t *player)
// Spawn a particle every 3 tics.
if (!(player->skidtime % 3))
{
if (player->mo->state-states == S_PLAY_GLIDE_LANDING)
if (P_IsPlayerInState(player, S_PLAY_GLIDE_LANDING))
P_SpawnSkidDust(player, player->mo->radius, true);
else
P_SpawnSkidDust(player, 0, false);
......@@ -8053,10 +8099,10 @@ static void P_SkidStuff(player_t *player)
// If your push angle is more than this close to a full 180 degrees, trigger a skid.
if (dang > ANGLE_157h)
{
if (player->mo->state-states != S_PLAY_SKID)
if (!P_IsPlayerInState(player, S_PLAY_SKID))
P_SetMobjState(player->mo, S_PLAY_SKID);
player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
S_StartSound(player->mo, sfx_skid);
S_StartSoundFromMobj(player->mo, sfx_skid);
}
}
}
......@@ -8077,7 +8123,7 @@ void P_MovePlayer(player_t *player)
fixed_t runspd;
if (player->mo->state >= &states[S_PLAY_SUPER_TRANS1] && player->mo->state <= &states[S_PLAY_SUPER_TRANS6])
if (P_IsPlayerInSuperTransformationState(player))
{
player->mo->momx = player->mo->momy = player->mo->momz = 0;
return;
......@@ -8098,7 +8144,7 @@ void P_MovePlayer(player_t *player)
if ((player->powers[pw_carry] == CR_BRAKGOOP)
|| (player->pflags & PF_GLIDING && player->skidtime)
|| (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2)
|| (player->charability2 == CA2_MELEE && player->mo->state-states == S_PLAY_MELEE_LANDING))
|| (player->charability2 == CA2_MELEE && P_IsPlayerInState(player, S_PLAY_MELEE_LANDING)))
player->pflags |= PF_FULLSTASIS;
else if (player->powers[pw_nocontrol])
{
......@@ -8107,7 +8153,7 @@ void P_MovePlayer(player_t *player)
player->pflags |= PF_JUMPSTASIS;
}
if (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING)
if (player->charability == CA_GLIDEANDCLIMB && P_IsPlayerInState(player, S_PLAY_GLIDE_LANDING))
{
player->pflags |= PF_STASIS;
}
......@@ -8172,7 +8218,7 @@ void P_MovePlayer(player_t *player)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -8213,7 +8259,7 @@ void P_MovePlayer(player_t *player)
if (G_IsSpecialStage(gamemap))
{
if (player == &players[displayplayer]) // only play the sound for yourself landing
S_StartSound(NULL, sfx_s3k6a);
S_StartSoundFromEverywhere(sfx_s3k6a);
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
players[i].exiting = (14*TICRATE)/5 + 1;
......@@ -8325,9 +8371,9 @@ void P_MovePlayer(player_t *player)
// Correct floating when ending up on the ground.
if (onground)
{
if (player->mo->state-states == S_PLAY_FLOAT)
if (P_IsPlayerInState(player, S_PLAY_FLOAT))
P_SetMobjState(player->mo, S_PLAY_WALK);
else if (player->mo->state-states == S_PLAY_FLOAT_RUN)
else if (P_IsPlayerInState(player, S_PLAY_FLOAT_RUN))
P_SetMobjState(player->mo, S_PLAY_RUN);
}
......@@ -8393,7 +8439,7 @@ void P_MovePlayer(player_t *player)
fixed_t glidespeed = player->actionspd;
fixed_t momx = mo->momx - player->cmomx, momy = mo->momy - player->cmomy;
angle_t angle, moveangle = R_PointToAngle2(0, 0, momx, momy);
boolean swimming = mo->state - states == S_PLAY_SWIM;
boolean swimming = P_IsPlayerInState(player, S_PLAY_SWIM);
boolean in2d = mo->flags2 & MF2_TWOD || twodlevel;
if (player->powers[pw_super] || player->powers[pw_sneakers])
......@@ -8540,7 +8586,7 @@ void P_MovePlayer(player_t *player)
}
}
}
else if (player->mo->state-states == S_PLAY_BOUNCE)
else if (P_IsPlayerInState(player, S_PLAY_BOUNCE))
P_SetMobjState(player->mo, S_PLAY_FALL);
// If you're running fast enough, you can create splashes as you run in shallow water.
......@@ -8556,18 +8602,17 @@ void P_MovePlayer(player_t *player)
if (!P_MobjWasRemoved(water))
{
if (player->mo->eflags & MFE_GOOWATER)
S_StartSound(water, sfx_ghit);
S_StartSoundFromMobj(water, sfx_ghit);
else if (player->mo->eflags & MFE_TOUCHLAVA)
S_StartSound(water, sfx_splash);
S_StartSoundFromMobj(water, sfx_splash);
else
S_StartSound(water, sfx_wslap);
S_StartSoundFromMobj(water, sfx_wslap);
if (player->mo->eflags & MFE_VERTICALFLIP)
{
water->flags2 |= MF2_OBJECTFLIP;
water->eflags |= MFE_VERTICALFLIP;
}
water->destscale = player->mo->scale;
P_SetScale(water, player->mo->scale);
P_SetScale(water, player->mo->scale, true);
}
}
......@@ -8575,7 +8620,7 @@ void P_MovePlayer(player_t *player)
if ((player->mo->eflags & MFE_TOUCHWATER) && !(player->mo->eflags & MFE_UNDERWATER) && !player->spectator)
{
if (P_RandomChance(FRACUNIT/2) && leveltime % TICRATE == 0)
S_StartSound(player->mo, sfx_floush);
S_StartSoundFromMobj(player->mo, sfx_floush);
}
////////////////
......@@ -8585,7 +8630,7 @@ void P_MovePlayer(player_t *player)
if (!(player->charability == CA_FLY || player->charability == CA_SWIM)) // why are you flying when you cannot fly?!
{
if (player->powers[pw_tailsfly]
|| player->mo->state-states == S_PLAY_FLY_TIRED)
|| P_IsPlayerInState(player, S_PLAY_FLY_TIRED))
{
if (onground)
P_SetMobjState(player->mo, S_PLAY_WALK);
......@@ -8638,7 +8683,7 @@ void P_MovePlayer(player_t *player)
&& !(player->mo->eflags & MFE_UNDERWATER)
&& leveltime % 10 == 0
&& !player->spectator)
S_StartSound(player->mo, sfx_putput);
S_StartSoundFromMobj(player->mo, sfx_putput);
// Descend
if (cmd->buttons & BT_SPIN && !(player->pflags & PF_STASIS) && !player->exiting && !(player->mo->eflags & MFE_GOOWATER))
......@@ -8653,14 +8698,14 @@ void P_MovePlayer(player_t *player)
else
{
// Tails-gets-tired Stuff
if (player->panim == PA_ABILITY && player->mo->state-states != S_PLAY_FLY_TIRED)
if (player->panim == PA_ABILITY && !P_IsPlayerInState(player, S_PLAY_FLY_TIRED))
P_SetMobjState(player->mo, S_PLAY_FLY_TIRED);
if (player->charability == CA_FLY && (leveltime % 10 == 0)
&& player->mo->state-states == S_PLAY_FLY_TIRED
&& P_IsPlayerInState(player, S_PLAY_FLY_TIRED)
&& !(player->mo->eflags & MFE_UNDERWATER)
&& !player->spectator)
S_StartSound(player->mo, sfx_pudpud);
S_StartSoundFromMobj(player->mo, sfx_pudpud);
}
}
......@@ -8752,7 +8797,7 @@ void P_MovePlayer(player_t *player)
}
// Otherwise, face the direction you're travelling.
else if (player->panim == PA_WALK || player->panim == PA_RUN || player->panim == PA_DASH || player->panim == PA_ROLL || player->panim == PA_JUMP
|| (player->panim == PA_ABILITY && player->mo->state-states == S_PLAY_GLIDE))
|| (player->panim == PA_ABILITY && P_IsPlayerInState(player, S_PLAY_GLIDE)))
player->mo->angle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
// Update the local angle control.
......@@ -8778,32 +8823,19 @@ void P_MovePlayer(player_t *player)
&& player->panim == PA_IDLE && !(player->powers[pw_carry]))
P_DoTeeter(player);
// Check for fire and shield buttons
if (!player->exiting && !(player->pflags & PF_STASIS))
{
// Check for fire buttons
P_DoFiring(player, cmd);
// Release the shield button
if (!(cmd->buttons & BT_SHIELD))
player->pflags &= ~PF_SHIELDDOWN;
// Shield button behavior
// Check P_PlayerShieldThink for actual shields!
else if (!(player->pflags & PF_SHIELDDOWN))
// Toss a flag
if (G_GametypeHasTeams() && (cmd->buttons & BT_TOSSFLAG) && !(player->powers[pw_super]) && !(player->tossdelay))
{
// Transform into super if we can!
if (P_SuperReady(player, true))
P_DoSuperTransformation(player, false);
// Detransform from super if we can!
else if (P_SuperReady(player, false))
P_DoSuperDetransformation(player);
player->pflags |= PF_SHIELDDOWN;
}
if (!(player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
P_PlayerEmeraldBurst(player, true); // Toss emeralds
else
P_PlayerFlagBurst(player, true);
}
// check for fire
if (!player->exiting)
P_DoFiring(player, cmd);
{
boolean atspinheight = false;
fixed_t oldheight = player->mo->height;
......@@ -8821,6 +8853,8 @@ void P_MovePlayer(player_t *player)
player->mo->height = P_GetPlayerSpinHeight(player);
atspinheight = true;
}
else if (player->powers[pw_carry] == CR_PLAYER || player->powers[pw_carry] == CR_PTERABYTE) // You're slightly shorter while being carried
player->mo->height = FixedDiv(P_GetPlayerHeight(player), FixedDiv(14*FRACUNIT,10*FRACUNIT));
else
player->mo->height = P_GetPlayerHeight(player);
......@@ -8995,7 +9029,7 @@ static void P_DoRopeHang(player_t *player)
// Play the 'clink' sound only if the player is moving.
if (!(leveltime & 7) && player->speed)
S_StartSound(player->mo, sfx_s3k55);
S_StartSoundFromMobj(player->mo, sfx_s3k55);
playerz = player->mo->z + player->mo->height;
......@@ -9024,7 +9058,7 @@ static void P_DoRopeHang(player_t *player)
return;
}
if (player->mo->state-states != S_PLAY_RIDE)
if (!P_IsPlayerInState(player, S_PLAY_RIDE))
P_SetMobjState(player->mo, S_PLAY_RIDE);
// If not allowed to move, we're done here.
......@@ -9151,7 +9185,7 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (think->removing)
continue;
mo = (mobj_t *)think;
......@@ -9249,7 +9283,7 @@ mobj_t *P_LookForFocusTarget(player_t *player, mobj_t *exclude, SINT8 direction,
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (think->removing)
continue;
mo = (mobj_t *)think;
......@@ -9368,7 +9402,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (think->removing)
continue;
mo = (mobj_t *)think;
......@@ -9514,7 +9548,7 @@ void P_FindEmerald(void)
// to find all emeralds
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -9571,7 +9605,7 @@ boolean P_GetLives(player_t *player)
if (maxlivesplayer != -1 && &players[maxlivesplayer] != player)
{
if (cv_cooplives.value == 2 && (P_IsLocalPlayer(player) || P_IsLocalPlayer(&players[maxlivesplayer])))
S_StartSound(NULL, sfx_jshard); // placeholder
S_StartSoundFromEverywhere(sfx_jshard); // placeholder
if (players[maxlivesplayer].lives != INFLIVES)
players[maxlivesplayer].lives--;
player->lives++;
......@@ -9817,20 +9851,21 @@ static CV_PossibleValue_t CV_CamSpeed[] = {{0, "MIN"}, {1*FRACUNIT, "MAX"}, {0,
static CV_PossibleValue_t rotation_cons_t[] = {{1, "MIN"}, {25, "MAX"}, {0, NULL}};
static CV_PossibleValue_t CV_CamRotate[] = {{-720, "MIN"}, {720, "MAX"}, {0, NULL}};
static CV_PossibleValue_t multiplier_cons_t[] = {{0, "MIN"}, {3*FRACUNIT, "MAX"}, {0, NULL}};
static CV_PossibleValue_t campos_cons_t[] = { {INT32_MIN, "MIN"}, {INT32_MAX, "MAX"}, {0, NULL} };
consvar_t cv_cam_dist = CVAR_INIT ("cam_curdist", "160", CV_FLOAT|CV_ALLOWLUA, NULL, NULL);
consvar_t cv_cam_height = CVAR_INIT ("cam_curheight", "25", CV_FLOAT|CV_ALLOWLUA, NULL, NULL);
consvar_t cv_cam_dist = CVAR_INIT ("cam_curdist", "160", CV_FLOAT|CV_ALLOWLUA, campos_cons_t, NULL);
consvar_t cv_cam_height = CVAR_INIT ("cam_curheight", "25", CV_FLOAT|CV_ALLOWLUA, campos_cons_t, NULL);
consvar_t cv_cam_still = CVAR_INIT ("cam_still", "Off", CV_ALLOWLUA, CV_OnOff, NULL);
consvar_t cv_cam_speed = CVAR_INIT ("cam_speed", "0.3", CV_FLOAT|CV_SAVE|CV_ALLOWLUA, CV_CamSpeed, NULL);
consvar_t cv_cam_speed = CVAR_INIT ("cam_speed", "0.4", CV_FLOAT|CV_SAVE|CV_ALLOWLUA, CV_CamSpeed, NULL);
consvar_t cv_cam_rotate = CVAR_INIT ("cam_rotate", "0", CV_CALL|CV_NOINIT|CV_ALLOWLUA, CV_CamRotate, CV_CamRotate_OnChange);
consvar_t cv_cam_rotspeed = CVAR_INIT ("cam_rotspeed", "10", CV_SAVE|CV_ALLOWLUA, rotation_cons_t, NULL);
consvar_t cv_cam_turnmultiplier = CVAR_INIT ("cam_turnmultiplier", "0.75", CV_FLOAT|CV_SAVE|CV_ALLOWLUA, multiplier_cons_t, NULL);
consvar_t cv_cam_orbit = CVAR_INIT ("cam_orbit", "Off", CV_SAVE|CV_ALLOWLUA, CV_OnOff, NULL);
consvar_t cv_cam_adjust = CVAR_INIT ("cam_adjust", "On", CV_SAVE|CV_ALLOWLUA, CV_OnOff, NULL);
consvar_t cv_cam2_dist = CVAR_INIT ("cam2_curdist", "160", CV_FLOAT|CV_ALLOWLUA, NULL, NULL);
consvar_t cv_cam2_height = CVAR_INIT ("cam2_curheight", "25", CV_FLOAT|CV_ALLOWLUA, NULL, NULL);
consvar_t cv_cam2_dist = CVAR_INIT ("cam2_curdist", "160", CV_FLOAT|CV_ALLOWLUA, campos_cons_t, NULL);
consvar_t cv_cam2_height = CVAR_INIT ("cam2_curheight", "25", CV_FLOAT|CV_ALLOWLUA, campos_cons_t, NULL);
consvar_t cv_cam2_still = CVAR_INIT ("cam2_still", "Off", CV_ALLOWLUA, CV_OnOff, NULL);
consvar_t cv_cam2_speed = CVAR_INIT ("cam2_speed", "0.3", CV_FLOAT|CV_SAVE|CV_ALLOWLUA, CV_CamSpeed, NULL);
consvar_t cv_cam2_speed = CVAR_INIT ("cam2_speed", "0.4", CV_FLOAT|CV_SAVE|CV_ALLOWLUA, CV_CamSpeed, NULL);
consvar_t cv_cam2_rotate = CVAR_INIT ("cam2_rotate", "0", CV_CALL|CV_NOINIT|CV_ALLOWLUA, CV_CamRotate, CV_CamRotate2_OnChange);
consvar_t cv_cam2_rotspeed = CVAR_INIT ("cam2_rotspeed", "10", CV_SAVE|CV_ALLOWLUA, rotation_cons_t, NULL);
consvar_t cv_cam2_turnmultiplier = CVAR_INIT ("cam2_turnmultiplier", "0.75", CV_FLOAT|CV_SAVE|CV_ALLOWLUA, multiplier_cons_t, NULL);
......@@ -9840,23 +9875,23 @@ consvar_t cv_cam2_adjust = CVAR_INIT ("cam2_adjust", "On", CV_SAVE|CV_ALLOWLUA,
// [standard vs simple][p1 or p2]
consvar_t cv_cam_savedist[2][2] = {
{ // standard
CVAR_INIT ("cam_dist", "192", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, NULL, CV_UpdateCamDist),
CVAR_INIT ("cam2_dist", "192", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, NULL, CV_UpdateCam2Dist),
CVAR_INIT ("cam_dist", "192", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, campos_cons_t, CV_UpdateCamDist),
CVAR_INIT ("cam2_dist", "192", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, campos_cons_t, CV_UpdateCam2Dist),
},
{ // simple
CVAR_INIT ("cam_simpledist", "256", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, NULL, CV_UpdateCamDist),
CVAR_INIT ("cam2_simpledist", "256", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, NULL, CV_UpdateCam2Dist),
CVAR_INIT ("cam_simpledist", "256", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, campos_cons_t, CV_UpdateCamDist),
CVAR_INIT ("cam2_simpledist", "256", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, campos_cons_t, CV_UpdateCam2Dist),
}
};
consvar_t cv_cam_saveheight[2][2] = {
{ // standard
CVAR_INIT ("cam_height", "40", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, NULL, CV_UpdateCamDist),
CVAR_INIT ("cam2_height", "40", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, NULL, CV_UpdateCam2Dist),
CVAR_INIT ("cam_height", "40", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, campos_cons_t, CV_UpdateCamDist),
CVAR_INIT ("cam2_height", "40", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, campos_cons_t, CV_UpdateCam2Dist),
},
{ // simple
CVAR_INIT ("cam_simpleheight", "60", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, NULL, CV_UpdateCamDist),
CVAR_INIT ("cam2_simpleheight", "60", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, NULL, CV_UpdateCam2Dist),
CVAR_INIT ("cam_simpleheight", "60", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, campos_cons_t, CV_UpdateCamDist),
CVAR_INIT ("cam2_simpleheight", "60", CV_FLOAT|CV_SAVE|CV_CALL|CV_ALLOWLUA, campos_cons_t, CV_UpdateCam2Dist),
}
};
......@@ -9960,9 +9995,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
&& !((gametyperules & GTR_FRIENDLY) && (netgame || multiplayer) && cv_exitmove.value)
&& !(twodlevel || (mo->flags2 & MF2_TWOD)))
sign = mo->target;
else if ((player->powers[pw_carry] == CR_NIGHTSMODE)
&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6]))
else if (player->powers[pw_carry] == CR_NIGHTSMODE && !P_IsPlayerInNightsTransformationState(player))
{
P_CalcChasePostImg(player, thiscam);
return true;
......@@ -9973,15 +10006,42 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (!(player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || tutorialmode))
{
if (player->spectator) // force cam off for spectators
return true;
if (player->spectator || !thiscam->chase)
{
// set the values to the player's values so they can still be used
thiscam->x = player->mo->x;
thiscam->y = player->mo->y;
thiscam->z = player->viewz;
thiscam->momx = player->mo->momx;
thiscam->momy = player->mo->momy;
thiscam->momz = player->mo->momz;
if (!cv_chasecam.value && thiscam == &camera)
return true;
if (thiscam == &camera)
{
// when not spectating, use local angles
if (&players[displayplayer] == &players[consoleplayer]) {
thiscam->angle = localangle;
thiscam->aiming = localaiming;
}
else
{
thiscam->angle = players[displayplayer].cmd.angleturn << 16;
thiscam->aiming = players[displayplayer].cmd.aiming << 16;
}
}
else if (thiscam == &camera2)
{
// i dont think secondarydisplayplayer changes, so we should be fine.
thiscam->angle = localangle2;
thiscam->aiming = localaiming2;
}
if (!cv_chasecam2.value && thiscam == &camera2)
thiscam->subsector = player->mo->subsector;
thiscam->floorz = player->mo->floorz;
thiscam->ceilingz = player->mo->ceilingz;
return true;
}
}
if (!thiscam->chase && !resetcalled)
{
......@@ -10844,7 +10904,7 @@ void P_DoPityCheck(player_t *player)
P_SwitchShield(player, SH_PITY);
if (player->pity > 0)
S_StartSound(player->mo, mobjinfo[MT_PITY_ICON].seesound);
S_StartSoundFromMobj(player->mo, mobjinfo[MT_PITY_ICON].seesound);
player->pity = 0;
}
......@@ -10914,7 +10974,7 @@ static mobj_t *P_GetAxis(INT32 num)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mobj = (mobj_t *)th;
......@@ -10999,8 +11059,7 @@ static void P_SpawnSparks(mobj_t *mo, angle_t maindir)
spark->momz = mo->momz + r3;
P_Thrust(spark, R_PointToAngle2(mo->x, mo->y, spark->x, spark->y), 8*FRACUNIT);
P_SetScale(spark, FRACUNIT/4);
spark->destscale = spark->scale;
P_SetScale(spark, FRACUNIT/4, true);
spark->fuse = TICRATE/3;
}
......@@ -11120,7 +11179,7 @@ static void P_MinecartThink(player_t *player)
if (minecart->eflags & MFE_JUSTHITFLOOR)
{
S_StopSound(minecart);
S_StartSound(minecart, sfx_s3k96);
S_StartSoundFromMobj(minecart, sfx_s3k96);
}
sec = P_GetMinecartSector(minecart->x, minecart->y, minecart->z, &dummy);
......@@ -11208,7 +11267,7 @@ static void P_MinecartThink(player_t *player)
else
minecart->momz = 10 * FRACUNIT;
S_StartSound(minecart, sfx_s3k51);
S_StartSoundFromMobj(minecart, sfx_s3k51);
jumped = true;
}
......@@ -11236,7 +11295,7 @@ static void P_MinecartThink(player_t *player)
if (minecart->movecount > 128*FRACUNIT)
{
minecart->movecount %= 128*FRACUNIT;
S_StartSound(minecart, minecart->info->activesound);
S_StartSoundFromMobj(minecart, minecart->info->activesound);
}
}
......@@ -11280,7 +11339,7 @@ static void P_MinecartThink(player_t *player)
}
}
if (player->mo->state-states != S_PLAY_STND)
if (!P_IsPlayerInState(player, S_PLAY_STND))
{
P_SetMobjState(player->mo, S_PLAY_STND);
player->mo->tics = -1;
......@@ -11307,7 +11366,7 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
fixed_t backwards = -1*FRACUNIT;
boolean doswim = (player->panim == PA_ABILITY && (player->mo->eflags & MFE_UNDERWATER));
boolean doroll = (player->panim == PA_ROLL || (player->panim == PA_JUMP && !(player->charflags & SF_NOJUMPSPIN)) || doswim);
angle_t rollangle;
angle_t rollangle = 0;
boolean panimchange;
INT32 ticnum = 0;
statenum_t chosenstate;
......@@ -11358,12 +11417,12 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
}
else if (player->panim == PA_PAIN)
backwards /= 16;
else if (player->mo->state-states == S_PLAY_GASP)
else if (P_IsPlayerInState(player, S_PLAY_GASP))
{
backwards /= 16;
zoffs += 12*FRACUNIT;
}
else if (player->mo->state-states == S_PLAY_EDGE)
else if (P_IsPlayerInState(player, S_PLAY_EDGE))
{
backwards /= 16;
zoffs = 3*FRACUNIT;
......@@ -11392,13 +11451,13 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
}
else if (player->panim == PA_SPRING || player->panim == PA_JUMP)
chosenstate = S_TAILSOVERLAY_MINUS60DEGREES;
else if (player->panim == PA_FALL || player->mo->state-states == S_PLAY_RIDE)
else if (player->panim == PA_FALL || P_IsPlayerInState(player, S_PLAY_RIDE))
chosenstate = S_TAILSOVERLAY_PLUS60DEGREES;
else if (player->panim == PA_PAIN)
chosenstate = S_TAILSOVERLAY_PAIN;
else if (player->mo->state-states == S_PLAY_GASP)
else if (P_IsPlayerInState(player, S_PLAY_GASP))
chosenstate = S_TAILSOVERLAY_GASP;
else if (player->mo->state-states == S_PLAY_EDGE)
else if (P_IsPlayerInState(player, S_PLAY_EDGE))
chosenstate = S_TAILSOVERLAY_EDGE;
else if (player->panim == PA_DASH)
chosenstate = S_TAILSOVERLAY_DASH;
......@@ -11406,7 +11465,7 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
chosenstate = S_TAILSOVERLAY_RUN;
else if (player->panim == PA_WALK)
{
if (!smilesonground || player->mo->state-states == S_PLAY_SKID)
if (!smilesonground || P_IsPlayerInState(player, S_PLAY_SKID))
chosenstate = S_TAILSOVERLAY_PLUS30DEGREES;
else if (player->speed >= FixedMul(player->runspeed/2, player->mo->scale))
chosenstate = S_TAILSOVERLAY_0DEGREES;
......@@ -11434,10 +11493,10 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
}
else
{
if (tails->state != states+chosenstate)
if (tails->state != &states[chosenstate])
{
if (states[chosenstate].sprite == SPR_PLAY)
tails->sprite2 = P_GetSkinSprite2(((skin_t *)tails->skin), (states[chosenstate].frame & FF_FRAMEMASK), player);
tails->sprite2 = P_GetSkinSprite2(((skin_t *)tails->skin), P_GetStateSprite2(&states[chosenstate]), player);
P_SetMobjState(tails, chosenstate);
}
}
......@@ -11448,7 +11507,7 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
#endif
// animation...
if (player->panim == PA_SPRING || player->panim == PA_FALL || player->mo->state-states == S_PLAY_RIDE)
if (player->panim == PA_SPRING || player->panim == PA_FALL || P_IsPlayerInState(player, S_PLAY_RIDE))
{
if (FixedDiv(abs(player->mo->momz), player->mo->scale) < 20<<FRACBITS)
ticnum = 2;
......@@ -11457,7 +11516,7 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
}
else if (player->panim == PA_PAIN)
ticnum = 2;
else if (player->mo->state-states == S_PLAY_GASP)
else if (P_IsPlayerInState(player, S_PLAY_GASP))
tails->tics = -1;
else if (player->mo->sprite2 == SPR2_TIRE)
ticnum = (doswim ? 2 : 4);
......@@ -11472,8 +11531,9 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
tails->threshold = player->mo->z;
tails->movecount = player->panim;
tails->angle = horizangle;
P_SetScale(tails, player->mo->scale);
P_SetScale(tails, player->mo->scale, false);
tails->destscale = player->mo->destscale;
tails->old_scale = player->mo->old_scale;
tails->radius = player->mo->radius;
tails->height = player->mo->height;
zoffs = FixedMul(zoffs, tails->scale);
......@@ -11503,6 +11563,18 @@ void P_DoTailsOverlay(player_t *player, mobj_t *tails)
}
// Metal Sonic's jet fume
//
// The follow object's state is set to its spawn state when deactivated.
// When the player is on a moving animation, the follow object goes to its see state.
// When dash mode is entered, the follow object switches to its melee state.
//
// If MF2_FRET is set, the jet fume will flash during dash mode.
// MF2_AMBUSH can be used to enable Metal Sonic's skidding animation.
// MF2_JUSTATTACKED will enable the color cycling.
// If the follow item is MT_METALJETFUME, the above two effects are automatically applied.
//
// MF2_STRONGBOX is internally used to track if the jet fume is in its deactivated state or not.
// MF2_BOSSNOTRAP is internally used to instantly reset the jet fume's scale to its intended scale.
void P_DoMetalJetFume(player_t *player, mobj_t *fume)
{
static const UINT8 FUME_SKINCOLORS[] =
......@@ -11529,17 +11601,30 @@ void P_DoMetalJetFume(player_t *player, mobj_t *fume)
fixed_t heightoffset = ((mo->eflags & MFE_VERTICALFLIP) ? mo->height - (P_GetPlayerHeight(player) >> 1) : (P_GetPlayerHeight(player) >> 1));
panim_t panim = player->panim;
tic_t dashmode = min(player->dashmode, DASHMODE_MAX);
boolean ismetaljetfume = fume->type == MT_METALJETFUME;
boolean notmoving = panim != PA_WALK && panim != PA_RUN && panim != PA_DASH;
boolean underwater = mo->eflags & MFE_UNDERWATER;
statenum_t stat = fume->state-states;
boolean resetinterp = false;
if (panim != PA_WALK && panim != PA_RUN && panim != PA_DASH) // turn invisible when not in a coherent movement state
if (notmoving) // deactivate when not in a coherent movement state
{
if ((fume->flags2 & MF2_STRONGBOX) == 0)
{
if (stat != fume->info->spawnstate)
P_SetMobjState(fume, fume->info->spawnstate);
fume->flags2 |= MF2_STRONGBOX;
}
if (P_MobjWasRemoved(fume) || ismetaljetfume)
return;
}
// Rotate on skid animation if follow item is MT_METALJETFUME, or if MF2_AMBUSH is set
if (player->mo->sprite2 == SPR2_SKID)
{
if ((ismetaljetfume && (player->charflags & SF_JETFUME)) || (fume->flags2 & MF2_AMBUSH))
angle += ANGLE_90;
}
if (underwater) // No fume underwater; spawn bubbles instead!
{
fume->movedir += FixedAngle(FixedDiv(2 * player->speed, 3 * mo->scale));
......@@ -11564,7 +11649,9 @@ void P_DoMetalJetFume(player_t *player, mobj_t *fume)
y = mo->y + radiusY + FixedMul(offsetH, factorY);
z = mo->z + heightoffset + offsetV;
bubble = P_SpawnMobj(x, y, z, MT_SMALLBUBBLE);
bubble->scale = mo->scale >> 1;
P_SetScale(bubble, mo->scale/2, true);
bubble->destscale = mo->scale;
bubble->scalespeed = FixedMul(bubble->scalespeed, mo->scale);
P_SetTarget(&bubble->dontdrawforviewmobj, mo); // Hide the bubble in first-person
}
......@@ -11573,54 +11660,82 @@ void P_DoMetalJetFume(player_t *player, mobj_t *fume)
if (panim == PA_WALK)
{
if (stat != fume->info->spawnstate)
if ((fume->flags2 & MF2_STRONGBOX) == 0)
{
fume->threshold = 0;
P_SetMobjState(fume, fume->info->spawnstate);
fume->threshold = 0;
fume->flags2 &= ~MF2_STRONGBOX;
}
if (P_MobjWasRemoved(fume) || ismetaljetfume)
return;
}
}
if (stat == fume->info->spawnstate) // If currently inivisble, activate!
// If currently deactivated, activate!
if (!notmoving && !underwater && (fume->flags2 & MF2_STRONGBOX))
{
P_SetMobjState(fume, (stat = fume->info->seestate));
P_SetScale(fume, mo->scale);
if (P_MobjWasRemoved(fume))
return;
P_SetScale(fume, mo->scale, false);
fume->flags2 &= ~MF2_STRONGBOX;
resetinterp = true;
}
if (dashmode > DASHMODE_THRESHOLD && stat != fume->info->seestate) // If in dashmode, grow really big and flash
// If in dash mode, grow really big
if (dashmode > DASHMODE_THRESHOLD && stat != fume->info->meleestate)
{
fume->destscale = mo->scale;
fume->flags2 ^= MF2_DONTDRAW;
fume->flags2 |= mo->flags2 & MF2_DONTDRAW;
// Flash if follow item is MT_METALJETFUME, or if MF2_FRET is set
if (ismetaljetfume || (fume->flags2 & MF2_FRET))
fume->flags2 ^= MF2_DONTDRAW;
}
else // Otherwise, pick a size and color depending on speed and proximity to dashmode
{
if (dashmode == DASHMODE_THRESHOLD && dashmode > (tic_t)fume->movecount) // If just about to enter dashmode, play the startup animation again
// If just about to enter dash mode, play the startup animation again
if (dashmode == DASHMODE_THRESHOLD && dashmode > (tic_t)fume->movecount)
{
P_SetMobjState(fume, (stat = fume->info->seestate));
P_SetScale(fume, mo->scale << 1);
P_SetMobjState(fume, fume->info->meleestate);
if (P_MobjWasRemoved(fume))
return;
P_SetScale(fume, 2*mo->scale, true);
}
fume->flags2 = (fume->flags2 & ~MF2_DONTDRAW) | (mo->flags2 & MF2_DONTDRAW);
fume->destscale = (mo->scale + FixedDiv(player->speed, player->normalspeed)) / (underwater ? 6 : 3);
// Do color cycling if follow item is MT_METALJETFUME, or if MF2_JUSTATTACKED is set
if (ismetaljetfume || (fume->flags2 & MF2_JUSTATTACKED))
fume->color = FUME_SKINCOLORS[(dashmode * sizeof(FUME_SKINCOLORS)) / (DASHMODE_MAX + 1)];
if (underwater)
{
fume->frame = (fume->frame & FF_FRAMEMASK) | FF_ANIMATE | (P_RandomRange(0, 9) * FF_TRANS10);
fume->frame = (fume->frame & ~FF_TRANSMASK) | (P_RandomRange(0, 9) << FF_TRANSSHIFT);
fume->threshold = 1;
}
else if (fume->threshold)
{
fume->frame = (fume->frame & FF_FRAMEMASK) | fume->state->frame;
fume->frame = (fume->frame & FF_FRAMEMASK) | (fume->state->frame & ~FF_FRAMEMASK);
fume->threshold = 0;
}
}
fume->movecount = dashmode; // keeps track of previous dashmode value so we know whether Metal is entering or leaving it
fume->flags2 = (fume->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP); // Make sure to flip in reverse gravity!
fume->eflags = (fume->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP); // Make sure to flip in reverse gravity!
// keeps track of previous dash mode value so we know whether Metal is entering or leaving it
fume->movecount = dashmode;
// Make sure to flip in reverse gravity!
fume->flags2 = (fume->flags2 & ~MF2_OBJECTFLIP) | (mo->flags2 & MF2_OBJECTFLIP);
fume->eflags = (fume->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP);
// Set the appropriate scale at spawn
// This is... strange, but I had to choose a flag that a follow object would not ordinarily use.
if ((fume->flags2 & MF2_BOSSNOTRAP) == 0)
{
P_SetScale(fume, fume->destscale, true);
fume->flags2 |= MF2_BOSSNOTRAP;
}
// Finally, set its position
dist = -mo->radius - FixedMul(fume->info->radius, fume->destscale - mo->scale/3);
......@@ -11630,7 +11745,8 @@ void P_DoMetalJetFume(player_t *player, mobj_t *fume)
fume->y = mo->y + P_ReturnThrustY(fume, angle, dist);
fume->z = mo->z + heightoffset - (fume->height >> 1);
P_SetThingPosition(fume);
if (resetinterp) R_ResetMobjInterpolationState(fume);
if (resetinterp)
R_ResetMobjInterpolationState(fume);
// If dash mode is high enough, spawn a trail
if (player->normalspeed >= skins[player->skin]->normalspeed*2)
......@@ -11646,6 +11762,8 @@ void P_DoFollowMobj(player_t *player, mobj_t *followmobj)
{
if (LUA_HookFollowMobj(player, followmobj) || P_MobjWasRemoved(followmobj))
{;}
else if (player->charflags & SF_JETFUME)
P_DoMetalJetFume(player, followmobj);
else
{
switch (followmobj->type)
......@@ -11994,7 +12112,7 @@ void P_PlayerThink(player_t *player)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
if (th->removing)
continue;
mo2 = (mobj_t *)th;
......@@ -12089,7 +12207,7 @@ void P_PlayerThink(player_t *player)
// deez New User eXperiences.
{
angle_t oldang = player->drawangle, diff = 0;
UINT8 factor;
UINT8 factor = 0;
// Directionchar!
// Camera angle stuff.
if (player->exiting // no control, no modification
......@@ -12135,6 +12253,10 @@ void P_PlayerThink(player_t *player)
case CR_DUSTDEVIL:
player->drawangle += ANG20;
break;
case CR_FAN:
if (player->pflags & PF_ANALOGMODE) // Don't impact drawangle in any special way when on a fan
player->drawangle = player->mo->angle;
break;
/* -- in case we wanted to have the camera freely movable during zoom tubes
case CR_ZOOMTUBE:*/
case CR_ROPEHANG:
......@@ -12217,10 +12339,9 @@ void P_PlayerThink(player_t *player)
diff = InvAngle(diff);
if (diff > ANG10/2)
{
statenum_t stat = player->mo->state-states;
if (stat == S_PLAY_WAIT)
if (P_IsPlayerInState(player, S_PLAY_WAIT))
P_SetMobjState(player->mo, S_PLAY_STND);
else if (stat == S_PLAY_STND && player->mo->tics != -1)
else if (P_IsPlayerInState(player, S_PLAY_STND) && player->mo->tics != -1)
player->mo->tics++;
}
}
......@@ -12245,12 +12366,12 @@ void P_PlayerThink(player_t *player)
// fake skidding! see P_SkidStuff for reference on conditionals
else if (!player->skidtime && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID) && P_AproxDistance(player->mo->momx, player->mo->momy) >= FixedMul(player->runspeed, player->mo->scale)) // modified from player->runspeed/2 'cuz the skid was just TOO frequent ngl
{
if (player->mo->state-states != S_PLAY_SKID)
if (!P_IsPlayerInState(player, S_PLAY_SKID))
P_SetMobjState(player->mo, S_PLAY_SKID);
player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
if (P_IsLocalPlayer(player)) // the sound happens way more frequently now, so give co-op players' ears a brake...
S_StartSound(player->mo, sfx_skid);
S_StartSoundFromMobj(player->mo, sfx_skid);
}
if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
......@@ -12416,16 +12537,14 @@ void P_PlayerThink(player_t *player)
player->stronganim = 0;
//pw_super acts as a timer now
if (player->powers[pw_super]
&& (player->mo->state < &states[S_PLAY_SUPER_TRANS1]
|| player->mo->state > &states[S_PLAY_SUPER_TRANS6]))
if (player->powers[pw_super] && !P_IsPlayerInSuperTransformationState(player))
player->powers[pw_super]++;
if (player->powers[pw_carry] == CR_BRAKGOOP)
{
if (!player->powers[pw_flashing])
{
if (player->mo->state != &states[S_PLAY_STND])
if (!P_IsPlayerInState(player, S_PLAY_STND))
P_SetMobjState(player->mo, S_PLAY_STND);
else
player->mo->tics = 2;
......@@ -12455,13 +12574,13 @@ void P_PlayerThink(player_t *player)
if (player->texttimer)
{
--player->texttimer;
if (!player->texttimer && !player->exiting && player->textvar >= 4)
if (!player->texttimer && !player->exiting && (player->textvar == NTV_NONE || player->textvar == NTV_BONUSTIMEEND))
{
player->texttimer = 4*TICRATE;
player->textvar = 2; // GET n RINGS!
player->textvar = NTV_GETSPHERES; // GET n SPHERES/CHIPS!
if (!P_MobjWasRemoved(player->capsule) && player->capsule->health != player->capsule->spawnpoint->angle)
player->textvar++; // GET n MORE RINGS!
if (!P_MobjWasRemoved(player->capsule) && player->capsule->health != player->capsule->spawnpoint->args[1])
player->textvar = NTV_GETMORESPHERES; // GET n MORE SPHERES/CHIPS!
}
}
......@@ -12474,8 +12593,6 @@ void P_PlayerThink(player_t *player)
else
player->mo->flags2 &= ~MF2_DONTDRAW;
player->pflags &= ~PF_SLIDING;
#define dashmode player->dashmode
// Dash mode - thanks be to VelocitOni
if ((player->charflags & SF_DASHMODE) && !player->gotflag && !player->powers[pw_carry] && !player->exiting && !(maptol & TOL_NIGHTS) && !metalrecording) // woo, dashmode! no nights tho.
......@@ -12489,7 +12606,7 @@ void P_PlayerThink(player_t *player)
if (dashmode < DASHMODE_MAX)
dashmode++; // Counter. Adds 1 to dash mode per tic in top speed.
if (dashmode == DASHMODE_THRESHOLD) // This isn't in the ">=" equation because it'd cause the sound to play infinitely.
S_StartSound(player->mo, (player->charflags & SF_MACHINE) ? sfx_kc4d : sfx_cdfm40); // If the player enters dashmode, play this sound on the the tic it starts.
S_StartSoundFromMobj(player->mo, (player->charflags & SF_MACHINE) ? sfx_kc4d : sfx_cdfm40); // If the player enters dashmode, play this sound on the the tic it starts.
}
else if ((!totallyradical || !floating) && !(player->pflags & PF_SPINNING))
{
......@@ -12497,7 +12614,7 @@ void P_PlayerThink(player_t *player)
{
dashmode -= 3; // Rather than lose it all, it gently counts back down!
if ((dashmode+3) >= DASHMODE_THRESHOLD && dashmode < DASHMODE_THRESHOLD)
S_StartSound(player->mo, sfx_kc65);
S_StartSoundFromMobj(player->mo, sfx_kc65);
}
else
dashmode = 0;
......@@ -12542,7 +12659,7 @@ void P_PlayerThink(player_t *player)
{
player->normalspeed = skins[player->skin]->normalspeed;
player->jumpfactor = skins[player->skin]->jumpfactor;
S_StartSound(player->mo, sfx_kc65);
S_StartSoundFromMobj(player->mo, sfx_kc65);
if (player->powers[pw_strong] & STR_DASH)
player->powers[pw_strong] = STR_NONE;
}
......@@ -12552,6 +12669,12 @@ void P_PlayerThink(player_t *player)
LUA_HookPlayer(player, HOOK(PlayerThink));
// Remove PF_SLIDING *AFTER* PlayerThink hooks, because
// no one wants to add a ThinkFrame just for detecting this (i'm also very lazy)
// This is such a trivial change, I doubt it'll change anything major
// -luigi budd
player->pflags &= ~PF_SLIDING;
/*
// Colormap verification
{
......@@ -12665,7 +12788,7 @@ void P_PlayerAfterThink(player_t *player)
// camera may still move when guy is dead
//if (!netgame)
{
if (thiscam && thiscam->chase)
if (thiscam)
P_MoveChaseCamera(player, thiscam, false);
}
if (player->followmobj)
......@@ -12787,10 +12910,10 @@ void P_PlayerAfterThink(player_t *player)
player->currentweapon = 0;
if (P_IsLocalPlayer(player) && (player->pflags & PF_WPNDOWN) && player->currentweapon != oldweapon)
S_StartSound(NULL, sfx_wepchg);
S_StartSoundFromEverywhere(sfx_wepchg);
if ((player->pflags & PF_SLIDING) && ((player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE)) != PF_JUMPED))
P_SetMobjState(player->mo, player->mo->info->painstate);
P_SetMobjState(player->mo, S_PLAY_PAIN);
/* if (player->powers[pw_carry] == CR_NONE && player->mo->tracer && !player->homing)
P_SetTarget(&player->mo->tracer, NULL);
......@@ -12829,9 +12952,9 @@ void P_PlayerAfterThink(player_t *player)
else
{
if (tails->player)
P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true);
P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*tails->scale), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*tails->scale), true);
else
P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->angle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->angle, 4*FRACUNIT), true);
P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->angle, 4*tails->scale), tails->y + P_ReturnThrustY(tails, tails->angle, 4*tails->scale), true);
player->mo->momx = tails->momx;
player->mo->momy = tails->momy;
player->mo->momz = tails->momz;
......@@ -12845,12 +12968,12 @@ void P_PlayerAfterThink(player_t *player)
P_SetPlayerAngle(player, player->mo->angle);
}
if (P_AproxDistance(player->mo->x - tails->x, player->mo->y - tails->y) > player->mo->radius)
if (P_AproxDistance(player->mo->x - tails->x, player->mo->y - tails->y) > tails->radius)
player->powers[pw_carry] = CR_NONE;
if (player->powers[pw_carry] == CR_PLAYER)
{
if (player->mo->state-states != S_PLAY_RIDE)
if (!P_IsPlayerInState(player, S_PLAY_RIDE))
P_SetMobjState(player->mo, S_PLAY_RIDE);
if (tails->player && (tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER))
tails->player->powers[pw_tailsfly] = 0;
......@@ -12875,7 +12998,7 @@ void P_PlayerAfterThink(player_t *player)
player->mo->z = item->z - FixedDiv(player->mo->height, 3*FRACUNIT/2);
player->mo->momx = player->mo->momy = player->mo->momz = 0;
P_SetThingPosition(player->mo);
if (player->mo->state-states != S_PLAY_RIDE)
if (!P_IsPlayerInState(player, S_PLAY_RIDE))
P_SetMobjState(player->mo, S_PLAY_RIDE);
// Controllable missile
......@@ -13066,14 +13189,14 @@ void P_PlayerAfterThink(player_t *player)
player->mo->momy = ptera->momy;
player->mo->momz = ptera->momz;
if (P_AproxDistance(player->mo->x - ptera->x - ptera->watertop, player->mo->y - ptera->y - ptera->waterbottom) > player->mo->radius)
if (P_AproxDistance(player->mo->x - ptera->x - ptera->watertop, player->mo->y - ptera->y - ptera->waterbottom) > ptera->radius)
goto dropoff;
ptera->watertop >>= 1;
ptera->waterbottom >>= 1;
ptera->cvmem >>= 1;
if (player->mo->state-states != S_PLAY_FALL)
if (!P_IsPlayerInState(player, S_PLAY_FALL))
P_SetMobjState(player->mo, S_PLAY_FALL);
break;
......@@ -13105,10 +13228,11 @@ void P_PlayerAfterThink(player_t *player)
player->viewz = player->mo->z + player->mo->height - player->viewheight;
else
player->viewz = player->mo->z + player->viewheight;
}
if (server || addedtogame)
P_MoveChaseCamera(player, thiscam, false); // calculate the camera movement
}
}
// spectator invisibility and nogravity.
if ((netgame || multiplayer) && player->spectator)
......@@ -13143,6 +13267,7 @@ void P_PlayerAfterThink(player_t *player)
player->followmobj->colorized = true;
break;
default:
if ((player->charflags & SF_JETFUME) == 0)
player->followmobj->flags2 |= MF2_LINKDRAW;
break;
}
......@@ -13219,9 +13344,9 @@ boolean P_PlayerCanEnterSpinGaps(player_t *player)
return false;
return ((player->pflags & (PF_SPINNING|PF_SLIDING|PF_GLIDING)) // players who are spinning, sliding, or gliding
|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING) // players who are landing from a glide
|| (player->charability == CA_GLIDEANDCLIMB && P_IsPlayerInState(player, S_PLAY_GLIDE_LANDING)) // players who are landing from a glide
|| ((player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE)
&& player->dashmode >= DASHMODE_THRESHOLD && player->mo->state-states == S_PLAY_DASH) // machine players in dashmode
&& player->dashmode >= DASHMODE_THRESHOLD && P_IsPlayerInState(player, S_PLAY_DASH)) // machine players in dashmode
|| JUMPCURLED(player)); // players who are jumpcurled, but only if they would normally jump that way
}
......@@ -13229,13 +13354,13 @@ boolean P_PlayerCanEnterSpinGaps(player_t *player)
boolean P_PlayerShouldUseSpinHeight(player_t *player)
{
return ((player->pflags & (PF_SPINNING|PF_SLIDING|PF_GLIDING))
|| (player->mo->state == &states[player->mo->info->painstate])
|| P_IsPlayerInState(player, S_PLAY_PAIN)
|| (player->panim == PA_ROLL)
|| ((player->powers[pw_tailsfly] || (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED))
|| ((player->powers[pw_tailsfly] || (player->charability == CA_FLY && P_IsPlayerInState(player, S_PLAY_FLY_TIRED)))
&& !(player->charflags & SF_NOJUMPSPIN))
|| (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING)
|| (player->charability == CA_GLIDEANDCLIMB && P_IsPlayerInState(player, S_PLAY_GLIDE_LANDING))
|| ((player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE)
&& player->dashmode >= DASHMODE_THRESHOLD && player->mo->state-states == S_PLAY_DASH)
&& player->dashmode >= DASHMODE_THRESHOLD && P_IsPlayerInState(player, S_PLAY_DASH))
|| JUMPCURLED(player));
}
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2025 by LJ Sonic
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file quaternion.c
/// \brief Fixed-point 3D vector
#include <string.h>
#include "quaternion.h"
#include "vector3d.h"
#include "matrix.h"
#include "r_main.h" // R_PointToDist2
quaternion_t *Quaternion_Set(quaternion_t *quat, fixed_t x, fixed_t y, fixed_t z, fixed_t w)
{
quat->x = x;
quat->y = y;
quat->z = z;
quat->w = w;
return quat;
}
quaternion_t *Quaternion_SetIdentity(quaternion_t *quat)
{
return Quaternion_Set(quat, 0, 0, 0, FRACUNIT);
}
quaternion_t *Quaternion_SetAxisRotation(quaternion_t *quat, vector3_t *axis, fixed_t angle)
{
fixed_t cosangle = FINECOSINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK);
fixed_t sinangle = FINESINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK);
vector3_t normaxis;
Vector3D_Normalize(&normaxis, axis);
return Quaternion_Set(quat,
FixedMul(normaxis.x, sinangle),
FixedMul(normaxis.y, sinangle),
FixedMul(normaxis.z, sinangle),
cosangle
);
}
quaternion_t *Quaternion_Copy(quaternion_t *out, quaternion_t *in)
{
return memcpy(out, in, sizeof(*out));
}
matrix_t *Quaternion_ToMatrix(matrix_t *mat, const quaternion_t *quat)
{
fixed_t x = quat->x, y = quat->y, z = quat->z, w = quat->w;
fixed_t xx2 = 2 * FixedMul(x, x);
fixed_t xy2 = 2 * FixedMul(x, y);
fixed_t xz2 = 2 * FixedMul(x, z);
fixed_t xw2 = 2 * FixedMul(x, w);
fixed_t yy2 = 2 * FixedMul(y, y);
fixed_t yz2 = 2 * FixedMul(y, z);
fixed_t yw2 = 2 * FixedMul(y, w);
fixed_t zz2 = 2 * FixedMul(z, z);
fixed_t zw2 = 2 * FixedMul(z, w);
Matrix_SetIdentity(mat);
mat->matrix[0][0] = FRACUNIT - yy2 - zz2;
mat->matrix[0][1] = xy2 - zw2;
mat->matrix[0][2] = xz2 + yw2;
mat->matrix[1][0] = xy2 + zw2;
mat->matrix[1][1] = FRACUNIT - xx2 - zz2;
mat->matrix[1][2] = yz2 - xw2;
mat->matrix[2][0] = xz2 - yw2;
mat->matrix[2][1] = yz2 + xw2;
mat->matrix[2][2] = FRACUNIT - xx2 - yy2;
mat->matrix[3][3] = FRACUNIT;
return mat;
}
quaternion_t *Quaternion_Normalize(quaternion_t *out, quaternion_t *in)
{
fixed_t sqlen =
FixedMul(in->x, in->x) +
FixedMul(in->y, in->y) +
FixedMul(in->z, in->z) +
FixedMul(in->w, in->w);
if (sqlen < FRACUNIT / 1024)
return Quaternion_Set(out, in->x, in->y, in->z, in->w);
fixed_t len = R_PointToDist2(0, 0, R_PointToDist2(0, 0, R_PointToDist2(0, 0, in->x, in->y), in->z), in->w);
return Quaternion_Set(out,
FixedDiv(in->x, len),
FixedDiv(in->y, len),
FixedDiv(in->z, len),
FixedDiv(in->w, len)
);
}
quaternion_t *Quaternion_Mul(quaternion_t *out, quaternion_t *a, quaternion_t *b)
{
fixed_t ax = a->x, ay = a->y, az = a->z, aw = a->w;
fixed_t bx = b->x, by = b->y, bz = b->z, bw = b->w;
return Quaternion_Normalize(out, Quaternion_Set(out,
FixedMul(aw, bx) + FixedMul(ax, bw) + FixedMul(ay, bz) - FixedMul(az, by),
FixedMul(aw, by) - FixedMul(ax, bz) + FixedMul(ay, bw) + FixedMul(az, bx),
FixedMul(aw, bz) + FixedMul(ax, by) - FixedMul(ay, bx) + FixedMul(az, bw),
FixedMul(aw, bw) - FixedMul(ax, bx) - FixedMul(ay, by) - FixedMul(az, bz)
));
}
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2025 by LJ Sonic
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file quaternion.c
/// \brief Fixed-point quaternion
#ifndef __QUATERNION__
#define __QUATERNION__
#include "m_fixed.h"
#include "matrix.h"
typedef struct
{
fixed_t x, y, z, w;
} quaternion_t;
quaternion_t *Quaternion_Set(quaternion_t *quat, fixed_t x, fixed_t y, fixed_t z, fixed_t w);
quaternion_t *Quaternion_SetIdentity(quaternion_t *quat);
quaternion_t *Quaternion_SetAxisRotation(quaternion_t *quat, vector3_t *axis, fixed_t angle);
quaternion_t *Quaternion_Copy(quaternion_t *out, quaternion_t *in);
matrix_t *Quaternion_ToMatrix(matrix_t *mat, const quaternion_t *quat);
quaternion_t *Quaternion_Normalize(quaternion_t *out, quaternion_t *in);
quaternion_t *Quaternion_Mul(quaternion_t *out, quaternion_t *a, quaternion_t *b);
#endif