From a4584906399eaee58314e94fd25b6bf9057863a7 Mon Sep 17 00:00:00 2001 From: lachablock Date: Sun, 22 May 2022 23:48:03 +1000 Subject: [PATCH] Ring Shooter: countdown animation & experimental stretchy spawn --- src/info.c | 8 +-- src/k_kart.c | 33 ++++++++--- src/p_mobj.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 177 insertions(+), 16 deletions(-) diff --git a/src/info.c b/src/info.c index 7d35232fd..a082abbc5 100644 --- a/src/info.c +++ b/src/info.c @@ -4472,8 +4472,8 @@ state_t states[NUMSTATES] = {SPR_RSHT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_SIDE {SPR_RSHT, FF_SEMIBRIGHT|FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NIPPLES {SPR_RSHT, FF_PAPERSPRITE|4, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_SCREEN - {SPR_RSHT, FF_FULLBRIGHT|FF_PAPERSPRITE|8, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NUMBERBACK - {SPR_RSHT, FF_FULLBRIGHT|FF_PAPERSPRITE|12, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NUMBERFRONT + {SPR_RSHT, FF_FULLBRIGHT|FF_PAPERSPRITE|5, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NUMBERBACK + {SPR_RSHT, FF_FULLBRIGHT|FF_PAPERSPRITE|9, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NUMBERFRONT {SPR_PLAY, FF_FULLBRIGHT|FF_PAPERSPRITE|SPR2_XTRA, -1, {A_RingShooterFace}, 0, 0, S_NULL}, // S_RINGSHOOTER_FACE {SPR_DEZL, FF_FULLBRIGHT|FF_PAPERSPRITE, 8, {NULL}, 0, 0, S_NULL}, // S_DEZLASER @@ -24672,12 +24672,12 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // attacksound S_NULL, // painstate 0, // painchance - sfx_None, // painsound + sfx_s3ka7, // painsound S_NULL, // meleestate S_NULL, // missilestate S_NULL, // deathstate S_NULL, // xdeathstate - sfx_None, // deathsound + sfx_s3kad, // deathsound 0, // speed 16*FRACUNIT, // radius 16*FRACUNIT, // height diff --git a/src/k_kart.c b/src/k_kart.c index 4f8658ecf..d4eb041a4 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11775,13 +11775,15 @@ boolean K_Cooperative(void) //} +// I've tried to reduce redundancy as much as I can, +// but check P_UpdateRingShooterParts if you edit this void K_SpawnRingShooter(player_t *player) { const fixed_t scale = 2*FRACUNIT; mobjinfo_t *info = &mobjinfo[MT_RINGSHOOTER_PART]; mobj_t *mo = player->mo; mobj_t *base = P_SpawnMobj(mo->x, mo->y, mo->z, MT_RINGSHOOTER); - mobj_t *part; + mobj_t *part, *refNipple; UINT32 frameNum; angle_t angle; vector2_t offset; @@ -11791,6 +11793,15 @@ void K_SpawnRingShooter(player_t *player) P_SetTarget(&base->target, mo); P_SetScale(base, base->destscale = FixedMul(base->destscale, scale)); P_InitAngle(base, mo->angle); + base->scalespeed = FRACUNIT/2; + base->extravalue1 = FRACUNIT; // horizontal scale + base->extravalue2 = 0; // vertical scale + + // the ring shooter object itself is invisible and acts as the thinker + // each ring shooter uses three linked lists to keep track of its parts + // the hprev chain stores the two NIPPLE BARS + // the hnext chain stores the four sides of the box + // the tracer chain stores the screen and the screen layers // spawn the RING NIPPLES part = base; @@ -11810,8 +11821,11 @@ void K_SpawnRingShooter(player_t *player) P_InitAngle(part, base->angle - i * ANGLE_45); P_SetMobjState(part, S_RINGSHOOTER_NIPPLES); part->frame += frameNum; + part->flags |= MF_NOTHINK; + part->old_spriteyscale = part->spriteyscale = 0; frameNum++; } + refNipple = part; // keep the second ring nipple; its position will be referenced by the box // spawn the box part = base; @@ -11835,15 +11849,23 @@ void K_SpawnRingShooter(player_t *player) frameNum++; frameNum ^= FF_HORIZONTALFLIP; angle -= ANGLE_90; - part->frame += frameNum; P_InitAngle(part, angle); + part->frame += frameNum; + part->extravalue1 = part->x - refNipple->x; + part->extravalue2 = part->y - refNipple->y; + part->flags |= MF_NOTHINK; + part->old_spriteyscale = part->spriteyscale = 0; } // spawn the screen - part = P_SpawnMobjFromMobj(base, offset.x, offset.y, info->height, MT_RINGSHOOTER_SCREEN); + part = P_SpawnMobjFromMobj(base, offset.x, offset.y, 0, MT_RINGSHOOTER_SCREEN); P_SetTarget(&base->tracer, part); P_SetTarget(&part->target, base); P_InitAngle(part, base->angle - ANGLE_45); + part->extravalue1 = part->x - refNipple->x; + part->extravalue2 = part->y - refNipple->y; + part->flags |= MF_NOTHINK; + part->old_spriteyscale = part->spriteyscale = 0; // spawn the screen numbers for (i = 0; i < 2; i++) @@ -11853,9 +11875,6 @@ void K_SpawnRingShooter(player_t *player) part = part->tracer; P_InitAngle(part, part->target->angle); P_SetMobjState(part, S_RINGSHOOTER_NUMBERBACK + i); + part->renderflags |= RF_DONTDRAW; } - - // test face feature (to be moved into thinker later) - part->skin = mo->skin; - P_SetMobjState(part, S_RINGSHOOTER_FACE); } diff --git a/src/p_mobj.c b/src/p_mobj.c index 376afcb06..07b5840be 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -4550,17 +4550,150 @@ static void P_SpawnItemCapsuleParts(mobj_t *mobj) #undef ANG_CAPSULE #undef ROTATIONSPEED -static void P_RingShooterThinker(mobj_t *mo) +// ------------ +// RING SHOOTER +// ------------ + +static void P_ActivateRingShooter(mobj_t *mo) +{ + mobj_t *part = mo->tracer; + + while (!P_MobjWasRemoved(part->tracer)) + { + part = part->tracer; + part->renderflags &= ~RF_DONTDRAW; + part->frame += 4; + } +} + +#define scaleSpeed mo->scalespeed +#define scaleState mo->threshold +#define xScale mo->extravalue1 +#define yScale mo->extravalue2 +#define xOffset part->extravalue1 +#define yOffset part->extravalue2 +#define SCALEPART part->spritexscale = xScale; part->spriteyscale = yScale; +#define MOVEPART P_MoveOrigin(part, refNipple->x + FixedMul(xOffset, xScale), refNipple->y + FixedMul(yOffset, xScale), part->z); + +// I've tried to reduce redundancy as much as I can, +// but check K_SpawnRingShooter if you edit this +static void P_UpdateRingShooterParts(mobj_t *mo) +{ + mobj_t *part, *refNipple; + + part = mo; + while (!P_MobjWasRemoved(part->hprev)) + { + part = part->hprev; + SCALEPART + } + refNipple = part; + + part = mo; + while (!P_MobjWasRemoved(part->hnext)) + { + part = part->hnext; + MOVEPART + SCALEPART + } + + part = mo->tracer; + part->z = mo->z + FixedMul(refNipple->height, yScale); + MOVEPART + SCALEPART +} + +static boolean P_RingShooterInit(mobj_t *mo) +{ + if (scaleState == -1) + return false; + + switch (scaleState) { + case 0: + yScale += scaleSpeed; + if (yScale >= FRACUNIT) + { + //xScale -= scaleSpeed; + scaleState++; + } + break; + case 1: + scaleSpeed -= FRACUNIT/5; + yScale += scaleSpeed; + xScale -= scaleSpeed; + if (yScale < 3*FRACUNIT/4) + { + scaleState ++; + scaleSpeed = FRACUNIT >> 2; + } + break; + case 2: + yScale += scaleSpeed; + xScale -= scaleSpeed; + if (yScale >= FRACUNIT) + { + scaleState = -1; + xScale = yScale = FRACUNIT; + P_ActivateRingShooter(mo); + } + } + + P_UpdateRingShooterParts(mo); + return scaleState != -1; +} +#undef scaleSpeed +#undef scaleState +#undef xScale +#undef yScale +#undef xOffset +#undef yOffset +#undef MOVEPART +#undef SCALEPART + +static void P_RingShooterCountdown(mobj_t *mo) +{ + mobj_t *part = mo->tracer; + + if (mo->reactiontime == -1) + return; + + if (mo->reactiontime > 0) + { + mo->reactiontime--; + return; + } + + while (!P_MobjWasRemoved(part->tracer)) + { + part = part->tracer; + part->frame--; + } + + switch ((part->frame & FF_FRAMEMASK) - (part->state->frame & FF_FRAMEMASK)) { + case -1: + mo->reactiontime = -1; + part->skin = mo->skin; + P_SetMobjState(part, S_RINGSHOOTER_FACE); + break; + case 0: + mo->reactiontime = TICRATE; + S_StartSound(mo, mo->info->deathsound); + break; + default: + mo->reactiontime = TICRATE; + S_StartSound(mo, mo->info->painsound); + break; + } +} + +static void P_RingShooterFlicker(mobj_t *mo) { UINT32 trans; - mobj_t *part = mo; + mobj_t *part = mo->tracer; while (!P_MobjWasRemoved(part->tracer)) part = part->tracer; - if (part == mo) // ??? where did you go - return; - part->renderflags ^= RF_DONTDRAW; if (part->renderflags & RF_DONTDRAW) trans = FF_TRANS50; @@ -4569,6 +4702,15 @@ static void P_RingShooterThinker(mobj_t *mo) part->target->frame = (part->target->frame & ~FF_TRANSMASK) | trans; } +static void P_RingShooterThinker(mobj_t *mo) +{ + if (P_MobjWasRemoved(mo->tracer) || P_RingShooterInit(mo)) + return; + + P_RingShooterCountdown(mo); + P_RingShooterFlicker(mo); +} + // // P_BossTargetPlayer // If closest is true, find the closest player.