Add guarding while ebraking with spheres

This commit is contained in:
AJ Martinez 2023-05-19 01:53:14 -07:00
parent 1b16b3f777
commit dadaab1817
12 changed files with 251 additions and 27 deletions

View file

@ -3288,6 +3288,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_SLIPTIDEZIP", "S_SLIPTIDEZIP",
"S_INSTAWHIP", "S_INSTAWHIP",
"S_BLOCKRING",
"S_BLOCKBODY",
// Signpost sparkles // Signpost sparkles
"S_SIGNSPARK1", "S_SIGNSPARK1",
@ -5321,6 +5323,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_SLIPTIDEZIP", "MT_SLIPTIDEZIP",
"MT_INSTAWHIP", "MT_INSTAWHIP",
"MT_BLOCKRING",
"MT_BLOCKBODY",
"MT_SIGNSPARKLE", "MT_SIGNSPARKLE",

View file

@ -556,6 +556,8 @@ char sprnames[NUMSPRITES + 1][5] =
"SLPT", // Sliptide zip indicator "SLPT", // Sliptide zip indicator
"IWHP", // Instawhip "IWHP", // Instawhip
"GRNG", // Guard ring
"GBDY", // Guard body
"WIPD", // Wipeout dust trail "WIPD", // Wipeout dust trail
"DRIF", // Drift Sparks "DRIF", // Drift Sparks
@ -3949,6 +3951,8 @@ state_t states[NUMSTATES] =
{SPR_SLPT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SLIPTIDEZIP {SPR_SLPT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SLIPTIDEZIP
{SPR_IWHP, FF_FLOORSPRITE|FF_ANIMATE|0, -1, {NULL}, 6, 2, S_NULL}, // S_INSTAWHIP {SPR_IWHP, FF_FLOORSPRITE|FF_ANIMATE|0, -1, {NULL}, 6, 2, S_NULL}, // S_INSTAWHIP
{SPR_GRNG, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_BLOCKRING
{SPR_GBDY, FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_BLOCKBODY
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_SIGNSPARK2}, // S_SIGNSPARK1 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_SIGNSPARK2}, // S_SIGNSPARK1
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_SIGNSPARK3}, // S_SIGNSPARK2 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_SIGNSPARK3}, // S_SIGNSPARK2
@ -22663,6 +22667,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_BLOCKRING
-1, // doomednum
S_BLOCKRING, // 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
67*FRACUNIT, // radius
67*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
{ // MT_BLOCKBODY
-1, // doomednum
S_BLOCKBODY, // 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
67*FRACUNIT, // radius
67*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
{ // MT_SIGNSPARKLE { // MT_SIGNSPARKLE
-1, // doomednum -1, // doomednum

View file

@ -1109,6 +1109,8 @@ typedef enum sprite
SPR_SLPT, // Sliptide zip indicator SPR_SLPT, // Sliptide zip indicator
SPR_IWHP, // Instawhip SPR_IWHP, // Instawhip
SPR_GRNG, // Guard ring
SPR_GBDY, // Guard body
SPR_WIPD, // Wipeout dust trail SPR_WIPD, // Wipeout dust trail
SPR_DRIF, // Drift Sparks SPR_DRIF, // Drift Sparks
@ -4360,6 +4362,8 @@ typedef enum state
S_SLIPTIDEZIP, S_SLIPTIDEZIP,
S_INSTAWHIP, S_INSTAWHIP,
S_BLOCKRING,
S_BLOCKBODY,
// Signpost sparkles // Signpost sparkles
S_SIGNSPARK1, S_SIGNSPARK1,
@ -6428,6 +6432,8 @@ typedef enum mobj_type
MT_SLIPTIDEZIP, MT_SLIPTIDEZIP,
MT_INSTAWHIP, MT_INSTAWHIP,
MT_BLOCKRING,
MT_BLOCKBODY,
MT_SIGNSPARKLE, MT_SIGNSPARKLE,

View file

@ -787,48 +787,96 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
return true; return true;
} }
boolean K_InstaWhipCollide(mobj_t *t1, mobj_t *t2) boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
{ {
const int defenderHitlag = 10; const int victimHitlag = 10;
const int attackerHitlag = 4; const int attackerHitlag = 4;
if (t2->player) // EV1 is used to indicate that we should no longer hit monitors.
// EV2 indicates we should no longer hit anything.
if (shield->extravalue2)
return false;
mobj_t *attacker = shield->target;
if (!attacker || P_MobjWasRemoved(attacker) || !attacker->player)
return false; // How did we even get here?
player_t *attackerPlayer = attacker->player;
if (victim->player)
{ {
if (t2 != t1->target && !P_PlayerInPain(t2->player) && t2->player->flashing == 0) player_t *victimPlayer = victim->player;
if (victim != attacker && !P_PlayerInPain(victimPlayer) && victimPlayer->flashing == 0)
{ {
if (K_PlayerEBrake(victimPlayer) && victimPlayer->spheres > 0)
{
if (P_PlayerInPain(attackerPlayer))
return false; // never punish shield more than once
angle_t thrangle = R_PointToAngle2(victim->x, victim->y, shield->x, shield->y);
attacker->momx = attacker->momy = 0;
P_Thrust(attacker, thrangle, FRACUNIT*7);
victimPlayer->spheres = std::min(victimPlayer->spheres + 10, 40);
shield->renderflags &= ~RF_DONTDRAW;
shield->flags |= MF_NOCLIPTHING;
attacker->renderflags &= ~RF_DONTDRAW;
attackerPlayer->instaShieldCooldown = TICRATE*2;
attackerPlayer->flashing = 0;
P_DamageMobj(attacker, victim, victim, 1, DMG_STING);
S_StartSound(victim, sfx_mbv92);
K_AddHitLag(attacker, 2*victimHitlag, true);
K_AddHitLag(victim, attackerHitlag, false);
attacker->hitlag = std::min(attacker->hitlag, 2*victimHitlag);
shield->hitlag = attacker->hitlag;
shield->extravalue2 = 1;
return true;
}
// Damage is a bit hacky, we want only a small loss-of-control // Damage is a bit hacky, we want only a small loss-of-control
// while still behaving as if it's a "real" hit. // while still behaving as if it's a "real" hit.
P_PlayRinglossSound(t2); P_PlayRinglossSound(victim);
P_PlayerRingBurst(t2->player, 5); P_PlayerRingBurst(victimPlayer, 5);
P_DamageMobj(t2, t1, t1->target, 1, DMG_STUMBLE); P_DamageMobj(victim, shield, attacker, 1, DMG_STUMBLE); // There's a pecial exception in P_DamageMobj for type==MT_INSTAWHIP
K_AddHitLag(t2, defenderHitlag, true); angle_t thrangle = ANGLE_180 + R_PointToAngle2(victim->x, victim->y, shield->x, shield->y);
K_AddHitLag(t1->target, attackerHitlag, false); P_Thrust(victim, thrangle, FRACUNIT*10);
t1->hitlag = t1->target->hitlag;
K_AddHitLag(victim, victimHitlag, true);
K_AddHitLag(attacker, attackerHitlag, false);
shield->hitlag = attacker->hitlag;
return true; return true;
} }
return false; return false;
} }
else else
{ {
if (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ || t2->type == MT_GACHABOM if (victim->type == MT_ORBINAUT || victim->type == MT_JAWZ || victim->type == MT_GACHABOM
|| t2->type == MT_BANANA || t2->type == MT_EGGMANITEM || t2->type == MT_BALLHOG || victim->type == MT_BANANA || victim->type == MT_EGGMANITEM || victim->type == MT_BALLHOG
|| t2->type == MT_SSMINE || t2->type == MT_LANDMINE || t2->type == MT_SINK || victim->type == MT_SSMINE || victim->type == MT_LANDMINE || victim->type == MT_SINK
|| t2->type == MT_GARDENTOP || t2->type == MT_DROPTARGET || t2->type == MT_BATTLECAPSULE || victim->type == MT_GARDENTOP || victim->type == MT_DROPTARGET || victim->type == MT_BATTLECAPSULE
|| t2->type == MT_MONITOR) || victim->type == MT_MONITOR)
{ {
// Monitor hack. We can hit monitors once per instawhip, no multihit shredding! // Monitor hack. We can hit monitors once per instawhip, no multihit shredding!
// Damage values in Obj_MonitorGetDamage. // Damage values in Obj_MonitorGetDamage.
if (t2->type == MT_MONITOR) if (victim->type == MT_MONITOR)
{ {
if (t1->extravalue1 == 1) if (shield->extravalue1 == 1)
return false; return false;
t1->extravalue1 = 1; shield->extravalue1 = 1;
} }
P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL); P_DamageMobj(victim, shield, attacker, 1, DMG_NORMAL);
K_AddHitLag(t1->target, attackerHitlag, false); K_AddHitLag(attacker, attackerHitlag, false);
t1->hitlag = t1->target->hitlag; shield = attacker;
} }
return false; return false;
} }
@ -1022,7 +1070,7 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
bool stung = false; bool stung = false;
if (t2->player->rings <= 0) if (t2->player->rings <= 0 && t2->player->spheres <= 0)
{ {
P_DamageMobj(t2, t1, t1, 1, DMG_STING|DMG_WOMBO); P_DamageMobj(t2, t1, t1, 1, DMG_STING|DMG_WOMBO);
stung = true; stung = true;

View file

@ -24,7 +24,7 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2);
void K_LightningShieldAttack(mobj_t *actor, fixed_t size); void K_LightningShieldAttack(mobj_t *actor, fixed_t size);
boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2); boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2);
boolean K_InstaWhipCollide(mobj_t *t1, mobj_t *t2); boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim);
boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2); boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2);

