PITY IN PINK!

* Smoothen Pity Shield animation to go with sphere's updates to Nev3r's sprites.
* Added LHRT object, designed to be summoned with CA2_MELEE.
    * Gives a pink Pity Shield (SH_PINK) on same-team player contact.
    * Deals damage to non-player enemies.
    * Harmlessly fades into nothing when touching an enemy player, players with SH_PINK already, and players capable of applying SH_PINK to others (through non-Lua methods).
* Basically, you-know-who is the Healer of the party whenever they're around. Fun consequences for the Co-op and CTF metas.
This commit is contained in:
toaster 2019-06-19 23:20:24 +01:00
parent 6a58ae34d1
commit 2e6898f29e
10 changed files with 170 additions and 18 deletions

View File

@ -196,6 +196,7 @@ typedef enum
SH_PITY = 1, // the world's most basic shield ever, given to players who suck at Match
SH_WHIRLWIND,
SH_ARMAGEDDON,
SH_PINK, // PITY IN PINK!
// Normal shields that use flags
SH_ATTRACT = SH_PITY|SH_PROTECTELECTRIC,

View File

@ -6027,6 +6027,12 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_PITY4",
"S_PITY5",
"S_PITY6",
"S_PITY7",
"S_PITY8",
"S_PITY9",
"S_PITY10",
"S_PITY11",
"S_PITY12",
"S_FIRS1",
"S_FIRS2",
@ -6519,6 +6525,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_GOTFLAG",
"S_CORK",
"S_LHRT",
// Red Ring
"S_RRNG1",
@ -7579,6 +7586,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_MACHINEAMBIENCE",
"MT_CORK",
"MT_LHRT",
// Ring Weapons
"MT_REDRING",
@ -8342,6 +8350,7 @@ struct {
{"SH_PITY",SH_PITY},
{"SH_WHIRLWIND",SH_WHIRLWIND},
{"SH_ARMAGEDDON",SH_ARMAGEDDON},
{"SH_PINK",SH_PINK},
// normal shields that use flags
{"SH_ATTRACT",SH_ATTRACT},
{"SH_ELEMENTAL",SH_ELEMENTAL},

View File

@ -469,6 +469,7 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_GFLG
&lspr[NOLIGHT], // SPR_CORK
&lspr[NOLIGHT], // SPR_LHRT
// Ring Weapons
&lspr[RINGLIGHT_L], // SPR_RRNG

View File

@ -364,6 +364,7 @@ char sprnames[NUMSPRITES + 1][5] =
"GFLG", // Got Flag sign
"CORK",
"LHRT",
// Ring Weapons
"RRNG", // Red Ring
@ -2664,12 +2665,18 @@ state_t states[NUMSTATES] =
{SPR_ELEM, FF_FULLBRIGHT|20, 1, {NULL}, 0, 0, S_ELEMF10}, // S_ELEMF9
{SPR_NULL, 0, 1, {NULL}, 0, 0, S_ELEMF1 }, // S_ELEMF10
{SPR_PITY, FF_TRANS30 , 2, {NULL}, 0, 0, S_PITY2}, // S_PITY1
{SPR_PITY, FF_TRANS30|1, 2, {NULL}, 0, 0, S_PITY3}, // S_PITY2
{SPR_PITY, FF_TRANS30|2, 2, {NULL}, 0, 0, S_PITY4}, // S_PITY3
{SPR_PITY, FF_TRANS20|3, 2, {NULL}, 0, 0, S_PITY5}, // S_PITY4
{SPR_PITY, FF_TRANS30|4, 2, {NULL}, 0, 0, S_PITY6}, // S_PITY5
{SPR_PITY, FF_TRANS20|5, 2, {NULL}, 0, 0, S_PITY1}, // S_PITY6
{SPR_PITY, FF_TRANS30 , 2, {NULL}, 0, 0, S_PITY2}, // S_PITY1
{SPR_PITY, FF_TRANS30| 1, 2, {NULL}, 0, 0, S_PITY3}, // S_PITY2
{SPR_PITY, FF_TRANS30| 2, 2, {NULL}, 0, 0, S_PITY4}, // S_PITY3
{SPR_PITY, FF_TRANS30| 3, 2, {NULL}, 0, 0, S_PITY5}, // S_PITY4
{SPR_PITY, FF_TRANS30| 4, 2, {NULL}, 0, 0, S_PITY6}, // S_PITY5
{SPR_PITY, FF_TRANS30| 5, 2, {NULL}, 0, 0, S_PITY7}, // S_PITY6
{SPR_PITY, FF_TRANS30| 6, 2, {NULL}, 0, 0, S_PITY8}, // S_PITY7
{SPR_PITY, FF_TRANS30| 7, 2, {NULL}, 0, 0, S_PITY9}, // S_PITY8
{SPR_PITY, FF_TRANS30| 8, 2, {NULL}, 0, 0, S_PITY10}, // S_PITY9
{SPR_PITY, FF_TRANS30| 9, 2, {NULL}, 0, 0, S_PITY11}, // S_PITY10
{SPR_PITY, FF_TRANS30|10, 2, {NULL}, 0, 0, S_PITY12}, // S_PITY11
{SPR_PITY, FF_TRANS30|11, 2, {NULL}, 0, 0, S_PITY1}, // S_PITY12
{SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40 , 2, {NULL}, 0, 0, S_FIRS2}, // S_FIRS1
{SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|1, 2, {NULL}, 0, 0, S_FIRS3}, // S_FIRS2
@ -3165,7 +3172,8 @@ state_t states[NUMSTATES] =
// CTF Sign
{SPR_GFLG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTFLAG
{SPR_CORK, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CORK
{SPR_CORK, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CORK
{SPR_LHRT, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_LHRT
// Red Rings (thrown)
{SPR_RRNG, FF_FULLBRIGHT, 1, {A_ThrownRing}, 0, 0, S_RRNG2}, // S_RRNG1
@ -16099,6 +16107,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_LHRT
-1, // doomednum
S_LHRT, // 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_SPRK1, // deathstate
S_SPRK1, // xdeathstate
sfx_None, // deathsound
60*FRACUNIT, // speed
16*FRACUNIT, // radius
16*FRACUNIT, // height
0, // display offset
0, // mass
1, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_MISSILE, // flags
S_NULL // raisestate
},
{ // MT_REDRING
-1, // doomednum
S_RRNG1, // spawnstate

View File

@ -595,6 +595,7 @@ typedef enum sprite
SPR_GFLG, // Got Flag sign
SPR_CORK,
SPR_LHRT,
// Ring Weapons
SPR_RRNG, // Red Ring
@ -2771,6 +2772,12 @@ typedef enum state
S_PITY4,
S_PITY5,
S_PITY6,
S_PITY7,
S_PITY8,
S_PITY9,
S_PITY10,
S_PITY11,
S_PITY12,
S_FIRS1,
S_FIRS2,
@ -3263,6 +3270,7 @@ typedef enum state
S_GOTFLAG,
S_CORK,
S_LHRT,
// Red Ring
S_RRNG1,
@ -4343,6 +4351,7 @@ typedef enum mobj_type
MT_MACHINEAMBIENCE,
MT_CORK,
MT_LHRT,
// Ring Weapons
MT_REDRING,

View File

@ -2813,26 +2813,47 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
if (player->powers[pw_flashing] || player->powers[pw_invulnerability])
return false;
// Ignore IT players shooting each other, unless friendlyfire is on.
if ((player->pflags & PF_TAGIT && !((cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) &&
source && source->player && source->player->pflags & PF_TAGIT)))
return false;
// Don't allow any damage before the round starts.
if (leveltime <= hidetime * TICRATE)
return false;
// Ignore IT players shooting each other, unless friendlyfire is on.
if ((player->pflags & PF_TAGIT && !((cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) &&
source && source->player && source->player->pflags & PF_TAGIT)))
{
if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
{
if (player->spinitem != MT_LHRT && player->revitem != MT_LHRT && player->thokitem != MT_LHRT) // Healers do not get to heal other healers.
{
P_SwitchShield(player, SH_PINK);
S_StartSound(target, mobjinfo[MT_PITY_ICON].seesound);
}
}
return false;
}
// Don't allow players on the same team to hurt one another,
// unless cv_friendlyfire is on.
if (!(cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) && (player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT))
{
if (!(inflictor->flags & MF_FIRE))
if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
{
if (player->spinitem != MT_LHRT && player->revitem != MT_LHRT && player->thokitem != MT_LHRT) // Healers do not get to heal other healers.
{
P_SwitchShield(player, SH_PINK);
S_StartSound(target, mobjinfo[MT_PITY_ICON].seesound);
}
}
else if (!(inflictor->flags & MF_FIRE))
P_GivePlayerRings(player, 1);
if (inflictor->flags2 & MF2_BOUNCERING)
inflictor->fuse = 0; // bounce ring disappears at -1 not 0
return false;
}
if (inflictor->type == MT_LHRT)
return false;
// The tag occurs so long as you aren't shooting another tagger with friendlyfire on.
if (source->player->pflags & PF_TAGIT && !(player->pflags & PF_TAGIT))
{
@ -2899,7 +2920,17 @@ static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj
// In COOP/RACE, you can't hurt other players unless cv_friendlyfire is on
if (!cv_friendlyfire.value && (G_PlatformGametype()))
{
if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
{
if (player->spinitem != MT_LHRT && player->revitem != MT_LHRT && player->thokitem != MT_LHRT) // Healers do not get to heal other healers.
{
P_SwitchShield(player, SH_PINK);
S_StartSound(target, mobjinfo[MT_PITY_ICON].seesound);
}
}
return false;
}
}
// Tag handling
@ -2913,7 +2944,15 @@ static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj
// unless cv_friendlyfire is on.
if (!cv_friendlyfire.value && target->player->ctfteam == source->player->ctfteam)
{
if (!(inflictor->flags & MF_FIRE))
if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
{
if (player->spinitem != MT_LHRT && player->revitem != MT_LHRT && player->thokitem != MT_LHRT) // Healers do not get to heal other healers.
{
P_SwitchShield(player, SH_PINK);
S_StartSound(target, mobjinfo[MT_PITY_ICON].seesound);
}
}
else if (!(inflictor->flags & MF_FIRE))
P_GivePlayerRings(target->player, 1);
if (inflictor->flags2 & MF2_BOUNCERING)
inflictor->fuse = 0; // bounce ring disappears at -1 not 0
@ -2922,6 +2961,9 @@ static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj
}
}
if (inflictor->type == MT_LHRT)
return false;
// Add pity.
if (!player->powers[pw_flashing] && !player->powers[pw_invulnerability] && !player->powers[pw_super]
&& source->player->score > player->score)

View File

@ -1076,7 +1076,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmthing->y = thing->y;
P_SetThingPosition(tmthing);
}
else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial
else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial for shell
{
UINT8 damagetype = tmthing->info->mass;
if (!damagetype && tmthing->flags & MF_FIRE) // BURN!

View File

@ -7861,6 +7861,10 @@ void P_MobjThinker(mobj_t *mobj)
}
}
break;
case MT_LHRT:
mobj->momx = FixedMul(mobj->momx, (48*FRACUNIT)/50);
mobj->momy = FixedMul(mobj->momy, (48*FRACUNIT)/50);
break;
case MT_EGGCAPSULE:
if (!mobj->reactiontime)
{
@ -8547,6 +8551,9 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE:
P_SetMobjState(mobj, mobj->info->deathstate);
break;
case MT_LHRT:
P_KillMobj(mobj, NULL, NULL, 0);
break;
case MT_BLUEFLAG:
case MT_REDFLAG:
if (mobj->spawnpoint)

View File

@ -1609,6 +1609,7 @@ void P_SpawnShieldOrb(player_t *player)
orbtype = MT_ARMAGEDDON_ORB;
break;
case SH_PITY:
case SH_PINK: // PITY IN PINK
orbtype = MT_PITY_ORB;
break;
case SH_FLAMEAURA:
@ -1639,7 +1640,13 @@ void P_SpawnShieldOrb(player_t *player)
shieldobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, orbtype);
shieldobj->flags2 |= MF2_SHIELD;
P_SetTarget(&shieldobj->target, player->mo);
shieldobj->color = (UINT8)shieldobj->info->painchance;
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK)
{
shieldobj->color = SKINCOLOR_PINK;
shieldobj->colorized = true;
}
else
shieldobj->color = (UINT8)shieldobj->info->painchance;
shieldobj->threshold = (player->powers[pw_shield] & SH_FORCE) ? SH_FORCE : (player->powers[pw_shield] & SH_NOSTACK);
if (shieldobj->info->seestate)
@ -1787,6 +1794,9 @@ void P_SpawnThokMobj(player_t *player)
if (player->spectator)
return;
if (!type)
return;
if (type == MT_GHOST)
mobj = P_SpawnGhostMobj(player->mo); // virtually does everything here for us
else
@ -1847,6 +1857,9 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type)
if (player->spectator)
return;
if (!type)
return;
if (type == MT_GHOST)
mobj = P_SpawnGhostMobj(player->mo); // virtually does everything here for us
else
@ -2002,10 +2015,42 @@ boolean P_PlayerHitFloor(player_t *player)
}
else if (player->charability2 == CA2_MELEE && (player->panim == PA_ABILITY2 && player->mo->state-states != S_PLAY_MELEE_LANDING))
{
mobjtype_t type = player->spinitem;
P_SetPlayerMobjState(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);
player->pflags |= PF_FULLSTASIS;
// hearticles
if (type)
{
UINT8 i = 0;
angle_t throwang = -(2*ANG30);
fixed_t xo = P_ReturnThrustX(player->mo, player->drawangle, 16*player->mo->scale);
fixed_t yo = P_ReturnThrustY(player->mo, player->drawangle, 16*player->mo->scale);
fixed_t zo = 6*player->mo->scale;
fixed_t mu = FixedMul(player->maxdash, player->mo->scale);
fixed_t mu2 = FixedHypot(player->mo->momx, player->mo->momy);
mobj_t *missile;
if (mu2 < mu)
mu2 = mu;
while (i < 5)
{
missile = P_SpawnMobjFromMobj(player->mo, xo, yo, zo, type);
P_SetTarget(&missile->target, player->mo);
missile->angle = throwang + player->drawangle;
P_Thrust(missile, player->drawangle + ANGLE_90,
P_ReturnThrustY(missile, throwang, mu)); // side to side component
P_Thrust(missile, player->drawangle, mu2); // forward component
P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true);
missile->fuse = TICRATE/2;
i++;
throwang += ANG30;
}
if (mobjinfo[type].seesound)
S_StartSound(missile, missile->info->seesound);
}
}
else if (player->pflags & PF_JUMPED || !(player->pflags & PF_SPINNING)
|| player->powers[pw_tailsfly] || player->mo->state-states == S_PLAY_FLY_TIRED)
@ -9806,12 +9851,12 @@ void P_DoPityCheck(player_t *player)
// Apply pity shield if available.
if ((player->pity >= 3 || player->pity < 0) && player->powers[pw_shield] == SH_NONE)
{
P_SwitchShield(player, SH_PITY);
if (player->pity > 0)
S_StartSound(player->mo, mobjinfo[MT_PITY_ICON].seesound);
player->pity = 0;
player->powers[pw_shield] = SH_PITY;
P_SpawnShieldOrb(player);
}
}

View File

@ -2704,6 +2704,9 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
player->revitem = skin->revitem < 0 ? (mobjtype_t)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem;
player->followitem = skin->followitem;
if (((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK) && (player->spinitem == MT_LHRT)) // Healers can't keep their buff.
player->powers[pw_shield] &= SH_STACK;
player->actionspd = skin->actionspd;
player->mindash = skin->mindash;
player->maxdash = skin->maxdash;