Ball switch

This commit is contained in:
Sally Coolatta 2023-09-26 08:30:41 -04:00
parent 0ae5071bbb
commit 643cf46b61
9 changed files with 345 additions and 6 deletions

View file

@ -4671,6 +4671,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_CHECKPOINT_SPARK9",
"S_CHECKPOINT_SPARK10",
"S_CHECKPOINT_SPARK11",
"S_BALLSWITCH_BALL",
"S_BALLSWITCH_BALL_ACTIVE",
"S_BALLSWITCH_PAD",
"S_BALLSWITCH_PAD_ACTIVE",
};
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
@ -5823,6 +5828,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_CHECKPOINT_END",
"MT_SCRIPT_THING",
"MT_BALLSWITCH_BALL",
"MT_BALLSWITCH_PAD",
};
const char *const MOBJFLAG_LIST[] = {

View file

@ -892,6 +892,8 @@ char sprnames[NUMSPRITES + 1][5] =
"CPT2", // Checkpoint Stick
"CPT3", // Checkpoint Base
"SA2S", // SA2-style Ball Switch
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
"VIEW",
};
@ -5417,6 +5419,11 @@ state_t states[NUMSTATES] =
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|8, 1, {NULL}, 0, 0, S_CHECKPOINT_SPARK10}, // S_CHECKPOINT_SPARK9
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|3, 1, {NULL}, 0, 0, S_CHECKPOINT_SPARK11}, // S_CHECKPOINT_SPARK10
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_CHECKPOINT_SPARK1}, // S_CHECKPOINT_SPARK11
{SPR_SA2S, FF_SEMIBRIGHT|3, -1, {NULL}, 0, 0, S_NULL}, // S_BALLSWITCH_BALL
{SPR_SA2S, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM|4, -1, {NULL}, 1, 1, S_NULL}, // S_BALLSWITCH_BALL_ACTIVE
{SPR_SA2S, FF_FLOORSPRITE|FF_SEMIBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BALLSWITCH_PAD
{SPR_SA2S, FF_FLOORSPRITE|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM|1, -1, {NULL}, 1, 1, S_NULL}, // S_BALLSWITCH_PAD_ACTIVE
};
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
@ -30381,6 +30388,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_BALLSWITCH_BALL
5000, // doomednum
S_BALLSWITCH_BALL, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
36*FRACUNIT, // radius
64*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_SHOOTABLE|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
{ // MT_BALLSWITCH_PAD
-1, // doomednum
S_BALLSWITCH_PAD, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
64*FRACUNIT, // radius
16*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_SCENERY, // flags
S_NULL // raisestate
},
};
skincolor_t skincolors[MAXSKINCOLORS] = {

View file

@ -1446,6 +1446,8 @@ typedef enum sprite
SPR_CPT2, // Checkpoint Stick
SPR_CPT3, // Checkpoint Base
SPR_SA2S, // SA2-style Ball Switch
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
SPR_VIEW,
@ -5845,6 +5847,11 @@ typedef enum state
S_CHECKPOINT_SPARK10,
S_CHECKPOINT_SPARK11,
S_BALLSWITCH_BALL,
S_BALLSWITCH_BALL_ACTIVE,
S_BALLSWITCH_PAD,
S_BALLSWITCH_PAD_ACTIVE,
S_FIRSTFREESLOT,
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
NUMSTATES
@ -7016,6 +7023,9 @@ typedef enum mobj_type
MT_CHECKPOINT_END,
MT_SCRIPT_THING,
MT_BALLSWITCH_BALL,
MT_BALLSWITCH_PAD,
MT_FIRSTFREESLOT,
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
NUMMOBJTYPES

View file

@ -931,16 +931,12 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
}
else
{
if (victim->type == MT_ORBINAUT || victim->type == MT_JAWZ || victim->type == MT_GACHABOM
|| victim->type == MT_BANANA || victim->type == MT_EGGMANITEM || victim->type == MT_BALLHOG
|| victim->type == MT_SSMINE || victim->type == MT_LANDMINE || victim->type == MT_SINK
|| victim->type == MT_GARDENTOP || victim->type == MT_DROPTARGET || victim->type == MT_BATTLECAPSULE
|| victim->type == MT_MONITOR || victim->type == MT_SPECIAL_UFO || victim->type == MT_BATTLEUFO)
if (victim->flags & MF_SHOOTABLE)
{
// Monitor hack. We can hit monitors once per instawhip, no multihit shredding!
// Damage values in Obj_MonitorGetDamage.
// Apply to UFO also -- steelt 29062023
if (victim->type == MT_MONITOR || victim->type == MT_BATTLEUFO)
if (victim->type == MT_MONITOR || victim->type == MT_BATTLEUFO || victim->type == MT_BALLSWITCH_BALL)
{
if (shield->extravalue1 == 1)
return false;

View file

@ -233,6 +233,12 @@ boolean Obj_GetCheckpointRespawnPosition(const mobj_t *checkpoint, vector3_t *re
angle_t Obj_GetCheckpointRespawnAngle(const mobj_t *checkpoint);
void Obj_ActivateCheckpointInstantly(mobj_t* mobj);
/* Ball Switch */
void Obj_BallSwitchInit(mobj_t *mobj);
void Obj_BallSwitchThink(mobj_t *mobj);
void Obj_BallSwitchTouched(mobj_t *mobj, mobj_t *toucher);
void Obj_BallSwitchDamaged(mobj_t *mobj, mobj_t *inflictor, mobj_t *source);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -30,4 +30,5 @@ target_sources(SRB2SDL2 PRIVATE
emerald.c
checkpoint.cpp
shadow.cpp
ball-switch.cpp
)

237
src/objects/ball-switch.cpp Normal file
View file

@ -0,0 +1,237 @@
#include "../k_objects.h"
#include "../doomdef.h"
#include "../info.h"
#include "../p_local.h"
#include "../r_main.h"
#include "../k_hitlag.h"
#define ball_pad(o) ((o)->target)
#define ball_instawhipped(o) ((o)->extravalue1) // see instawhip collide
#define ball_cooldown(o) ((o)->cvmem)
#define ball_activedefer(o) ((o)->extravalue2)
#define ball_activator(o) ((o)->tracer)
namespace
{
struct BallSwitch_Pad : mobj_t
{
statenum_t Anim() const { return static_cast<statenum_t>(this->state - states); }
void Anim(statenum_t n)
{
if (Anim() != n)
{
P_SetMobjState(this, n);
}
}
void Spawned()
{
renderflags |= RF_FLOORSPRITE|RF_NOSPLATBILLBOARD|RF_SLOPESPLAT|RF_NOSPLATROLLANGLE;
}
void Tick(boolean active)
{
if (active == true)
{
Anim(S_BALLSWITCH_PAD_ACTIVE);
}
else
{
Anim(S_BALLSWITCH_PAD);
}
}
};
struct BallSwitch_Ball : mobj_t
{
BallSwitch_Pad *Pad() const { return static_cast<BallSwitch_Pad *>( ball_pad(this) ); }
void Pad(BallSwitch_Pad *n) { P_SetTarget(&ball_pad(this), n); }
statenum_t Anim() const { return static_cast<statenum_t>(this->state - states); }
void Anim(statenum_t n)
{
if (Anim() != n)
{
P_SetMobjState(this, n);
}
}
INT32 Cooldown() const { return ball_cooldown(this); }
void Cooldown(INT32 n) { ball_cooldown(this) = n; }
boolean Active() const { return (ball_cooldown(this) != 0); }
boolean DeferActivation() const { return ball_activedefer(this); }
mobj_t *Activator() const { return ball_activator(this); }
void DeferActivation(boolean n, mobj_t *src)
{
ball_activedefer(this) = n;
P_SetTarget(&ball_activator(this), src);
}
SINT8 IntSign(int value) const
{
if (value > 0)
{
return 1;
}
if (value < 0)
{
return -1;
}
return 0;
}
void Spawned()
{
Pad( static_cast<BallSwitch_Pad *>( P_SpawnMobjFromMobj(this, 0, 0, 0, MT_BALLSWITCH_PAD) ) );
Pad()->Spawned();
this->z += Pad()->height * P_MobjFlip(this);
}
void Tick()
{
if (P_MobjWasRemoved(Pad()) == true)
{
P_RemoveMobj(this);
return;
}
ball_instawhipped(this) = 0;
if (DeferActivation() == true)
{
P_ActivateThingSpecial(this, Activator());
Cooldown(-1); // maybe later?
DeferActivation(false, nullptr);
}
fixed_t ourZ = P_GetMobjFeet(this);
fixed_t theirZ = P_GetMobjHead(Pad());
fixed_t dist = P_AproxDistance(P_AproxDistance(Pad()->x - this->x, Pad()->y - this->y), theirZ - ourZ);
fixed_t move = P_AproxDistance(P_AproxDistance(this->momx, this->momy), this->momz);
constexpr INT32 accelScale = 4;
if (dist < accelScale * this->scale && move < accelScale * this->scale)
{
P_SetOrigin(this, Pad()->x, Pad()->y, theirZ);
this->momx = this->momy = this->momz = 0;
}
else
{
static constexpr const INT32 accel[2] = { FRACUNIT*3/4, FRACUNIT*3/16 };
constexpr fixed_t frict = FRACUNIT*99/100;
this->momx = FixedMul(this->momx, frict);
this->momy = FixedMul(this->momy, frict);
this->momz = FixedMul(this->momz, frict);
SINT8 xSign = IntSign(Pad()->x - this->x);
SINT8 ySign = IntSign(Pad()->y - this->y);
SINT8 zSign = IntSign(theirZ - ourZ);
boolean xAway = (IntSign(this->momx) == xSign);
boolean yAway = (IntSign(this->momy) == ySign);
boolean zAway = (IntSign(this->momz) == zSign);
this->momx += FixedMul(accel[xAway], accelScale * this->scale) * xSign;
this->momy += FixedMul(accel[yAway], accelScale * this->scale) * ySign;
this->momz += FixedMul(accel[zAway], accelScale * this->scale) * zSign;
this->angle += FixedAngle(move * 2);
if (dist > this->radius * 2)
{
P_Thrust(this, this->angle, (move / accelScale) * 2 / 3);
}
}
if (Active() == true)
{
INT32 cool = Cooldown();
if (cool > 0)
{
Cooldown(cool - 1);
}
Anim(S_BALLSWITCH_BALL_ACTIVE);
}
else
{
Anim(S_BALLSWITCH_BALL);
}
Pad()->Tick(Active());
}
void Push(mobj_t *toucher, const fixed_t pushValue, const fixed_t repelValue)
{
fixed_t push = FixedMul(pushValue, toucher->scale);
fixed_t repel = FixedMul(repelValue, this->scale);
angle_t thrustAngle = R_PointToAngle2(toucher->x, toucher->y, this->x, this->y);
fixed_t thrustAngleCos = FINECOSINE(thrustAngle >> ANGLETOFINESHIFT);
fixed_t thrustAngleSin = FINESINE(thrustAngle >> ANGLETOFINESHIFT);
fixed_t thisZ = this->z + (this->height / 2);
fixed_t toucherZ = toucher->z + (toucher->height / 2);
angle_t thrustPitch = R_PointToAngle2(0, toucherZ, R_PointToDist2(toucher->x, toucher->y, this->x, this->y), thisZ);
fixed_t thrustPitchCos = FINECOSINE(thrustPitch >> ANGLETOFINESHIFT);
fixed_t thrustPitchSin = FINESINE(thrustPitch >> ANGLETOFINESHIFT);
this->momx += FixedMul(FixedMul(push, thrustAngleCos), thrustPitchCos);
this->momy += FixedMul(FixedMul(push, thrustAngleSin), thrustPitchCos);
this->momz += FixedMul(push, thrustPitchSin);
toucher->momx -= FixedMul(FixedMul(repel, thrustAngleCos), thrustPitchCos);
toucher->momy -= FixedMul(FixedMul(repel, thrustAngleSin), thrustPitchCos);
toucher->momz -= FixedMul(repel, thrustPitchSin);
}
void Touch(mobj_t *toucher)
{
Push(toucher, 4 << FRACBITS, 6 << FRACBITS);
}
void Hit(mobj_t *inflictor, mobj_t *source)
{
Push(inflictor, 64 << FRACBITS, 1 << FRACBITS);
K_SetHitLagForObjects(this, inflictor, source, 4, true);
DeferActivation(true, source);
}
};
}; // namespace
void Obj_BallSwitchInit(mobj_t *mobj)
{
BallSwitch_Ball *ball = static_cast<BallSwitch_Ball *>(mobj);
ball->Spawned();
}
void Obj_BallSwitchThink(mobj_t *mobj)
{
BallSwitch_Ball *ball = static_cast<BallSwitch_Ball *>(mobj);
ball->Tick();
}
void Obj_BallSwitchTouched(mobj_t *mobj, mobj_t *toucher)
{
BallSwitch_Ball *ball = static_cast<BallSwitch_Ball *>(mobj);
ball->Touch(toucher);
}
void Obj_BallSwitchDamaged(mobj_t *mobj, mobj_t *inflictor, mobj_t *source)
{
BallSwitch_Ball *ball = static_cast<BallSwitch_Ball *>(mobj);
ball->Hit(inflictor, source);
}

View file

@ -817,6 +817,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
Obj_DashRingTouch(special, player);
return;
case MT_BALLSWITCH_BALL:
{
Obj_BallSwitchTouched(special, toucher);
return;
}
default: // SOC or script pickup
P_SetTarget(&special->target, toucher);
break;
@ -2398,6 +2404,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false;
}
if (target->type == MT_BALLSWITCH_BALL)
{
Obj_BallSwitchDamaged(target, inflictor, source);
return false;
}
if (!force)
{
if (!spbpop)

View file

@ -9703,6 +9703,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
case MT_RAINBOWDASHRING:
Obj_RainbowDashRingThink(mobj);
break;
case MT_BALLSWITCH_BALL:
{
Obj_BallSwitchThink(mobj);
break;
}
default:
// check mobj against possible water content, before movement code
P_MobjCheckWater(mobj);
@ -11142,6 +11147,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
case MT_SNEAKERPANELSPAWNER:
Obj_SneakerPanelSpawnerSpawn(mobj);
break;
case MT_BALLSWITCH_BALL:
Obj_BallSwitchInit(mobj);
break;
default:
break;
}