A much more focused replacement for Hornmod, specc'd out by Tyron and Oni working together and implemented by the author of this commit because it's pretty funny.

- Followers have `hornsound` in their SOC configuration.
    - The default sound for all followers without a provided one is sfx_horn00.
- They'll play this sound if you use lookback with one following you, and there's nearby players to get the player looking all the way around.
    - Only the players who are successfully considered for lookback will hear it.
- Has a v1-like visual with less randomisation, but still netsynced.
- Also controlled by the cvar `taunthorns`, which, like `tauntvoices`, takes "Tasteful" (default), "Meme", and "Off".

TODO: make the condition for horn a little delayed, so you have to hold lookback for a little bit.
This commit is contained in:
toaster 2023-06-28 17:54:23 +01:00
parent be9ff7e7cb
commit e7ee979f1a
13 changed files with 217 additions and 4 deletions

View file

@ -412,6 +412,7 @@ static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentag
consvar_t cv_kartspeedometer = CVAR_INIT ("speedometer", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display consvar_t cv_kartspeedometer = CVAR_INIT ("speedometer", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display
static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}};
consvar_t cv_kartvoices = CVAR_INIT ("tauntvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL); consvar_t cv_kartvoices = CVAR_INIT ("tauntvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL);
consvar_t cv_karthorns = CVAR_INIT ("taunthorns", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL);
static CV_PossibleValue_t kartbot_cons_t[] = { static CV_PossibleValue_t kartbot_cons_t[] = {
{0, "Off"}, {0, "Off"},

View file

@ -86,6 +86,7 @@ extern consvar_t cv_kartfrantic;
extern consvar_t cv_kartencore; extern consvar_t cv_kartencore;
extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartspeedometer;
extern consvar_t cv_kartvoices; extern consvar_t cv_kartvoices;
extern consvar_t cv_karthorns;
extern consvar_t cv_kartbot; extern consvar_t cv_kartbot;
extern consvar_t cv_karteliminatelast; extern consvar_t cv_karteliminatelast;
extern consvar_t cv_thunderdome; extern consvar_t cv_thunderdome;

View file

@ -277,6 +277,7 @@ typedef enum
khud_enginesnd, // Engine sound offset this player is using. khud_enginesnd, // Engine sound offset this player is using.
khud_voices, // Used to stop the player saying more voices than it should khud_voices, // Used to stop the player saying more voices than it should
khud_tauntvoices, // Used to specifically stop taunt voice spam khud_tauntvoices, // Used to specifically stop taunt voice spam
khud_taunthorns, // Used to specifically stop taunt horn spam
// Battle // Battle
khud_cardanimation, // Used to determine the position of some full-screen Battle Mode graphics khud_cardanimation, // Used to determine the position of some full-screen Battle Mode graphics

View file

@ -3625,6 +3625,7 @@ void readfollower(MYFILE *f)
followers[numfollowers].hitconfirmtime = TICRATE; followers[numfollowers].hitconfirmtime = TICRATE;
followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH; followers[numfollowers].defaultcolor = FOLLOWERCOLOR_MATCH;
followers[numfollowers].category = UINT8_MAX; followers[numfollowers].category = UINT8_MAX;
followers[numfollowers].hornsound = sfx_horn00;
strcpy(followers[numfollowers].icon, "MISSING"); strcpy(followers[numfollowers].icon, "MISSING");
do do
@ -3710,6 +3711,10 @@ void readfollower(MYFILE *f)
deh_warning("Follower %d: unknown follower color '%s'", numfollowers, word2); deh_warning("Follower %d: unknown follower color '%s'", numfollowers, word2);
} }
} }
else if (fastcmp(word, "HORNSOUND"))
{
followers[numfollowers].hornsound = get_number(word2);
}
else if (fastcmp(word, "SCALE")) else if (fastcmp(word, "SCALE"))
{ {
followers[numfollowers].scale = (fixed_t)get_number(word2); followers[numfollowers].scale = (fixed_t)get_number(word2);

View file

@ -3308,6 +3308,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_SERVANTHAND", "S_SERVANTHAND",
"S_HORNCODE",
// Signpost sparkles // Signpost sparkles
"S_SIGNSPARK1", "S_SIGNSPARK1",
"S_SIGNSPARK2", "S_SIGNSPARK2",
@ -5359,6 +5361,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_SERVANTHAND", "MT_SERVANTHAND",
"MT_HORNCODE",
"MT_SIGNSPARKLE", "MT_SIGNSPARKLE",
"MT_FASTLINE", "MT_FASTLINE",

View file

@ -563,6 +563,8 @@ char sprnames[NUMSPRITES + 1][5] =
"DHND", // Servant Hand "DHND", // Servant Hand
"HORN", // Horncode
"WIPD", // Wipeout dust trail "WIPD", // Wipeout dust trail
"DRIF", // Drift Sparks "DRIF", // Drift Sparks
"BDRF", // Brake drift sparks "BDRF", // Brake drift sparks
@ -3980,6 +3982,8 @@ state_t states[NUMSTATES] =
{SPR_DHND, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SERVANTHAND {SPR_DHND, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SERVANTHAND
{SPR_HORN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_HORNCODE
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_SIGNSPARK2}, // S_SIGNSPARK1 {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|1, 1, {NULL}, 0, 0, S_SIGNSPARK3}, // S_SIGNSPARK2
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_SIGNSPARK4}, // S_SIGNSPARK3 {SPR_SGNS, FF_ADD|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_SIGNSPARK4}, // S_SIGNSPARK3
@ -22840,6 +22844,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_HORNCODE
-1, // doomednum
S_HORNCODE, // 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
36*FRACUNIT, // radius
36*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SIGNSPARKLE { // MT_SIGNSPARKLE
-1, // doomednum -1, // doomednum

View file

@ -1114,6 +1114,8 @@ typedef enum sprite
SPR_DHND, // Servant Hand SPR_DHND, // Servant Hand
SPR_HORN, // Horncode
SPR_WIPD, // Wipeout dust trail SPR_WIPD, // Wipeout dust trail
SPR_DRIF, // Drift Sparks SPR_DRIF, // Drift Sparks
SPR_BDRF, // Brake drift sparks SPR_BDRF, // Brake drift sparks
@ -4390,6 +4392,8 @@ typedef enum state
S_SERVANTHAND, S_SERVANTHAND,
S_HORNCODE,
// Signpost sparkles // Signpost sparkles
S_SIGNSPARK1, S_SIGNSPARK1,
S_SIGNSPARK2, S_SIGNSPARK2,
@ -6476,6 +6480,8 @@ typedef enum mobj_type
MT_SERVANTHAND, MT_SERVANTHAND,
MT_HORNCODE,
MT_SIGNSPARKLE, MT_SIGNSPARKLE,
MT_FASTLINE, MT_FASTLINE,

View file

@ -11,6 +11,7 @@
#include "r_skins.h" #include "r_skins.h"
#include "p_local.h" #include "p_local.h"
#include "p_mobj.h" #include "p_mobj.h"
#include "s_sound.h"
#include "m_cond.h" #include "m_cond.h"
INT32 numfollowers = 0; INT32 numfollowers = 0;
@ -142,6 +143,11 @@ void K_RemoveFollower(player_t *player)
bub = tmp; bub = tmp;
} }
// And the honk.
bub = player->follower->hprev;
if (!P_MobjWasRemoved(bub))
P_RemoveMobj(bub);
P_RemoveMobj(player->follower); P_RemoveMobj(player->follower);
P_SetTarget(&player->follower, NULL); P_SetTarget(&player->follower, NULL);
} }
@ -649,6 +655,50 @@ void K_HandleFollower(player_t *player)
{ {
K_UpdateFollowerState(player->follower, fl->idlestate, FOLLOWERSTATE_IDLE); K_UpdateFollowerState(player->follower, fl->idlestate, FOLLOWERSTATE_IDLE);
} }
// Horncode
if (P_MobjWasRemoved(player->follower->hprev) == false)
{
mobj_t *honk = player->follower->hprev;
honk->flags2 &= ~MF2_AMBUSH;
honk->color = player->skincolor;
P_MoveOrigin(
honk,
player->follower->x,
player->follower->y,
player->follower->z + player->follower->height
);
K_FlipFromObject(honk, player->follower);
honk->angle = R_PointToAngle2(
player->mo->x,
player->mo->y,
player->follower->x,
player->follower->y
);
honk->destscale = (2*player->mo->scale)/3;
fixed_t offsetamount = 0;
if (honk->fuse > 1)
{
offsetamount = (honk->fuse-1)*honk->destscale/2;
if (leveltime & 1)
offsetamount = -offsetamount;
}
else if (S_SoundPlaying(honk, fl->hornsound))
{
honk->fuse++;
}
honk->sprxoff = P_ReturnThrustX(honk, honk->angle, offsetamount);
honk->spryoff = P_ReturnThrustY(honk, honk->angle, offsetamount);
honk->sprzoff = -honk->sprxoff;
}
} }
if (player->mo->hitlag) if (player->mo->hitlag)
@ -658,3 +708,76 @@ void K_HandleFollower(player_t *player)
return; return;
} }
} }
/*--------------------------------------------------
void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
See header file for description.
--------------------------------------------------*/
void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
{
if (
(cv_karthorns.value == 0)
|| taunter == NULL
|| victim == NULL
|| taunter->followerskin < 0
|| taunter->followerskin >= numfollowers
|| (P_IsLocalPlayer(victim) == false && cv_karthorns.value != 2)
|| P_MobjWasRemoved(taunter->mo) == true
|| P_MobjWasRemoved(taunter->follower) == true
)
return;
const follower_t *fl = &followers[taunter->followerskin];
const boolean tasteful = (taunter->karthud[khud_taunthorns] == 0);
if (tasteful || cv_karthorns.value == 2)
{
mobj_t *honk = taunter->follower->hprev;
const fixed_t desiredscale = (2*taunter->mo->scale)/3;
if (P_MobjWasRemoved(honk) == true)
{
honk = P_SpawnMobj(
taunter->follower->x,
taunter->follower->y,
taunter->follower->z + taunter->follower->height,
MT_HORNCODE
);
if (P_MobjWasRemoved(honk) == true)
return; // Permit lua override of horn production
P_SetTarget(&taunter->follower->hprev, honk);
P_SetTarget(&honk->target, taunter->follower);
K_FlipFromObject(honk, taunter->follower);
honk->color = taunter->skincolor;
honk->angle = honk->old_angle = R_PointToAngle2(
taunter->mo->x,
taunter->mo->y,
taunter->follower->x,
taunter->follower->y
);
}
// Only do for the first activation this tic.
if (!(honk->flags2 & MF2_AMBUSH))
{
honk->destscale = desiredscale;
P_SetScale(honk, (11*desiredscale)/10);
honk->fuse = TICRATE/2;
honk->renderflags |= RF_DONTDRAW;
S_StartSound(taunter->follower, fl->hornsound);
honk->flags2 |= MF2_AMBUSH;
}
honk->renderflags &= ~K_GetPlayerDontDrawFlag(victim);
}
}

