First pass on UFO visuals

This commit is contained in:
Sally Coolatta 2022-11-27 08:16:18 -05:00
parent 2c5df772c8
commit a524422071
7 changed files with 313 additions and 17 deletions

View file

@ -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[] = {

View file

@ -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] = {

View file

@ -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,

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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