Add Garden Top arrows

Two arrows. One above the player, floating. One below the
player, on the ground.

Both arrows use the player's facing angle. Arrows are only
visible to their own player (other players can't see them).

The floating arrow is a papersprite. You can see the arrow
turn with you as you turn your camera.

The grounded arrow is a splat with additive blending. It
tilts to match the slope of the ground you're driving on.
This commit is contained in:
James R 2023-02-18 01:22:55 -08:00
parent ecec400ca4
commit b1fac18844
6 changed files with 180 additions and 13 deletions

View file

@ -3776,6 +3776,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_GARDENTOP_SINKING3", "S_GARDENTOP_SINKING3",
"S_GARDENTOP_DEAD", "S_GARDENTOP_DEAD",
"S_GARDENTOPSPARK", "S_GARDENTOPSPARK",
"S_GARDENTOPARROW",
// Caked-Up Booty-Sheet Ghost // Caked-Up Booty-Sheet Ghost
"S_HYUDORO", "S_HYUDORO",
@ -5389,6 +5390,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_BUBBLESHIELDTRAP", "MT_BUBBLESHIELDTRAP",
"MT_GARDENTOP", "MT_GARDENTOP",
"MT_GARDENTOPSPARK", "MT_GARDENTOPSPARK",
"MT_GARDENTOPARROW",
"MT_HYUDORO", "MT_HYUDORO",
"MT_HYUDORO_CENTER", "MT_HYUDORO_CENTER",

View file

@ -586,6 +586,7 @@ char sprnames[NUMSPRITES + 1][5] =
"FLML", // Flame Shield speed lines "FLML", // Flame Shield speed lines
"FLMF", // Flame Shield flash "FLMF", // Flame Shield flash
"GTOP", // Marble Garden Zone Spinning Top "GTOP", // Marble Garden Zone Spinning Top
"GTAR", // Garden Top Arrow
"HYUU", // Hyudoro "HYUU", // Hyudoro
"GRWP", // Grow "GRWP", // Grow
"POHB", // Shrink Poh-Bee "POHB", // Shrink Poh-Bee
@ -4370,6 +4371,7 @@ state_t states[NUMSTATES] =
{SPR_GTOP, 4, 1, {NULL}, 5, 1, S_GARDENTOP_SINKING1}, // S_GARDENTOP_SINKING3 {SPR_GTOP, 4, 1, {NULL}, 5, 1, S_GARDENTOP_SINKING1}, // S_GARDENTOP_SINKING3
{SPR_GTOP, FF_ANIMATE, 100, {A_Scream}, 5, 1, S_NULL}, // S_GARDENTOP_DEAD {SPR_GTOP, FF_ANIMATE, 100, {A_Scream}, 5, 1, S_NULL}, // S_GARDENTOP_DEAD
{SPR_BDRF, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 5, 2, S_NULL}, // S_GARDENTOPSPARK {SPR_BDRF, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 5, 2, S_NULL}, // S_GARDENTOPSPARK
{SPR_GTAR, FF_FULLBRIGHT, -1, {NULL}, 5, 2, S_NULL}, // S_GARDENTOPARROW
{SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO {SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO
@ -24036,6 +24038,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_GARDENTOPARROW
-1, // doomednum
S_GARDENTOPARROW, // 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
8, // speed
8*FRACUNIT, // radius
54*FRACUNIT, // height
1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOSQUISH, // flags
S_NULL // raisestate
},
{ // MT_HYUDORO { // MT_HYUDORO
-1, // doomednum -1, // doomednum
S_HYUDORO, // spawnstate S_HYUDORO, // spawnstate

View file

@ -1137,6 +1137,7 @@ typedef enum sprite
SPR_FLML, // Flame Shield speed lines SPR_FLML, // Flame Shield speed lines
SPR_FLMF, // Flame Shield flash SPR_FLMF, // Flame Shield flash
SPR_GTOP, // Marble Garden Zone Spinning Top SPR_GTOP, // Marble Garden Zone Spinning Top
SPR_GTAR, // Garden Top Arrow
SPR_HYUU, // Hyudoro SPR_HYUU, // Hyudoro
SPR_GRWP, // Grow SPR_GRWP, // Grow
SPR_POHB, // Shrink Poh-Bee SPR_POHB, // Shrink Poh-Bee
@ -4808,6 +4809,7 @@ typedef enum state
S_GARDENTOP_SINKING3, S_GARDENTOP_SINKING3,
S_GARDENTOP_DEAD, S_GARDENTOP_DEAD,
S_GARDENTOPSPARK, S_GARDENTOPSPARK,
S_GARDENTOPARROW,
// Caked-Up Booty-Sheet Ghost // Caked-Up Booty-Sheet Ghost
S_HYUDORO, S_HYUDORO,
@ -6457,6 +6459,7 @@ typedef enum mobj_type
MT_BUBBLESHIELDTRAP, MT_BUBBLESHIELDTRAP,
MT_GARDENTOP, MT_GARDENTOP,
MT_GARDENTOPSPARK, MT_GARDENTOPSPARK,
MT_GARDENTOPARROW,
MT_HYUDORO, MT_HYUDORO,
MT_HYUDORO_CENTER, MT_HYUDORO_CENTER,

View file

@ -19,6 +19,7 @@ mobj_t *Obj_GardenTopThrow(player_t *player);
mobj_t *Obj_GardenTopDestroy(player_t *player); mobj_t *Obj_GardenTopDestroy(player_t *player);
void Obj_GardenTopThink(mobj_t *top); void Obj_GardenTopThink(mobj_t *top);
void Obj_GardenTopSparkThink(mobj_t *spark); void Obj_GardenTopSparkThink(mobj_t *spark);
void Obj_GardenTopArrowThink(mobj_t *arrow);
boolean Obj_GardenTopPlayerIsGrinding(player_t *player); boolean Obj_GardenTopPlayerIsGrinding(player_t *player);
/* Shrink */ /* Shrink */

View file

@ -51,6 +51,14 @@ enum {
#define spark_top(o) ((o)->target) #define spark_top(o) ((o)->target)
#define spark_angle(o) ((o)->movedir) #define spark_angle(o) ((o)->movedir)
enum {
ARROW_OVERHEAD,
ARROW_IN_FRONT,
};
#define arrow_top(o) ((o)->target)
#define arrow_kind(o) ((o)->reactiontime)
static inline player_t * static inline player_t *
get_rider_player (mobj_t *rider) get_rider_player (mobj_t *rider)
{ {
@ -204,6 +212,49 @@ spawn_grind_spark (mobj_t *top)
} }
} }
static mobj_t *
spawn_arrow
( mobj_t * top,
UINT32 ff,
UINT8 kind)
{
mobj_t *arrow = P_SpawnMobjFromMobj(
top, 0, 0, 0, MT_GARDENTOPARROW);
P_SetTarget(&arrow_top(arrow), top);
arrow_kind(arrow) = kind;
arrow->frame |= ff;
return arrow;
}
static void
spawn_arrow_pair (mobj_t *top)
{
{
mobj_t *x = spawn_arrow(top,
FF_PAPERSPRITE, ARROW_OVERHEAD);
// overhead arrow is slightly smaller
P_SetScale(x, (x->destscale = 3 * x->scale / 4));
}
{
mobj_t *x = spawn_arrow(top,
FF_FLOORSPRITE | FF_ADD, ARROW_IN_FRONT);
x->renderflags |= RF_SLOPESPLAT | RF_NOSPLATBILLBOARD;
// Let splat be flat, useful later for
// Obj_GardenTopArrowThink reverse gravity.
x->height = 0;
// Make the arrow wider (sprite length is horizontal).
x->spriteyscale = 2*FRACUNIT;
}
}
static void static void
loop_sfx loop_sfx
( mobj_t * top, ( mobj_t * top,
@ -361,6 +412,25 @@ tilt (mobj_t *top)
} }
} }
static void
anchor
( mobj_t * us,
mobj_t * them,
angle_t angle,
fixed_t radius)
{
const fixed_t x = P_ReturnThrustX(us, angle, radius);
const fixed_t y = P_ReturnThrustY(us, angle, radius);
/* FIXME: THIS FUNCTION FUCKING SUCKS */
K_FlipFromObject(us, them);
P_MoveOrigin(us, them->x + x, them->y + y,
them->z + K_FlipZOffset(us, them));
us->angle = angle;
}
static void static void
anchor_top (mobj_t *top) anchor_top (mobj_t *top)
{ {
@ -375,8 +445,7 @@ anchor_top (mobj_t *top)
tilt(top); tilt(top);
P_MoveOrigin(top, rider->x, rider->y, anchor(top, rider, rider->angle, 0);
rider->z + K_FlipZOffset(top, rider));
K_GenericExtraFlagsNoZAdjust(top, rider); K_GenericExtraFlagsNoZAdjust(top, rider);
@ -447,17 +516,8 @@ anchor_spark (mobj_t *spark)
mobj_t *rider = top_rider(top); mobj_t *rider = top_rider(top);
player_t *player = get_rider_player(rider); player_t *player = get_rider_player(rider);
const angle_t angle = top->angle + spark_angle(spark); anchor(spark, top,
const fixed_t x = P_ReturnThrustX(top, angle, spark->scale); (top->angle + spark_angle(spark)), spark->scale);
const fixed_t y = P_ReturnThrustY(top, angle, spark->scale);
/* FIXME: THIS FUNCTION FUCKING SUCKS */
K_FlipFromObject(spark, top);
P_MoveOrigin(spark, top->x + x, top->y + y,
top->z + K_FlipZOffset(spark, top));
spark->angle = angle;
if (player) if (player)
{ {
@ -472,6 +532,39 @@ anchor_spark (mobj_t *spark)
} }
} }
static void
anchor_arrow_overhead (mobj_t *arrow)
{
mobj_t *top = arrow_top(arrow);
mobj_t *rider = top_rider(top);
const fixed_t height =
top->height + rider->height + (3 * arrow->height / 4);
arrow->sprzoff = top->sprzoff +
(height * P_MobjFlip(arrow));
anchor(arrow, top, rider->angle + ANGLE_180, 0);
}
static void
anchor_arrow_in_front (mobj_t *arrow)
{
mobj_t *top = arrow_top(arrow);
mobj_t *rider = top_rider(top);
anchor(arrow, top, rider->angle, 2 * rider->radius);
arrow->angle += ANGLE_90;
if (P_IsObjectFlipped(arrow))
{
arrow->angle += ANGLE_180;
}
arrow->floorspriteslope = rider->standingslope;
}
void void
Obj_GardenTopDeploy (mobj_t *rider) Obj_GardenTopDeploy (mobj_t *rider)
{ {
@ -497,6 +590,8 @@ Obj_GardenTopDeploy (mobj_t *rider)
} }
spawn_spark_circle(top, 6); spawn_spark_circle(top, 6);
spawn_arrow_pair(top);
} }
mobj_t * mobj_t *
@ -605,6 +700,38 @@ Obj_GardenTopSparkThink (mobj_t *spark)
} }
} }
void
Obj_GardenTopArrowThink (mobj_t *arrow)
{
mobj_t *top = arrow_top(arrow);
mobj_t *rider = top ? top_rider(top) : NULL;
if (!rider)
{
P_RemoveMobj(arrow);
return;
}
switch (arrow_kind(arrow))
{
case ARROW_OVERHEAD:
anchor_arrow_overhead(arrow);
break;
case ARROW_IN_FRONT:
anchor_arrow_in_front(arrow);
break;
}
if (rider->player)
{
// Don't show for other players
arrow->renderflags =
(arrow->renderflags & ~(RF_DONTDRAW)) |
(RF_DONTDRAW & ~(K_GetPlayerDontDrawFlag(rider->player)));
}
}
boolean boolean
Obj_GardenTopPlayerIsGrinding (player_t *player) Obj_GardenTopPlayerIsGrinding (player_t *player)
{ {

View file

@ -8394,6 +8394,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
Obj_GardenTopSparkThink(mobj); Obj_GardenTopSparkThink(mobj);
break; break;
} }
case MT_GARDENTOPARROW:
{
Obj_GardenTopArrowThink(mobj);
break;
}
case MT_HYUDORO: case MT_HYUDORO:
{ {
Obj_HyudoroThink(mobj); Obj_HyudoroThink(mobj);