View file

@ -7777,7 +7777,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
// where's the < 0 check? see below the following block! // where's the < 0 check? see below the following block!
{ {
tic_t spheredigestion = TICRATE; // Base rate of 1 every second when playing. tic_t spheredigestion = TICRATE*2; // Base rate of 1 every second when playing.
tic_t digestionpower = ((10 - player->kartspeed) + (10 - player->kartweight))-1; // 1 to 17 tic_t digestionpower = ((10 - player->kartspeed) + (10 - player->kartweight))-1; // 1 to 17
// currently 0-34 // currently 0-34
@ -7789,7 +7789,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
} }
else else
{ {
spheredigestion -= digestionpower; spheredigestion -= digestionpower/2;
} }
if ((player->spheres > 0) && (player->spheredigestion > 0)) if ((player->spheres > 0) && (player->spheredigestion > 0))
@ -7807,6 +7807,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->spheres--; player->spheres--;
player->spheredigestion = spheredigestion; player->spheredigestion = spheredigestion;
} }
if (K_PlayerEBrake(player) && (player->ebrakefor%6 == 0))
player->spheres--;
} }
else else
{ {
@ -9833,6 +9836,23 @@ void K_KartEbrakeVisuals(player_t *p)
if (!S_SoundPlaying(p->mo, sfx_s3kd9s)) if (!S_SoundPlaying(p->mo, sfx_s3kd9s))
S_ReducedVFXSound(p->mo, sfx_s3kd9s, p); S_ReducedVFXSound(p->mo, sfx_s3kd9s, p);
// Block visuals
// (These objects track whether a player is block-eligible on their own, no worries)
if (!p->ebrakefor)
{
mobj_t *ring = P_SpawnMobj(p->mo->x, p->mo->y, p->mo->z, MT_BLOCKRING);
P_SetTarget(&ring->target, p->mo);
P_SetScale(ring, p->mo->scale);
K_MatchGenericExtraFlags(ring, p->mo);
ring->renderflags &= ~RF_DONTDRAW;
mobj_t *body = P_SpawnMobj(p->mo->x, p->mo->y, p->mo->z, MT_BLOCKBODY);
P_SetTarget(&body->target, p->mo);
P_SetScale(body, p->mo->scale);
K_MatchGenericExtraFlags(body, p->mo);
body->renderflags |= RF_DONTDRAW;
}
// HOLD! bubble. // HOLD! bubble.
if (!p->ebrakefor) if (!p->ebrakefor)
{ {

View file

@ -110,6 +110,10 @@ boolean Obj_DropTargetMorphThink(mobj_t *morph);
/* Instawhip */ /* Instawhip */
void Obj_InstaWhipThink(mobj_t *whip); void Obj_InstaWhipThink(mobj_t *whip);
/* Block VFX */
void Obj_BlockRingThink(mobj_t *ring);
void Obj_BlockBodyThink(mobj_t *body);
/* Ring Shooter */ /* Ring Shooter */
boolean Obj_RingShooterThinker(mobj_t *mo); boolean Obj_RingShooterThinker(mobj_t *mo);
boolean Obj_PlayerRingShooterFreeze(player_t *const player); boolean Obj_PlayerRingShooterFreeze(player_t *const player);

View file

@ -18,4 +18,5 @@ target_sources(SRB2SDL2 PRIVATE
audience.c audience.c
random-item.c random-item.c
instawhip.c instawhip.c
block.c
) )

68
src/objects/block.c Normal file
View file

@ -0,0 +1,68 @@
#include "../doomdef.h"
#include "../info.h"
#include "../k_objects.h"
#include "../p_local.h"
void Obj_BlockRingThink (mobj_t *ring)
{
if (P_MobjWasRemoved(ring->target) || !ring->target->player || !ring->target->player->ebrakefor)
{
P_RemoveMobj(ring);
}
else
{
mobj_t *mo = ring->target;
player_t *player = mo->player;
// Follow player
ring->flags &= ~(MF_NOCLIPTHING);
P_SetScale(ring, mo->scale);
P_MoveOrigin(ring, mo->x, mo->y, mo->z + mo->height/2);
ring->flags |= MF_NOCLIPTHING;
ring->color = mo->color;
// Twirl
ring->angle = ring->target->angle + (ANG15 * leveltime);
// Visuals
ring->renderflags |= RF_ADD|RF_PAPERSPRITE;
if (leveltime%2)
ring->renderflags &= ~RF_DONTDRAW;
else
ring->renderflags |= RF_DONTDRAW;
if (player->spheres == 0)
ring->renderflags |= RF_DONTDRAW;
}
}
void Obj_BlockBodyThink (mobj_t *body)
{
if (P_MobjWasRemoved(body->target) || !body->target->player || !body->target->player->ebrakefor)
{
P_RemoveMobj(body);
}
else
{
mobj_t *mo = body->target;
player_t *player = mo->player;
// Follow player
body->flags &= ~(MF_NOCLIPTHING);
P_SetScale(body, mo->scale);
P_MoveOrigin(body, mo->x, mo->y, mo->z + mo->height/2);
body->flags |= MF_NOCLIPTHING;
body->color = mo->color;
// Visuals
body->renderflags |= RF_ADD;
if (leveltime%2 == 0)
body->renderflags &= ~RF_DONTDRAW;
else
body->renderflags |= RF_DONTDRAW;
if (player->spheres == 0)
body->renderflags |= RF_DONTDRAW;
}
}

View file

@ -2208,6 +2208,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
{ {
sfx = sfx_grownd; sfx = sfx_grownd;
} }
else if (player->spheres > 0 && K_PlayerEBrake(player))
{
sfx = sfx_s3k3a;
player->spheres = max(player->spheres - 10, 0);
}
else if (player->hyudorotimer > 0) else if (player->hyudorotimer > 0)
; ;
else else
@ -2308,7 +2313,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// Instawhip breaks the rules and does "damaging stumble", // Instawhip breaks the rules and does "damaging stumble",
// but sting and stumble shouldn't be rewarding Battle hits otherwise. // but sting and stumble shouldn't be rewarding Battle hits otherwise.
if ((type == DMG_STING || type == DMG_STUMBLE) && inflictor->type != MT_INSTAWHIP ) if ((type == DMG_STING || type == DMG_STUMBLE) && (inflictor && inflictor->type != MT_INSTAWHIP))
{ {
damage = 0; damage = 0;
} }

View file

@ -8361,6 +8361,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
Obj_InstaWhipThink(mobj); Obj_InstaWhipThink(mobj);
break; break;
} }
case MT_BLOCKRING:
{
Obj_BlockRingThink(mobj);
break;
}
case MT_BLOCKBODY:
{
Obj_BlockBodyThink(mobj);
break;
}
case MT_GARDENTOPSPARK: case MT_GARDENTOPSPARK:
{ {
Obj_GardenTopSparkThink(mobj); Obj_GardenTopSparkThink(mobj);

View file

@ -901,7 +901,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"mbv8f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"mbv8f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbv90", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"mbv90", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbv91", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"mbv91", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbv92", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"mbv92", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Changed falloff for use in instashield parry.
{"mbv93", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"mbv93", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbv94", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"mbv94", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"mbv95", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"mbv95", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},