View file

@ -226,6 +226,21 @@ void K_HandleFollower(player_t *player);
void K_RemoveFollower(player_t *player); void K_RemoveFollower(player_t *player);
/*--------------------------------------------------
void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
Plays horn and spawns object (MOSTLY non-netsynced)
Input Arguments:-
taunter - Source player with a follower
victim - Player that hears and sees the honk
Return:-
None
--------------------------------------------------*/
void K_FollowerHornTaunt(player_t *taunter, player_t *victim);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -359,6 +359,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartencore); CV_RegisterVar(&cv_kartencore);
CV_RegisterVar(&cv_kartspeedometer); CV_RegisterVar(&cv_kartspeedometer);
CV_RegisterVar(&cv_kartvoices); CV_RegisterVar(&cv_kartvoices);
CV_RegisterVar(&cv_karthorns);
CV_RegisterVar(&cv_kartbot); CV_RegisterVar(&cv_kartbot);
CV_RegisterVar(&cv_karteliminatelast); CV_RegisterVar(&cv_karteliminatelast);
CV_RegisterVar(&cv_thunderdome); CV_RegisterVar(&cv_thunderdome);
@ -2055,7 +2056,7 @@ void K_SpawnMagicianParticles(mobj_t *mo, int spread)
} }
} }
static SINT8 K_GlanceAtPlayers(player_t *glancePlayer) static SINT8 K_GlanceAtPlayers(player_t *glancePlayer, boolean horn)
{ {
const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
const angle_t blindSpotSize = ANG10; // ANG5 const angle_t blindSpotSize = ANG10; // ANG5
@ -2136,9 +2137,24 @@ static SINT8 K_GlanceAtPlayers(player_t *glancePlayer)
// That poses a limitation if there's an equal number of targets on both sides... // That poses a limitation if there's an equal number of targets on both sides...
// In that case, we'll pick the last chosen glance direction. // In that case, we'll pick the last chosen glance direction.
lastValidGlance = dir; lastValidGlance = dir;
if (horn == true)
{
K_FollowerHornTaunt(glancePlayer, p);
}
} }
} }
if (horn == true && lastValidGlance != 0)
{
const boolean tasteful = (glancePlayer->karthud[khud_taunthorns] == 0);
K_FollowerHornTaunt(glancePlayer, glancePlayer);
if (tasteful && glancePlayer->karthud[khud_taunthorns] < 2*TICRATE)
glancePlayer->karthud[khud_taunthorns] = 2*TICRATE;
}
if (glanceDir > 0) if (glanceDir > 0)
{ {
return 1; return 1;
@ -2215,7 +2231,8 @@ void K_KartMoveAnimation(player_t *player)
{ {
// Only try glancing if you're driving straight. // Only try glancing if you're driving straight.
// This avoids all-players loops when we don't need it. // This avoids all-players loops when we don't need it.
destGlanceDir = K_GlanceAtPlayers(player); const boolean horn = lookback && !(player->pflags & PF_GAINAX);
destGlanceDir = K_GlanceAtPlayers(player, horn);
if (lookback == true) if (lookback == true)
{ {
@ -7417,6 +7434,9 @@ void K_KartPlayerHUDUpdate(player_t *player)
if (player->karthud[khud_tauntvoices]) if (player->karthud[khud_tauntvoices])
player->karthud[khud_tauntvoices]--; player->karthud[khud_tauntvoices]--;
if (player->karthud[khud_taunthorns])
player->karthud[khud_taunthorns]--;
if (player->karthud[khud_trickcool]) if (player->karthud[khud_trickcool])
player->karthud[khud_trickcool]--; player->karthud[khud_trickcool]--;

View file

@ -2484,6 +2484,10 @@ static boolean TypeIsNetSynced(mobjtype_t type)
if (type == MT_SPARK) if (type == MT_SPARK)
return false; return false;
// MT_HORNCODE: So it turns out hornmod is fundamentally incompatible with netsync
if (type == MT_HORNCODE)
return false;
return true; return true;
} }

View file

@ -1183,8 +1183,9 @@ sfxinfo_t S_sfx[NUMSFX] =
{"clawk1", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND {"clawk1", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND
{"clawk2", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND {"clawk2", false, 64, 16, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // SF_X8AWAYSOUND
{"monch", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"horn00", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "/"}, // HORNCODE
{"etexpl", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Game crash"}, {"monch", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"etexpl", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Game crash"},
{"iwhp", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Instawhip attack {"iwhp", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Instawhip attack
{"gbrk", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Guard break! {"gbrk", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Guard break!

View file

@ -1252,6 +1252,7 @@ typedef enum
sfx_clawk1, sfx_clawk1,
sfx_clawk2, sfx_clawk2,
sfx_horn00,
sfx_monch, sfx_monch,
sfx_etexpl, sfx_etexpl,