diff --git a/src/deh_tables.c b/src/deh_tables.c index 19a5e4dde..16134fe68 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3776,6 +3776,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_GARDENTOP_SINKING3", "S_GARDENTOP_DEAD", "S_GARDENTOPSPARK", + "S_GARDENTOPARROW", // Caked-Up Booty-Sheet Ghost "S_HYUDORO", @@ -5389,6 +5390,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BUBBLESHIELDTRAP", "MT_GARDENTOP", "MT_GARDENTOPSPARK", + "MT_GARDENTOPARROW", "MT_HYUDORO", "MT_HYUDORO_CENTER", diff --git a/src/info.c b/src/info.c index 063c48220..cdfa6f088 100644 --- a/src/info.c +++ b/src/info.c @@ -586,6 +586,7 @@ char sprnames[NUMSPRITES + 1][5] = "FLML", // Flame Shield speed lines "FLMF", // Flame Shield flash "GTOP", // Marble Garden Zone Spinning Top + "GTAR", // Garden Top Arrow "HYUU", // Hyudoro "GRWP", // Grow "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, 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_GTAR, FF_FULLBRIGHT, -1, {NULL}, 5, 2, S_NULL}, // S_GARDENTOPARROW {SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO @@ -24036,6 +24038,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 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 -1, // doomednum S_HYUDORO, // spawnstate diff --git a/src/info.h b/src/info.h index fb932676d..f4771bd9c 100644 --- a/src/info.h +++ b/src/info.h @@ -1137,6 +1137,7 @@ typedef enum sprite SPR_FLML, // Flame Shield speed lines SPR_FLMF, // Flame Shield flash SPR_GTOP, // Marble Garden Zone Spinning Top + SPR_GTAR, // Garden Top Arrow SPR_HYUU, // Hyudoro SPR_GRWP, // Grow SPR_POHB, // Shrink Poh-Bee @@ -4808,6 +4809,7 @@ typedef enum state S_GARDENTOP_SINKING3, S_GARDENTOP_DEAD, S_GARDENTOPSPARK, + S_GARDENTOPARROW, // Caked-Up Booty-Sheet Ghost S_HYUDORO, @@ -6457,6 +6459,7 @@ typedef enum mobj_type MT_BUBBLESHIELDTRAP, MT_GARDENTOP, MT_GARDENTOPSPARK, + MT_GARDENTOPARROW, MT_HYUDORO, MT_HYUDORO_CENTER, diff --git a/src/k_objects.h b/src/k_objects.h index b1fe2014b..be73d4949 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -19,6 +19,7 @@ mobj_t *Obj_GardenTopThrow(player_t *player); mobj_t *Obj_GardenTopDestroy(player_t *player); void Obj_GardenTopThink(mobj_t *top); void Obj_GardenTopSparkThink(mobj_t *spark); +void Obj_GardenTopArrowThink(mobj_t *arrow); boolean Obj_GardenTopPlayerIsGrinding(player_t *player); /* Shrink */ diff --git a/src/objects/gardentop.c b/src/objects/gardentop.c index 3d55e03d1..6b8cf780a 100644 --- a/src/objects/gardentop.c +++ b/src/objects/gardentop.c @@ -51,6 +51,14 @@ enum { #define spark_top(o) ((o)->target) #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 * 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 loop_sfx ( 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 anchor_top (mobj_t *top) { @@ -375,8 +445,7 @@ anchor_top (mobj_t *top) tilt(top); - P_MoveOrigin(top, rider->x, rider->y, - rider->z + K_FlipZOffset(top, rider)); + anchor(top, rider, rider->angle, 0); K_GenericExtraFlagsNoZAdjust(top, rider); @@ -447,17 +516,8 @@ anchor_spark (mobj_t *spark) mobj_t *rider = top_rider(top); player_t *player = get_rider_player(rider); - const angle_t angle = top->angle + spark_angle(spark); - const fixed_t x = P_ReturnThrustX(top, angle, 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; + anchor(spark, top, + (top->angle + spark_angle(spark)), spark->scale); 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 Obj_GardenTopDeploy (mobj_t *rider) { @@ -497,6 +590,8 @@ Obj_GardenTopDeploy (mobj_t *rider) } spawn_spark_circle(top, 6); + + spawn_arrow_pair(top); } 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 Obj_GardenTopPlayerIsGrinding (player_t *player) { diff --git a/src/p_mobj.c b/src/p_mobj.c index f8c6ec400..bd50a9a51 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8394,6 +8394,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_GardenTopSparkThink(mobj); break; } + case MT_GARDENTOPARROW: + { + Obj_GardenTopArrowThink(mobj); + break; + } case MT_HYUDORO: { Obj_HyudoroThink(mobj);