From 1fee9f65fb68722772960793033e68d05e03d287 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 15 Jun 2023 18:46:44 +0100 Subject: [PATCH] Servant Hand - Points in the direction of the best waypoint to take - Vwoops in and out like a drop target squash-n-stretch - Shows WRONG WAY only on debugwaypoints - Flexible enough to be used for custom purposes and other gametypes, the only caveat being if those gametypes use GTR_CIRCUIT conflicting with the other purpose of PF_WRONGWAY --- src/d_player.h | 4 ++ src/deh_tables.c | 4 ++ src/g_game.c | 1 + src/info.c | 31 +++++++++++++ src/info.h | 6 +++ src/k_hud.c | 7 +-- src/k_kart.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++- src/k_kart.h | 1 + src/p_mobj.c | 16 +++++++ src/p_saveg.c | 25 +++++++++++ 10 files changed, 198 insertions(+), 7 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 877f65c59..7775a70f4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -744,10 +744,14 @@ struct player_t mobj_t *stumbleIndicator; mobj_t *sliptideZipIndicator; mobj_t *whip; + mobj_t *hand; UINT8 instaShieldCooldown; UINT8 guardCooldown; + UINT8 handtimer; + angle_t besthanddirection; + INT16 incontrol; // -1 to -175 when spinning out or tumbling, 1 to 175 when not. Use to check for combo hits or emergency inputs. boolean markedfordeath; diff --git a/src/deh_tables.c b/src/deh_tables.c index 3cce64a49..e44c6cb52 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3291,6 +3291,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_BLOCKRING", "S_BLOCKBODY", + "S_SERVANTHAND", + // Signpost sparkles "S_SIGNSPARK1", "S_SIGNSPARK2", @@ -5337,6 +5339,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BLOCKRING", "MT_BLOCKBODY", + "MT_SERVANTHAND", + "MT_SIGNSPARKLE", "MT_FASTLINE", diff --git a/src/g_game.c b/src/g_game.c index 5c3e3ca4a..8d8610741 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2689,6 +2689,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) P_SetTarget(&players[player].awayview.mobj, NULL); P_SetTarget(&players[player].stumbleIndicator, NULL); P_SetTarget(&players[player].whip, NULL); + P_SetTarget(&players[player].hand, NULL); P_SetTarget(&players[player].ringShooter, NULL); P_SetTarget(&players[player].followmobj, NULL); diff --git a/src/info.c b/src/info.c index 05ce6aa36..cbe5556a3 100644 --- a/src/info.c +++ b/src/info.c @@ -559,6 +559,8 @@ char sprnames[NUMSPRITES + 1][5] = "GRNG", // Guard ring "GBDY", // Guard body + "DHND", // Servant Hand + "WIPD", // Wipeout dust trail "DRIF", // Drift Sparks "BDRF", // Brake drift sparks @@ -3956,6 +3958,8 @@ state_t states[NUMSTATES] = {SPR_GRNG, FF_FULLBRIGHT|FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_BLOCKRING {SPR_GBDY, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_BLOCKBODY + {SPR_DHND, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SERVANTHAND + {SPR_SGNS, FF_ADD|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_SIGNSPARK2}, // S_SIGNSPARK1 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_SIGNSPARK3}, // S_SIGNSPARK2 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_SIGNSPARK4}, // S_SIGNSPARK3 @@ -22760,6 +22764,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate }, + + { // MT_SERVANTHAND + -1, // doomednum + S_SERVANTHAND, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + 40*FRACUNIT, // radius + 40*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, { // MT_SIGNSPARKLE -1, // doomednum diff --git a/src/info.h b/src/info.h index ea28b56c8..566e23eab 100644 --- a/src/info.h +++ b/src/info.h @@ -1112,6 +1112,8 @@ typedef enum sprite SPR_GRNG, // Guard ring SPR_GBDY, // Guard body + SPR_DHND, // Servant Hand + SPR_WIPD, // Wipeout dust trail SPR_DRIF, // Drift Sparks SPR_BDRF, // Brake drift sparks @@ -4367,6 +4369,8 @@ typedef enum state S_BLOCKRING, S_BLOCKBODY, + S_SERVANTHAND, + // Signpost sparkles S_SIGNSPARK1, S_SIGNSPARK2, @@ -6448,6 +6452,8 @@ typedef enum mobj_type MT_BLOCKRING, MT_BLOCKBODY, + MT_SERVANTHAND, + MT_SIGNSPARKLE, MT_FASTLINE, diff --git a/src/k_hud.c b/src/k_hud.c index f958ff6c9..c0098b016 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5005,7 +5005,7 @@ static void K_DrawWaypointDebugger(void) } V_DrawString(8, 156, 0, va("Current Waypoint ID: %d", K_GetWaypointID(stplyr->currentwaypoint))); - V_DrawString(8, 166, 0, va("Next Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); + V_DrawString(8, 166, 0, va("Next Waypoint ID: %d%s", K_GetWaypointID(stplyr->nextwaypoint), ((stplyr->pflags & PF_WRONGWAY) ? " (WRONG WAY)" : ""))); V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); if (numstarposts > 0) @@ -5365,11 +5365,6 @@ void K_drawKartHUD(void) // Draw FREE PLAY. K_drawKartFreePlay(); - if (r_splitscreen == 0 && (stplyr->pflags & PF_WRONGWAY) && ((leveltime / 8) & 1)) - { - V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); - } - if ((netgame || cv_mindelay.value) && r_splitscreen && Playing()) { K_drawMiniPing(); diff --git a/src/k_kart.c b/src/k_kart.c index 33fcd8819..8ff8e25c3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8275,9 +8275,14 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->tripwireState = TRIPSTATE_NONE; } + if (player->hand && P_MobjWasRemoved(player->hand)) + P_SetTarget(&player->hand, NULL); + if (player->spectator == false) { K_KartEbrakeVisuals(player); + + K_KartServantHandVisuals(player); } if (K_GetKartButtons(player) & BT_BRAKE && @@ -8596,6 +8601,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) { angle_t nextbestdelta = ANGLE_90; angle_t nextbestmomdelta = ANGLE_90; + angle_t nextbestanydelta = ANGLE_MAX; size_t i = 0U; if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) @@ -8637,8 +8643,14 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) momdelta = InvAngle(momdelta); } - if (angledelta < nextbestdelta || momdelta < nextbestmomdelta) + if (angledelta < nextbestanydelta || momdelta < nextbestanydelta) { + nextbestanydelta = min(angledelta, momdelta); + player->besthanddirection = angletowaypoint; + + if (nextbestanydelta >= ANGLE_90) + continue; + // Wanted to use a next waypoint, so remove WRONG WAY flag. // Done here instead of when set, because of finish line // hacks meaning we might not actually use this one, but @@ -10158,6 +10170,102 @@ void K_KartEbrakeVisuals(player_t *p) } } +void K_KartServantHandVisuals(player_t *player) +{ + if (player->pflags & PF_WRONGWAY) + { + if (player->handtimer < TICRATE) + { + player->handtimer++; + if (player->hand == NULL && player->handtimer == TICRATE) + { + mobj_t *hand = P_SpawnMobj( + player->mo->x, + player->mo->y, + player->mo->z + player->mo->height + 30*mapobjectscale, + MT_SERVANTHAND + ); + + if (hand) + { + K_FlipFromObject(hand, player->mo); + hand->old_z = hand->z; + + P_SetTarget(&hand->target, player->mo); + P_SetTarget(&player->hand, hand); + + hand->fuse = 8; + } + } + } + + if (player->hand) + { + player->hand->destscale = mapobjectscale; + } + } + else if (player->handtimer != 0) + { + player->handtimer--; + } + + if (player->hand) + { + const fixed_t handpokespeed = 4; + const fixed_t looping = handpokespeed - abs((player->hand->threshold % (handpokespeed*2)) - handpokespeed); + fixed_t xoffs = 0, yoffs = 0; + + player->hand->color = player->skincolor; + player->hand->angle = player->besthanddirection; + + if (player->hand->fuse != 0) + { + ; + } + else if (looping != 0) + { + xoffs = FixedMul(2 * looping * mapobjectscale, FINECOSINE(player->hand->angle >> ANGLETOFINESHIFT)), + yoffs = FixedMul(2 * looping * mapobjectscale, FINESINE(player->hand->angle >> ANGLETOFINESHIFT)), + + player->hand->threshold++; + } + else if (player->handtimer == 0) + { + player->hand->fuse = 8; + } + else + { + player->hand->threshold++; + } + + if (player->hand->fuse != 0) + { + if ((player->hand->fuse > 4) ^ (player->handtimer < TICRATE/2)) + { + player->hand->spritexscale = FRACUNIT/3; + player->hand->spriteyscale = 3*FRACUNIT; + } + else + { + player->hand->spritexscale = 2*FRACUNIT; + player->hand->spriteyscale = FRACUNIT/2; + } + } + + P_MoveOrigin(player->hand, + player->mo->x + xoffs, + player->mo->y + yoffs, + player->mo->z + player->mo->height + 30*mapobjectscale + ); + K_FlipFromObject(player->hand, player->mo); + + player->hand->sprzoff = player->mo->sprzoff; + + player->hand->renderflags &= ~RF_DONTDRAW; + player->hand->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player)); + } +} + static void K_KartSpindashDust(mobj_t *parent) { fixed_t rad = FixedDiv(FixedHypot(parent->radius, parent->radius), parent->scale); diff --git a/src/k_kart.h b/src/k_kart.h index af7d142d2..379d8c3ea 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -196,6 +196,7 @@ UINT8 K_GetInvincibilityItemFrame(void); UINT8 K_GetOrbinautItemFrame(UINT8 count); boolean K_IsSPBInGame(void); void K_KartEbrakeVisuals(player_t *p); +void K_KartServantHandVisuals(player_t *player); void K_HandleDirectionalInfluence(player_t *player); fixed_t K_DefaultPlayerRadius(player_t *player); diff --git a/src/p_mobj.c b/src/p_mobj.c index f0e529a0d..57880f350 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9776,6 +9776,22 @@ static boolean P_FuseThink(mobj_t *mobj) Obj_SPBExplode(mobj); break; } + case MT_SERVANTHAND: + { + if (!mobj->target + || P_MobjWasRemoved(mobj->target) + || !mobj->target->player + || mobj->target->player->handtimer == 0) + { + P_RemoveMobj(mobj); + return false; + } + + mobj->spritexscale = FRACUNIT; + mobj->spriteyscale = FRACUNIT; + + break; + } case MT_PLAYER: break; // don't remove default: diff --git a/src/p_saveg.c b/src/p_saveg.c index f022feedf..f9d019050 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -76,6 +76,7 @@ typedef enum SLIPTIDEZIP = 0x0080, RINGSHOOTER = 0x0100, WHIP = 0x0200, + HAND = 0x0400, } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -229,6 +230,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].whip) flags |= WHIP; + if (players[i].hand) + flags |= HAND; + if (players[i].ringShooter) flags |= RINGSHOOTER; @@ -258,6 +262,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & WHIP) WRITEUINT32(save->p, players[i].whip->mobjnum); + if (flags & HAND) + WRITEUINT32(save->p, players[i].hand->mobjnum); + if (flags & RINGSHOOTER) WRITEUINT32(save->p, players[i].ringShooter->mobjnum); @@ -429,6 +436,10 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].instaShieldCooldown); WRITEUINT8(save->p, players[i].guardCooldown); + + WRITEUINT8(save->p, players[i].handtimer); + WRITEANGLE(save->p, players[i].besthanddirection); + WRITEINT16(save->p, players[i].incontrol); WRITEUINT8(save->p, players[i].markedfordeath); @@ -652,6 +663,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & WHIP) players[i].whip = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & HAND) + players[i].hand = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & RINGSHOOTER) players[i].ringShooter = (mobj_t *)(size_t)READUINT32(save->p); @@ -824,6 +838,10 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].instaShieldCooldown = READUINT8(save->p); players[i].guardCooldown = READUINT8(save->p); + + players[i].handtimer = READUINT8(save->p); + players[i].besthanddirection = READANGLE(save->p); + players[i].incontrol = READINT16(save->p); players[i].markedfordeath = READUINT8(save->p); @@ -5136,6 +5154,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&players[i].whip, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "whip not found on player %d\n", i); } + if (players[i].hand) + { + temp = (UINT32)(size_t)players[i].hand; + players[i].hand = NULL; + if (!P_SetTarget(&players[i].hand, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "hand not found on player %d\n", i); + } if (players[i].ringShooter) { temp = (UINT32)(size_t)players[i].ringShooter;