From a524422071bf07fc786a2d25a66b80b333e32077 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 27 Nov 2022 08:16:18 -0500 Subject: [PATCH] First pass on UFO visuals --- src/deh_tables.c | 6 ++ src/info.c | 40 +++++++- src/info.h | 10 ++ src/k_objects.h | 5 +- src/objects/ufo.c | 236 +++++++++++++++++++++++++++++++++++++++++++--- src/p_map.c | 2 +- src/p_mobj.c | 31 +++++- 7 files changed, 313 insertions(+), 17 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index b60bf269e..d856a33c3 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4534,6 +4534,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi // Broly Ki Orb "S_BROLY1", "S_BROLY2", + + "S_SPECIAL_UFO_POD", + "S_SPECIAL_UFO_OVERLAY", + "S_SPECIAL_UFO_ARM", + "S_SPECIAL_UFO_STEM", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -5631,6 +5636,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BROLY", "MT_SPECIAL_UFO", + "MT_SPECIAL_UFO_PIECE", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index ca4b802ba..84bfc7c17 100644 --- a/src/info.c +++ b/src/info.c @@ -785,6 +785,10 @@ char sprnames[NUMSPRITES + 1][5] = "FLBM", + "UFOB", + "UFOA", + "UFOS", + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later "VIEW", }; @@ -5146,6 +5150,11 @@ state_t states[NUMSTATES] = // Broly Ki Orb {SPR_LSSJ, FF_REVERSESUBTRACT|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_BROLY2}, // S_BROLY1 {SPR_NULL, 0, 5*TICRATE, {A_SSMineFlash}, 0, 0, S_NULL}, // S_BROLY2 + + {SPR_UFOB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_POD + {SPR_UFOB, 1|FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_SPECIAL_UFO_OVERLAY + {SPR_UFOA, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_ARM + {SPR_UFOS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_STEM }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -29058,7 +29067,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_SPECIAL_UFO + { // MT_SPECIAL_UFO -1, // doomednum S_CHAOSEMERALD1, // spawnstate 101, // spawnhealth @@ -29075,7 +29084,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 72*FRACUNIT, // radius + 108*FRACUNIT, // radius 72*FRACUNIT, // height 0, // display offset 16, // mass @@ -29084,6 +29093,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_SHOOTABLE|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, + + { // MT_SPECIAL_UFO_PIECE + -1, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // 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 + 8*FRACUNIT, // radius + 16*FRACUNIT, // height + 1, // display offset + 100, // mass + 1, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, }; skincolor_t skincolors[MAXSKINCOLORS] = { diff --git a/src/info.h b/src/info.h index fac8100c6..cf2b5d6bd 100644 --- a/src/info.h +++ b/src/info.h @@ -1332,6 +1332,10 @@ typedef enum sprite SPR_FLBM, // Finish line beam + SPR_UFOB, + SPR_UFOA, + SPR_UFOS, + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -5569,6 +5573,11 @@ typedef enum state S_BROLY1, S_BROLY2, + S_SPECIAL_UFO_POD, + S_SPECIAL_UFO_OVERLAY, + S_SPECIAL_UFO_ARM, + S_SPECIAL_UFO_STEM, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -6685,6 +6694,7 @@ typedef enum mobj_type MT_BROLY, MT_SPECIAL_UFO, + MT_SPECIAL_UFO_PIECE, MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, diff --git a/src/k_objects.h b/src/k_objects.h index 0f2318ee8..d9d89321e 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -58,9 +58,12 @@ void Obj_DuelBombInit(mobj_t *bomb); mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration); /* Special Stage UFO */ -void Obj_SpecialUFOThinker(mobj_t *bomb); +void Obj_SpecialUFOThinker(mobj_t *ufo); boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType); void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other); +void Obj_UFOPieceThink(mobj_t *piece); +void Obj_UFOPieceDead(mobj_t *piece); +void Obj_UFOPieceRemoved(mobj_t *piece); mobj_t *Obj_CreateSpecialUFO(void); UINT32 K_GetSpecialUFODistance(void); diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 7cb15ff27..9672d840e 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -8,7 +8,7 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file ufo.c -/// \brief Special Stage UFO +/// \brief Special Stage UFO + Emerald handler #include "../doomdef.h" #include "../doomstat.h" @@ -31,11 +31,29 @@ #define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much. #define UFO_SPEEDFACTOR (FRACUNIT * 3 / 4) // Factor of player's best speed, to make it more fair. +#define UFO_NUMARMS (3) +#define UFO_ARMDELTA (ANGLE_MAX / UFO_NUMARMS) + #define ufo_waypoint(o) ((o)->extravalue1) #define ufo_distancetofinish(o) ((o)->extravalue2) #define ufo_speed(o) ((o)->watertop) #define ufo_collectdelay(o) ((o)->threshold) +#define ufo_pieces(o) ((o)->hnext) + +#define ufo_piece_type(o) ((o)->extravalue1) + +#define ufo_piece_owner(o) ((o)->target) +#define ufo_piece_next(o) ((o)->hnext) +#define ufo_piece_prev(o) ((o)->hprev) + +enum +{ + UFO_PIECE_TYPE_POD, + UFO_PIECE_TYPE_ARM, + UFO_PIECE_TYPE_STEM, +}; + static void UFOMoveTo(mobj_t *ufo, fixed_t destx, fixed_t desty, fixed_t destz) { ufo->momx = destx - ufo->x; @@ -55,6 +73,11 @@ static boolean UFOEmeraldChase(mobj_t *ufo) return (ufo->health <= 1); } +static boolean UFOPieceValid(mobj_t *piece) +{ + return (piece != NULL && P_MobjWasRemoved(piece) == false && piece->health > 0); +} + static void UFOUpdateDistanceToFinish(mobj_t *ufo) { waypoint_t *finishLine = K_GetFinishLineWaypoint(); @@ -221,6 +244,7 @@ static void UFOMove(mobj_t *ufo) fixed_t newX = ufo->x; fixed_t newY = ufo->y; fixed_t newZ = ufo->z; + const fixed_t floatHeight = 24 * ufo->scale; const boolean useshortcuts = false; const boolean huntbackwards = false; @@ -253,7 +277,7 @@ static void UFOMove(mobj_t *ufo) { fixed_t wpX = curWaypoint->mobj->x; fixed_t wpY = curWaypoint->mobj->y; - fixed_t wpZ = curWaypoint->mobj->z; + fixed_t wpZ = curWaypoint->mobj->z + floatHeight; fixed_t distToNext = GenericDistance( newX, newY, newZ, @@ -336,13 +360,19 @@ static void UFOMove(mobj_t *ufo) static void UFOEmeraldVFX(mobj_t *ufo) { + const INT32 bobS = 32; + const angle_t bobA = (leveltime & (bobS - 1)) * (ANGLE_MAX / bobS); + const fixed_t bobH = 16 * ufo->scale; + + ufo->sprzoff = FixedMul(bobH, FINESINE(bobA >> ANGLETOFINESHIFT)); + if (leveltime % 3 == 0) { mobj_t *sparkle = P_SpawnMobjFromMobj( ufo, P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT, - P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT, + (P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT) + FixedDiv(ufo->sprzoff, ufo->scale), MT_EMERALDSPARK ); @@ -370,6 +400,65 @@ void Obj_SpecialUFOThinker(mobj_t *ufo) } } +static void UFOCopyHitlagToPieces(mobj_t *ufo) +{ + mobj_t *piece = NULL; + + piece = ufo_pieces(ufo); + while (UFOPieceValid(piece) == true) + { + piece->hitlag = ufo->hitlag; + piece->eflags = (piece->eflags & ~MFE_DAMAGEHITLAG) | (ufo->eflags & MFE_DAMAGEHITLAG); + piece = ufo_piece_next(piece); + } +} + +static void UFOKillPiece(mobj_t *piece) +{ + angle_t dir = ANGLE_MAX; + fixed_t thrust = 0; + + if (UFOPieceValid(piece) == false) + { + return; + } + + piece->health = 0; + piece->tics = TICRATE; + piece->flags &= ~MF_NOGRAVITY; + + switch (ufo_piece_type(piece)) + { + case UFO_PIECE_TYPE_ARM: + { + dir = piece->angle; + thrust = 12 * piece->scale; + break; + } + default: + { + dir = FixedAngle(P_RandomRange(PR_DECORATION, 0, 359) << FRACBITS); + thrust = 4 * piece->scale; + break; + } + } + + P_Thrust(piece, dir, -thrust); + P_SetObjectMomZ(piece, 12*FRACUNIT, true); +} + +static void UFOKillPieces(mobj_t *ufo) +{ + mobj_t *piece = NULL; + + piece = ufo_pieces(ufo); + while (UFOPieceValid(piece) == true) + { + UFOKillPiece(piece); + piece = ufo_piece_next(piece); + } +} + static UINT8 GetUFODamage(mobj_t *inflictor) { if (inflictor == NULL || P_MobjWasRemoved(inflictor) == true) @@ -408,18 +497,15 @@ static UINT8 GetUFODamage(mobj_t *inflictor) boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType) { + const fixed_t addSpeed = FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); UINT8 damage = 1; (void)source; (void)damageType; - // Speed up on damage! - ufo_speed(ufo) += FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed)); - if (UFOEmeraldChase(ufo) == true) { // Damaged fully already, no need for any more. - ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); // Double check flags, just to be sure. return false; } @@ -430,13 +516,22 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN return false; } + // Speed up on damage! + ufo_speed(ufo) += addSpeed; + K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); + UFOCopyHitlagToPieces(ufo); if (damage >= ufo->health - 1) { // Destroy the UFO parts, and make the emerald collectible! + UFOKillPieces(ufo); + ufo->health = 1; ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); + ufo->shadowscale = FRACUNIT/3; + + ufo_speed(ufo) += addSpeed; // Even more speed! return true; } @@ -462,10 +557,96 @@ void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other) } } +void Obj_UFOPieceThink(mobj_t *piece) +{ + mobj_t *ufo = ufo_piece_owner(piece); + + if (ufo == NULL || P_MobjWasRemoved(ufo) == true) + { + P_KillMobj(piece, NULL, NULL, DMG_NORMAL); + return; + } + + piece->destscale = ufo->destscale; + piece->scalespeed = ufo->scalespeed; + + switch (ufo_piece_type(piece)) + { + case UFO_PIECE_TYPE_POD: + { + UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (120 * ufo->scale)); + break; + } + case UFO_PIECE_TYPE_ARM: + { + fixed_t dis = (104 * ufo->scale); + + fixed_t x = ufo->x - FixedMul(dis, FINECOSINE(piece->angle >> ANGLETOFINESHIFT)); + fixed_t y = ufo->y - FixedMul(dis, FINESINE(piece->angle >> ANGLETOFINESHIFT)); + + UFOMoveTo(piece, x, y, ufo->z + (24 * ufo->scale)); + + piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED)); + break; + } + default: + { + P_KillMobj(piece, NULL, NULL, DMG_NORMAL); + return; + } + } +} + +void Obj_UFOPieceDead(mobj_t *piece) +{ + piece->renderflags ^= RF_DONTDRAW; +} + +void Obj_UFOPieceRemoved(mobj_t *piece) +{ + // Repair piece list. + mobj_t *ufo = ufo_piece_owner(piece); + mobj_t *next = ufo_piece_next(piece); + mobj_t *prev = ufo_piece_prev(piece); + + if (prev != NULL && P_MobjWasRemoved(prev) == false) + { + P_SetTarget( + &ufo_piece_next(prev), + (next != NULL && P_MobjWasRemoved(next) == false) ? next : NULL + ); + } + + if (next != NULL && P_MobjWasRemoved(next) == false) + { + P_SetTarget( + &ufo_piece_prev(next), + (prev != NULL && P_MobjWasRemoved(prev) == false) ? prev : NULL + ); + + if (ufo != NULL && P_MobjWasRemoved(ufo) == false) + { + if (piece == ufo_pieces(ufo)) + { + P_SetTarget( + &ufo_pieces(ufo), + next + ); + } + } + } + + P_SetTarget(&ufo_piece_next(piece), NULL); + P_SetTarget(&ufo_piece_prev(piece), NULL); +} + static mobj_t *InitSpecialUFO(waypoint_t *start) { mobj_t *ufo = NULL; - mobj_t *underlay = NULL; + mobj_t *overlay = NULL; + mobj_t *piece = NULL; + mobj_t *prevPiece = NULL; + size_t i; if (start == NULL) { @@ -487,12 +668,43 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) // TODO: Adjustable Special Stage emerald color ufo->color = SKINCOLOR_CHAOSEMERALD1; - underlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&underlay->target, ufo); - underlay->color = ufo->color; + overlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, ufo); + overlay->color = ufo->color; // TODO: Super Emeralds / Chaos Rings - P_SetMobjState(underlay, S_CHAOSEMERALD_UNDER); + P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER); + + // Create UFO pieces. + // First: UFO center. + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_POD); + ufo_piece_type(piece) = UFO_PIECE_TYPE_POD; + + overlay = P_SpawnMobjFromMobj(piece, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, piece); + P_SetMobjState(overlay, S_SPECIAL_UFO_OVERLAY); + + P_SetTarget(&ufo_pieces(ufo), piece); + prevPiece = piece; + + for (i = 0; i < UFO_NUMARMS; i++) + { + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_ARM); + ufo_piece_type(piece) = UFO_PIECE_TYPE_ARM; + + piece->angle = UFO_ARMDELTA * i; + + P_SetTarget(&ufo_piece_next(prevPiece), piece); + P_SetTarget(&ufo_piece_prev(piece), prevPiece); + + prevPiece = piece; + } return ufo; } diff --git a/src/p_map.c b/src/p_map.c index 9816643fe..ee18c2b81 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1352,7 +1352,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) { if (!(thing->flags & MF_SPECIAL)) { - Obj_PlayerUFOCollide(thing, tmthing); + Obj_PlayerUFOCollide(thing, tm.thing); return BMIT_CONTINUE; } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 0e6ad5647..fcbfeafa7 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5298,10 +5298,22 @@ void P_RunOverlays(void) mo->pitch = mo->target->pitch; mo->roll = mo->target->roll; + mo->spritexoffset = mo->target->spritexoffset; + mo->spriteyoffset = mo->target->spriteyoffset; + mo->spritexscale = mo->target->spritexscale; + mo->spriteyscale = mo->target->spriteyscale; + + mo->sprxoff = mo->target->sprxoff; + mo->spryoff = mo->target->spryoff; + mo->sprzoff = mo->target->sprzoff; + + mo->hitlag = mo->target->hitlag; + mo->eflags = (mo->eflags & ~MFE_DAMAGEHITLAG) | (mo->target->eflags & MFE_DAMAGEHITLAG); + if ((mo->flags & MF_DONTENCOREMAP) != (mo->target->flags & MF_DONTENCOREMAP)) mo->flags ^= MF_DONTENCOREMAP; - mo->dispoffset = mo->target->dispoffset + mo->info->dispoffset; + mo->dispoffset = mo->target->dispoffset; if (!(mo->state->frame & FF_ANIMATE)) { @@ -5321,6 +5333,7 @@ void P_RunOverlays(void) // if you're using FF_ANIMATE on an overlay, // then you're on your own. zoffs = 0; + mo->dispoffset++; } P_UnsetThingPosition(mo); @@ -6743,6 +6756,11 @@ static boolean P_MobjDeadThink(mobj_t *mobj) S_StartSound(dust, sfx_s3k3d); } break; + case MT_SPECIAL_UFO_PIECE: + { + Obj_UFOPieceDead(mobj); + break; + } default: break; } @@ -7297,6 +7315,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_SpecialUFOThinker(mobj); break; } + case MT_SPECIAL_UFO_PIECE: + { + Obj_UFOPieceThink(mobj); + break; + } case MT_EMERALD: { if (battleovertime.enabled >= 10*TICRATE) @@ -10050,6 +10073,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_PLAYER: case MT_KART_LEFTOVER: case MT_BATTLECAPSULE: + case MT_SPECIAL_UFO: thing->shadowscale = FRACUNIT; break; case MT_SMALLMACE: @@ -10906,6 +10930,11 @@ void P_RemoveMobj(mobj_t *mobj) Obj_ShrinkGunRemoved(mobj); } + if (mobj->type == MT_SPECIAL_UFO_PIECE) + { + Obj_UFOPieceRemoved(mobj); + } + mobj->health = 0; // Just because // unlink from sector and block lists