mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Move follower code to its own file
This commit is contained in:
parent
14053a55cd
commit
b5334e6b42
14 changed files with 649 additions and 475 deletions
|
|
@ -118,3 +118,4 @@ k_hud.c
|
|||
k_terrain.c
|
||||
k_brightmap.c
|
||||
k_director.c
|
||||
k_follower.c
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@
|
|||
#include "k_respawn.h"
|
||||
#include "k_grandprix.h"
|
||||
#include "k_boss.h"
|
||||
#include "k_follower.h"
|
||||
#include "doomstat.h"
|
||||
#include "deh_tables.h"
|
||||
|
||||
|
|
@ -1452,7 +1453,7 @@ static void SendNameAndColor(UINT8 n)
|
|||
|
||||
// Update follower for local games:
|
||||
if (cv_follower[n].value >= -1 && cv_follower[n].value != player->followerskin)
|
||||
SetFollower(playernum, cv_follower[n].value);
|
||||
K_SetFollowerByNum(playernum, cv_follower[n].value);
|
||||
|
||||
player->followercolor = cv_followercolor[n].value;
|
||||
|
||||
|
|
@ -1632,11 +1633,13 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
|||
SetPlayerSkinByNum(playernum, skin);
|
||||
|
||||
// set follower colour:
|
||||
// Don't bother doing garbage and kicking if we receive None, this is both silly and a waste of time, this will be handled properly in P_HandleFollower.
|
||||
// Don't bother doing garbage and kicking if we receive None,
|
||||
// this is both silly and a waste of time,
|
||||
// this will be handled properly in K_HandleFollower.
|
||||
p->followercolor = followercolor;
|
||||
|
||||
// set follower
|
||||
SetFollower(playernum, follower);
|
||||
K_SetFollowerByNum(playernum, follower);
|
||||
|
||||
#ifdef HAVE_DISCORDRPC
|
||||
if (playernum == consoleplayer)
|
||||
|
|
@ -5316,7 +5319,7 @@ static void Follower_OnChange(void)
|
|||
return;
|
||||
}
|
||||
|
||||
num = R_FollowerAvailable(str);
|
||||
num = K_FollowerAvailable(str);
|
||||
|
||||
if (num == -1) // that's an error.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
|
||||
|
|
@ -5370,7 +5373,7 @@ static void Follower2_OnChange(void)
|
|||
return;
|
||||
}
|
||||
|
||||
num = R_FollowerAvailable(str);
|
||||
num = K_FollowerAvailable(str);
|
||||
|
||||
if (num == -1) // that's an error.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
|
||||
|
|
@ -5421,7 +5424,7 @@ static void Follower3_OnChange(void)
|
|||
return;
|
||||
}
|
||||
|
||||
num = R_FollowerAvailable(str);
|
||||
num = K_FollowerAvailable(str);
|
||||
|
||||
if (num == -1) // that's an error.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
|
||||
|
|
@ -5472,7 +5475,7 @@ static void Follower4_OnChange(void)
|
|||
return;
|
||||
}
|
||||
|
||||
num = R_FollowerAvailable(str);
|
||||
num = K_FollowerAvailable(str);
|
||||
|
||||
if (num == -1) // that's an error.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
|
||||
// SRB2Kart
|
||||
#include "filesrch.h" // refreshdirmenu
|
||||
#include "k_follower.h"
|
||||
|
||||
// Loops through every constant and operation in word and performs its calculations, returning the final value.
|
||||
fixed_t get_number(const char *word)
|
||||
|
|
@ -3819,15 +3820,15 @@ void readfollower(MYFILE *f)
|
|||
|
||||
// Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead.
|
||||
followers[numfollowers].scale = FRACUNIT;
|
||||
followers[numfollowers].bubblescale = 0; // No bubble by default
|
||||
followers[numfollowers].atangle = 230;
|
||||
followers[numfollowers].dist = 32; // changed from 16 to 32 to better account for ogl models
|
||||
followers[numfollowers].height = 16;
|
||||
followers[numfollowers].zoffs = 32;
|
||||
followers[numfollowers].horzlag = 2;
|
||||
followers[numfollowers].vertlag = 6;
|
||||
followers[numfollowers].bubblescale = 0; // No bubble by default
|
||||
followers[numfollowers].atangle = FixedAngle(230 * FRACUNIT);
|
||||
followers[numfollowers].dist = 32*FRACUNIT; // changed from 16 to 32 to better account for ogl models
|
||||
followers[numfollowers].height = 16*FRACUNIT;
|
||||
followers[numfollowers].zoffs = 32*FRACUNIT;
|
||||
followers[numfollowers].horzlag = 2*FRACUNIT;
|
||||
followers[numfollowers].vertlag = 6*FRACUNIT;
|
||||
followers[numfollowers].bobspeed = TICRATE*2;
|
||||
followers[numfollowers].bobamp = 4;
|
||||
followers[numfollowers].bobamp = 4*FRACUNIT;
|
||||
followers[numfollowers].hitconfirmtime = TICRATE;
|
||||
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
|
||||
|
||||
|
|
@ -3865,48 +3866,47 @@ void readfollower(MYFILE *f)
|
|||
}
|
||||
else if (fastcmp(word, "DEFAULTCOLOR"))
|
||||
{
|
||||
followers[numfollowers].defaultcolor = (UINT16)get_number(word2);
|
||||
followers[numfollowers].defaultcolor = get_number(word2);
|
||||
}
|
||||
|
||||
else if (fastcmp(word, "SCALE"))
|
||||
{
|
||||
followers[numfollowers].scale = get_number(word2);
|
||||
followers[numfollowers].scale = (fixed_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "BUBBLESCALE"))
|
||||
{
|
||||
followers[numfollowers].bubblescale = get_number(word2);
|
||||
followers[numfollowers].bubblescale = (fixed_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "ATANGLE"))
|
||||
{
|
||||
followers[numfollowers].atangle = (INT32)atoi(word2);
|
||||
followers[numfollowers].atangle = (angle_t)(get_number(word2) * ANG1);
|
||||
}
|
||||
else if (fastcmp(word, "HORZLAG"))
|
||||
{
|
||||
followers[numfollowers].horzlag = (INT32)atoi(word2);
|
||||
followers[numfollowers].horzlag = (fixed_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "VERTLAG"))
|
||||
{
|
||||
followers[numfollowers].vertlag = (INT32)atoi(word2);
|
||||
followers[numfollowers].vertlag = (fixed_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "BOBSPEED"))
|
||||
{
|
||||
followers[numfollowers].bobspeed = (INT32)atoi(word2);
|
||||
followers[numfollowers].bobspeed = (tic_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "BOBAMP"))
|
||||
{
|
||||
followers[numfollowers].bobamp = (INT32)atoi(word2);
|
||||
followers[numfollowers].bobamp = (fixed_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "ZOFFSET") || (fastcmp(word, "ZOFFS")))
|
||||
{
|
||||
followers[numfollowers].zoffs = (INT32)atoi(word2);
|
||||
followers[numfollowers].zoffs = (fixed_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "DISTANCE") || (fastcmp(word, "DIST")))
|
||||
{
|
||||
followers[numfollowers].dist = (INT32)atoi(word2);
|
||||
followers[numfollowers].dist = (fixed_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "HEIGHT"))
|
||||
{
|
||||
followers[numfollowers].height = (INT32)atoi(word2);
|
||||
followers[numfollowers].height = (fixed_t)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "IDLESTATE"))
|
||||
{
|
||||
|
|
@ -3947,16 +3947,19 @@ void readfollower(MYFILE *f)
|
|||
}
|
||||
else if (fastcmp(word, "HITTIME") || (fastcmp(word, "HITCONFIRMTIME")))
|
||||
{
|
||||
followers[numfollowers].hitconfirmtime = (INT32)atoi(word2);
|
||||
followers[numfollowers].hitconfirmtime = (tic_t)get_number(word2);
|
||||
}
|
||||
else
|
||||
{
|
||||
deh_warning("Follower %d: unknown word '%s'", numfollowers, word);
|
||||
}
|
||||
}
|
||||
} while (!myfeof(f)); // finish when the line is empty
|
||||
|
||||
if (!nameset) // well this is problematic.
|
||||
if (!nameset)
|
||||
{
|
||||
strcpy(followers[numfollowers].name, va("Follower%d", numfollowers)); // this is lazy, so what
|
||||
// well this is problematic.
|
||||
strcpy(followers[numfollowers].name, va("Follower%d", numfollowers)); // this is lazy, so what
|
||||
}
|
||||
|
||||
// set skin name (this is just the follower's name in lowercases):
|
||||
|
|
@ -3966,7 +3969,7 @@ void readfollower(MYFILE *f)
|
|||
|
||||
// lower testname for skin checks...
|
||||
strlwr(testname);
|
||||
res = R_FollowerAvailable(testname);
|
||||
res = K_FollowerAvailable(testname);
|
||||
if (res > -1) // yikes, someone else has stolen our name already
|
||||
{
|
||||
INT32 startlen = strlen(testname);
|
||||
|
|
@ -3990,7 +3993,7 @@ void readfollower(MYFILE *f)
|
|||
// fallbacks for variables
|
||||
// Print a warning if the variable is on a weird value and set it back to the minimum available if that's the case.
|
||||
#define FALLBACK(field, field2, threshold, set) \
|
||||
if (followers[numfollowers].field < threshold) \
|
||||
if ((signed)followers[numfollowers].field < threshold) \
|
||||
{ \
|
||||
followers[numfollowers].field = set; \
|
||||
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", dname, field2, set, set); \
|
||||
|
|
@ -4008,7 +4011,7 @@ if (followers[numfollowers].field < threshold) \
|
|||
FALLBACK(bubblescale, "BUBBLESCALE", 0, 0); // No negative scale
|
||||
|
||||
// Special case for color I suppose
|
||||
if (followers[numfollowers].defaultcolor > numskincolors-1)
|
||||
if (followers[numfollowers].defaultcolor > (unsigned)(numskincolors-1))
|
||||
{
|
||||
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
|
||||
deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, numskincolors-1);
|
||||
|
|
@ -4036,7 +4039,7 @@ if (!followers[numfollowers].field) \
|
|||
#undef NOSTATE
|
||||
|
||||
CONS_Printf("Added follower '%s'\n", dname);
|
||||
numfollowers++; // add 1 follower
|
||||
numfollowers++; // add 1 follower
|
||||
Z_Free(s);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ extern char logfilename[1024];
|
|||
#define MAXPLAYERNAME 21
|
||||
#define MAXSPLITSCREENPLAYERS 4 // Max number of players on a single computer
|
||||
|
||||
#define MAXSKINS 128
|
||||
#define MAXSKINS UINT8_MAX
|
||||
|
||||
#define COLORRAMPSIZE 16
|
||||
#define MAXCOLORNAME 32
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include "k_respawn.h"
|
||||
#include "k_bot.h"
|
||||
#include "k_color.h"
|
||||
#include "k_follower.h"
|
||||
|
||||
static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}};
|
||||
consvar_t cv_recordmultiplayerdemos = CVAR_INIT ("netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL);
|
||||
|
|
@ -300,7 +301,7 @@ void G_ReadDemoExtraData(void)
|
|||
// Set our follower
|
||||
M_Memcpy(name, demo_p, 16);
|
||||
demo_p += 16;
|
||||
SetPlayerFollower(p, name);
|
||||
K_SetFollowerByName(p, name);
|
||||
|
||||
// Follower's color
|
||||
M_Memcpy(name, demo_p, 16);
|
||||
|
|
@ -3057,7 +3058,7 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
// Follower
|
||||
M_Memcpy(follower, demo_p, 16);
|
||||
demo_p += 16;
|
||||
SetPlayerFollower(p, follower);
|
||||
K_SetFollowerByName(p, follower);
|
||||
|
||||
// Follower colour
|
||||
M_Memcpy(color, demo_p, 16);
|
||||
|
|
|
|||
462
src/k_follower.c
Normal file
462
src/k_follower.c
Normal file
|
|
@ -0,0 +1,462 @@
|
|||
|
||||
#include "k_follower.h"
|
||||
|
||||
#include "k_kart.h"
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "doomdef.h"
|
||||
#include "g_game.h"
|
||||
#include "g_demo.h"
|
||||
#include "r_skins.h"
|
||||
#include "p_local.h"
|
||||
#include "p_mobj.h"
|
||||
|
||||
INT32 numfollowers = 0;
|
||||
follower_t followers[MAXSKINS];
|
||||
|
||||
CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL
|
||||
|
||||
/*--------------------------------------------------
|
||||
INT32 K_FollowerAvailable(const char *name)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
INT32 K_FollowerAvailable(const char *name)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < numfollowers; i++)
|
||||
{
|
||||
if (stricmp(followers[i].skinname, name) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
|
||||
{
|
||||
INT32 i;
|
||||
player_t *player = &players[playernum];
|
||||
|
||||
if (stricmp("None", skinname) == 0)
|
||||
{
|
||||
K_SetFollowerByNum(playernum, -1); // reminder that -1 is nothing
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < numfollowers; i++)
|
||||
{
|
||||
// search in the skin list
|
||||
if (stricmp(followers[i].skinname, skinname) == 0)
|
||||
{
|
||||
K_SetFollowerByNum(playernum, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (P_IsLocalPlayer(player))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found.\n"), skinname);
|
||||
}
|
||||
else if (server || IsPlayerAdmin(consoleplayer))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) follower '%s' not found\n"), playernum, player_names[playernum], skinname);
|
||||
}
|
||||
|
||||
K_SetFollowerByNum(playernum, -1); // reminder that -1 is nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
|
||||
{
|
||||
player_t *player = &players[playernum];
|
||||
mobj_t *bub;
|
||||
mobj_t *tmp;
|
||||
|
||||
player->followerready = true; // we are ready to perform follower related actions in the player thinker, now.
|
||||
|
||||
if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists!
|
||||
{
|
||||
/*
|
||||
We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself.
|
||||
However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it.
|
||||
*/
|
||||
|
||||
if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins
|
||||
{
|
||||
// Remove follower's possible hnext list (bubble)
|
||||
bub = player->follower->hnext;
|
||||
|
||||
while (bub && !P_MobjWasRemoved(bub))
|
||||
{
|
||||
tmp = bub->hnext;
|
||||
P_RemoveMobj(bub);
|
||||
bub = tmp;
|
||||
}
|
||||
|
||||
P_RemoveMobj(player->follower);
|
||||
P_SetTarget(&player->follower, NULL);
|
||||
}
|
||||
|
||||
player->followerskin = skinnum;
|
||||
|
||||
// for replays: We have changed our follower mid-game; let the game know so it can do the same in the replay!
|
||||
demo_extradata[playernum] |= DXD_FOLLOWER;
|
||||
return;
|
||||
}
|
||||
|
||||
if (P_IsLocalPlayer(player))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower %d not found\n"), skinnum);
|
||||
}
|
||||
else if (server || IsPlayerAdmin(consoleplayer))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "Player %d (%s) follower %d not found\n", playernum, player_names[playernum], skinnum);
|
||||
}
|
||||
|
||||
K_SetFollowerByNum(playernum, -1); // Not found, then set -1 (nothing) as our follower.
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
static void K_SetFollowerState(mobj_t *f, statenum_t state)
|
||||
|
||||
Sets a follower object's state.
|
||||
This is done as a separate function to prevent running follower actions.
|
||||
|
||||
Input Arguments:-
|
||||
f - The follower's mobj_t.
|
||||
state - The state to set.
|
||||
|
||||
Return:-
|
||||
None
|
||||
--------------------------------------------------*/
|
||||
static void K_SetFollowerState(mobj_t *f, statenum_t state)
|
||||
{
|
||||
if (f == NULL || P_MobjWasRemoved(f) == true)
|
||||
{
|
||||
// safety net
|
||||
return;
|
||||
}
|
||||
|
||||
// No, do NOT set the follower to S_NULL. Set it to S_INVISIBLE.
|
||||
if (state == S_NULL)
|
||||
{
|
||||
state = S_INVISIBLE;
|
||||
f->threshold = 1; // Threshold = 1 means stop doing anything related to setting states, so that we don't get out of S_INVISIBLE
|
||||
}
|
||||
|
||||
// extravalue2 stores the last "first state" we used.
|
||||
// because states default to idlestates, if we use an animation that uses an "ongoing" state line, don't reset it!
|
||||
// this prevents it from looking very dumb
|
||||
if (state == (statenum_t)f->extravalue2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// we will save the state into extravalue2.
|
||||
f->extravalue2 = state;
|
||||
|
||||
P_SetMobjStateNF(f, state);
|
||||
if (f->state->tics > 0)
|
||||
{
|
||||
f->tics++;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_HandleFollower(player_t *player)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_HandleFollower(player_t *player)
|
||||
{
|
||||
follower_t fl;
|
||||
angle_t an;
|
||||
fixed_t zoffs;
|
||||
fixed_t sx, sy, sz, deltaz;
|
||||
UINT16 color;
|
||||
|
||||
fixed_t bubble; // bubble scale (0 if no bubble)
|
||||
mobj_t *bmobj; // temp bubble mobj
|
||||
|
||||
if (player->followerready == false)
|
||||
{
|
||||
// we aren't ready to perform anything follower related yet.
|
||||
return;
|
||||
}
|
||||
|
||||
// How about making sure our follower exists and is added before trying to spawn it n' all?
|
||||
if (player->followerskin > numfollowers-1 || player->followerskin < -1)
|
||||
{
|
||||
//CONS_Printf("Follower skin invlaid. Setting to -1.\n");
|
||||
player->followerskin = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// don't do anything if we can't have a follower to begin with.
|
||||
// (It gets removed under those conditions)
|
||||
if (player->spectator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->followerskin < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Before we do anything, let's be sure of where we're supposed to be
|
||||
fl = followers[player->followerskin];
|
||||
|
||||
an = player->mo->angle + fl.atangle;
|
||||
zoffs = fl.zoffs;
|
||||
bubble = fl.bubblescale; // 0 if no bubble to spawn.
|
||||
|
||||
// do you like angle maths? I certainly don't...
|
||||
sx = player->mo->x + FixedMul(FixedMul(player->mo->scale, fl.dist), FINECOSINE((an) >> ANGLETOFINESHIFT));
|
||||
sy = player->mo->y + FixedMul(FixedMul(player->mo->scale, fl.dist), FINESINE((an) >> ANGLETOFINESHIFT));
|
||||
|
||||
// interp info helps with stretchy fix
|
||||
deltaz = (player->mo->z - player->mo->old_z);
|
||||
|
||||
// for the z coordinate, don't be a doof like Steel and forget that MFE_VERTICALFLIP exists :P
|
||||
sz = player->mo->z + FixedMul(player->mo->scale, zoffs) * P_MobjFlip(player->mo);
|
||||
if (player->mo->eflags & MFE_VERTICALFLIP)
|
||||
{
|
||||
sz += FixedMul(fl.height, player->mo->scale);
|
||||
}
|
||||
|
||||
// finally, add a cool floating effect to the z height.
|
||||
// not stolen from k_kart I swear!!
|
||||
{
|
||||
const fixed_t pi = (22<<FRACBITS) / 7; // loose approximation, this doesn't need to be incredibly precise
|
||||
fixed_t sine = FixedMul(fl.bobamp, FINESINE((((8 * pi * fl.bobspeed) * leveltime) >> ANGLETOFINESHIFT) & FINEMASK));
|
||||
sz += FixedMul(player->mo->scale, sine) * P_MobjFlip(player->mo);
|
||||
}
|
||||
|
||||
// Set follower colour
|
||||
switch (player->followercolor)
|
||||
{
|
||||
case FOLLOWERCOLOR_MATCH: // "Match"
|
||||
color = player->skincolor;
|
||||
break;
|
||||
|
||||
case FOLLOWERCOLOR_OPPOSITE: // "Opposite"
|
||||
color = skincolors[player->skincolor].invcolor;
|
||||
break;
|
||||
|
||||
default:
|
||||
color = player->followercolor;
|
||||
if (color == 0 || color > MAXSKINCOLORS+2) // Make sure this isn't garbage
|
||||
{
|
||||
color = player->skincolor; // "Match" as fallback.
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (player->follower == NULL) // follower doesn't exist / isn't valid
|
||||
{
|
||||
//CONS_Printf("Spawning follower...\n");
|
||||
|
||||
// so let's spawn one!
|
||||
P_SetTarget(&player->follower, P_SpawnMobj(sx, sy, sz, MT_FOLLOWER));
|
||||
K_SetFollowerState(player->follower, fl.idlestate);
|
||||
P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear
|
||||
P_InitAngle(player->follower, player->mo->angle);
|
||||
|
||||
// This is safe to only spawn it here, the follower is removed then respawned when switched.
|
||||
if (bubble)
|
||||
{
|
||||
bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_FRONT);
|
||||
P_SetTarget(&player->follower->hnext, bmobj);
|
||||
P_SetTarget(&bmobj->target, player->follower); // Used to know if we have to despawn at some point.
|
||||
|
||||
bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_BACK);
|
||||
P_SetTarget(&player->follower->hnext->hnext, bmobj); // this seems absolutely stupid, I know, but this will make updating the momentums/flags of these a bit easier.
|
||||
P_SetTarget(&bmobj->target, player->follower); // Ditto
|
||||
}
|
||||
|
||||
player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use.
|
||||
/*
|
||||
0 = idle
|
||||
1 = forwards
|
||||
2 = hurt
|
||||
3 = win
|
||||
4 = lose
|
||||
5 = hitconfirm (< this one uses ->movecount as timer to know when to end, and goes back to normal states afterwards, unless hurt)
|
||||
*/
|
||||
}
|
||||
else // follower exists, woo!
|
||||
{
|
||||
// Safety net (2)
|
||||
|
||||
if (P_MobjWasRemoved(player->follower))
|
||||
{
|
||||
P_SetTarget(&player->follower, NULL); // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing.
|
||||
return;
|
||||
}
|
||||
|
||||
// first of all, handle states following the same model as above:
|
||||
if (player->follower->tics == 1)
|
||||
{
|
||||
K_SetFollowerState(player->follower, player->follower->state->nextstate);
|
||||
}
|
||||
|
||||
// move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)!
|
||||
// 02/09/2021: cast lag to int32 otherwise funny things happen since it was changed to uint32 in the struct
|
||||
player->follower->momx = FixedDiv(sx - player->follower->x, fl.horzlag);
|
||||
player->follower->momy = FixedDiv(sy - player->follower->y, fl.horzlag);
|
||||
player->follower->z += FixedDiv(deltaz, fl.vertlag);
|
||||
player->follower->momz = FixedDiv(sz - player->follower->z, fl.vertlag);
|
||||
player->follower->angle = player->mo->angle;
|
||||
|
||||
if (player->mo->colorized)
|
||||
{
|
||||
player->follower->color = player->mo->color;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->follower->color = color;
|
||||
}
|
||||
|
||||
player->follower->colorized = player->mo->colorized;
|
||||
|
||||
P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale));
|
||||
K_GenericExtraFlagsNoZAdjust(player->follower, player->mo); // Not K_MatchGenericExtraFlag because the Z adjust it has only works properly if master & mo have the same Z height.
|
||||
|
||||
// Match how the player is being drawn
|
||||
player->follower->renderflags = player->mo->renderflags;
|
||||
|
||||
// Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously.
|
||||
if (player->pflags & PF_NOCONTEST)
|
||||
{
|
||||
player->follower->renderflags |= RF_DONTDRAW;
|
||||
}
|
||||
|
||||
// if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward.
|
||||
player->follower->angle = K_MomentumAngle(player->follower);
|
||||
|
||||
// Finally, if the follower has bubbles, move them, set their scale, etc....
|
||||
// This is what I meant earlier by it being easier, now we can just use this weird lil loop to get the job done!
|
||||
|
||||
bmobj = player->follower->hnext; // will be NULL if there's no bubble
|
||||
|
||||
while (bmobj != NULL && P_MobjWasRemoved(bmobj) == false)
|
||||
{
|
||||
// match follower's momentums and (e)flags(2).
|
||||
bmobj->momx = player->follower->momx;
|
||||
bmobj->momy = player->follower->momy;
|
||||
bmobj->z += FixedDiv(deltaz, fl.vertlag);
|
||||
bmobj->momz = player->follower->momz;
|
||||
|
||||
P_SetScale(bmobj, FixedMul(bubble, player->mo->scale));
|
||||
K_GenericExtraFlagsNoZAdjust(bmobj, player->follower);
|
||||
bmobj->renderflags = player->mo->renderflags;
|
||||
|
||||
if (player->follower->threshold)
|
||||
{
|
||||
// threshold means the follower was "despawned" with S_NULL (is actually just set to S_INVISIBLE)
|
||||
P_SetMobjState(bmobj, S_INVISIBLE); // sooooo... let's do the same!
|
||||
}
|
||||
|
||||
// switch to other bubble layer or exit
|
||||
bmobj = bmobj->hnext;
|
||||
}
|
||||
|
||||
if (player->follower->threshold)
|
||||
{
|
||||
// Threshold means the follower was "despanwed" with S_NULL.
|
||||
return;
|
||||
}
|
||||
|
||||
// However with how the code is factored, this is just a special case of S_INVISBLE to avoid having to add other player variables.
|
||||
|
||||
// handle follower animations. Could probably be better...
|
||||
// hurt or dead
|
||||
if (P_PlayerInPain(player) == true || player->mo->state == &states[S_KART_SPINOUT] || player->mo->health <= 0)
|
||||
{
|
||||
// cancel hit confirm.
|
||||
player->follower->movecount = 0;
|
||||
|
||||
// spin out
|
||||
player->follower->angle = player->drawangle;
|
||||
|
||||
if (player->follower->extravalue1 != 2)
|
||||
{
|
||||
player->follower->extravalue1 = 2;
|
||||
K_SetFollowerState(player->follower, fl.hurtstate);
|
||||
}
|
||||
|
||||
if (player->mo->health <= 0)
|
||||
{
|
||||
// if dead, follow the player's z momentum exactly so they both look like they die at the same speed.
|
||||
player->follower->momz = player->mo->momz;
|
||||
}
|
||||
}
|
||||
else if (player->follower->movecount)
|
||||
{
|
||||
if (player->follower->extravalue1 != 5)
|
||||
{
|
||||
player->follower->extravalue1 = 5;
|
||||
K_SetFollowerState(player->follower, fl.hitconfirmstate);
|
||||
}
|
||||
|
||||
player->follower->movecount--;
|
||||
}
|
||||
else if (player->speed > 10*player->mo->scale) // animation for moving fast enough
|
||||
{
|
||||
if (player->follower->extravalue1 != 1)
|
||||
{
|
||||
player->follower->extravalue1 = 1;
|
||||
K_SetFollowerState(player->follower, fl.followstate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// animations when nearly still. This includes winning and losing.
|
||||
if (player->follower->extravalue1 != 0)
|
||||
{
|
||||
if (player->exiting)
|
||||
{
|
||||
// win/ loss animations
|
||||
if (K_IsPlayerLosing(player))
|
||||
{
|
||||
// L
|
||||
if (player->follower->extravalue1 != 4)
|
||||
{
|
||||
player->follower->extravalue1 = 4;
|
||||
K_SetFollowerState(player->follower, fl.losestate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// W
|
||||
if (player->follower->extravalue1 != 3)
|
||||
{
|
||||
player->follower->extravalue1 = 3;
|
||||
K_SetFollowerState(player->follower, fl.winstate);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal standstill
|
||||
player->follower->extravalue1 = 0;
|
||||
K_SetFollowerState(player->follower, fl.idlestate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
src/k_follower.h
Normal file
133
src/k_follower.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018-2022 by "Lat'"
|
||||
// Copyright (C) 2018-2022 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file k_follower.h
|
||||
/// \brief Code relating to the follower system
|
||||
|
||||
#ifndef __K_FOLLOWER__
|
||||
#define __K_FOLLOWER__
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "r_skins.h"
|
||||
|
||||
#define FOLLOWERCOLOR_MATCH UINT16_MAX
|
||||
#define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1)
|
||||
|
||||
extern CV_PossibleValue_t Followercolor_cons_t[]; // follower colours table, not a duplicate because of the "Match" option.
|
||||
|
||||
//
|
||||
// We'll define these here because they're really just a mobj that'll follow some rules behind a player
|
||||
//
|
||||
typedef struct follower_s
|
||||
{
|
||||
char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything.
|
||||
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
|
||||
|
||||
skincolornum_t defaultcolor; // default color for menus.
|
||||
|
||||
fixed_t scale; // Scale relative to the player's.
|
||||
fixed_t bubblescale; // Bubble scale relative to the player scale. If not set, no bubble will spawn (default)
|
||||
|
||||
// some position shenanigans:
|
||||
angle_t atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player.
|
||||
fixed_t dist; // distance relative to the player. (In a circle)
|
||||
fixed_t height; // height of the follower, this is mostly important for Z flipping.
|
||||
fixed_t zoffs; // Z offset relative to the player's height. Cannot be negative.
|
||||
|
||||
// movement options
|
||||
|
||||
fixed_t horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number.
|
||||
fixed_t vertlag; // Z displacement lag. Default is 6. Must be > 0 because we divide by this number.
|
||||
fixed_t bobamp; // Bob amplitude. Default is 4.
|
||||
tic_t bobspeed; // Arbitrary modifier for bobbing speed. Default is TICRATE*2 (70)
|
||||
|
||||
// from there on out, everything is STATES to allow customization
|
||||
// these are only set once when the action is performed and are then free to animate however they want.
|
||||
|
||||
statenum_t idlestate; // state when the player is at a standstill
|
||||
statenum_t followstate; // state when the player is moving
|
||||
statenum_t hurtstate; // state when the player is being hurt
|
||||
statenum_t winstate; // state when the player has won
|
||||
statenum_t losestate; // state when the player has lost
|
||||
statenum_t hitconfirmstate; // state for hit confirm
|
||||
tic_t hitconfirmtime; // time to keep the above playing for
|
||||
} follower_t;
|
||||
|
||||
extern INT32 numfollowers;
|
||||
extern follower_t followers[MAXSKINS];
|
||||
|
||||
/*--------------------------------------------------
|
||||
INT32 K_FollowerAvailable(const char *name)
|
||||
|
||||
Check if a follower with the specified name
|
||||
exists or not.
|
||||
|
||||
Input Arguments:-
|
||||
name - The skin name of the follower to check for.
|
||||
|
||||
Return:-
|
||||
The follower numerical ID of the follower,
|
||||
or -1 if it doesn't exist.
|
||||
--------------------------------------------------*/
|
||||
|
||||
INT32 K_FollowerAvailable(const char *name);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_SetFollowerByName(INT32 playernum, const char *skinname)
|
||||
|
||||
Updates a player's follower type via a named value.
|
||||
Calls "K_SetFollowerByNum" internally.
|
||||
|
||||
Input Arguments:-
|
||||
playernum - The player ID to update
|
||||
skinname - The follower's skin name
|
||||
|
||||
Return:-
|
||||
true if it was a valid name for a follower,
|
||||
otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_SetFollowerByName(INT32 playernum, const char *skinname);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum)
|
||||
|
||||
Updates a player's follower type via a numerical ID.
|
||||
|
||||
Input Arguments:-
|
||||
playernum - The player ID to update.
|
||||
skinnum - The follower's skin ID
|
||||
|
||||
Return:-
|
||||
None
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_SetFollowerByNum(INT32 playernum, INT32 skinnum);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_HandleFollower(player_t *player)
|
||||
|
||||
Updates a player's follower pointer, and does
|
||||
its positioning and animations.
|
||||
|
||||
Input Arguments:-
|
||||
player - The player who we want to update the follower of.
|
||||
|
||||
Return:-
|
||||
None
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_HandleFollower(player_t *player);
|
||||
|
||||
|
||||
#endif // __K_FOLLOWER__
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
#include "k_terrain.h"
|
||||
#include "k_director.h"
|
||||
#include "k_collide.h"
|
||||
#include "k_follower.h"
|
||||
|
||||
// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H:
|
||||
// gamespeed is cc (0 for easy, 1 for normal, 2 for hard)
|
||||
|
|
@ -2806,7 +2807,7 @@ void K_PlayHitEmSound(mobj_t *source, mobj_t *victim)
|
|||
if (source->player->follower)
|
||||
{
|
||||
follower_t fl = followers[source->player->followerskin];
|
||||
source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers.
|
||||
source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers.
|
||||
}
|
||||
|
||||
if (cv_kartvoices.value)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include "d_player.h" // KITEM_ constants
|
||||
#include "k_color.h"
|
||||
#include "k_grandprix.h"
|
||||
#include "k_follower.h"
|
||||
#include "r_fps.h"
|
||||
|
||||
#include "i_joy.h" // for joystick menu controls
|
||||
|
|
@ -9667,7 +9668,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
|
|||
follower_t fl = followers[setupm_fakefollower]; // shortcut for our sanity
|
||||
// smooth floating, totally not stolen from rocket sneakers.
|
||||
const fixed_t pi = (22<<FRACBITS) / 7; // loose approximation, this doesn't need to be incredibly precise
|
||||
fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * followertimer)>>ANGLETOFINESHIFT) & FINEMASK);
|
||||
fixed_t sine = FixedMul(fl.bobamp, FINESINE((((8 * pi * (fl.bobspeed)) * followertimer)>>ANGLETOFINESHIFT) & FINEMASK));
|
||||
|
||||
UINT8 *colormap = R_GetTranslationColormap(-1, setupm_fakecolor->color, 0);
|
||||
V_DrawFixedPatch((mx+65)*FRACUNIT, (my+131-fl.zoffs)*FRACUNIT+sine, fl.scale, flags, patch, colormap);
|
||||
|
|
|
|||
291
src/p_user.c
291
src/p_user.c
|
|
@ -56,6 +56,7 @@
|
|||
#include "k_boss.h"
|
||||
#include "k_terrain.h" // K_SpawnSplashForMobj
|
||||
#include "k_color.h"
|
||||
#include "k_follower.h"
|
||||
|
||||
#ifdef HW3SOUND
|
||||
#include "hardware/hw3sound.h"
|
||||
|
|
@ -3961,289 +3962,6 @@ static void P_ParabolicMove(mobj_t *mo, fixed_t x, fixed_t y, fixed_t z, fixed_t
|
|||
|
||||
#endif
|
||||
|
||||
/* set follower state with our weird hacks
|
||||
the reason we do this is to avoid followers ever using actions (majormods, yikes!)
|
||||
without having to touch p_mobj.c.
|
||||
so we give it 1more tic and change the state when tic == 1 instead of 0
|
||||
cool beans?
|
||||
cool beans.
|
||||
*/
|
||||
static void P_SetFollowerState(mobj_t *f, INT32 state)
|
||||
{
|
||||
|
||||
if (!f || P_MobjWasRemoved(f))
|
||||
return; // safety net
|
||||
|
||||
// No, do NOT set the follower to S_NULL. Set it to S_INVISIBLE.
|
||||
if (state == S_NULL)
|
||||
{
|
||||
state = S_INVISIBLE;
|
||||
f->threshold = 1; // Threshold = 1 means stop doing anything related to setting states, so that we don't get out of S_INVISIBLE
|
||||
}
|
||||
|
||||
// extravalue2 stores the last "first state" we used.
|
||||
// because states default to idlestates, if we use an animation that uses an "ongoing" state line, don't reset it!
|
||||
// this prevents it from looking very dumb
|
||||
if (state == f->extravalue2)
|
||||
return;
|
||||
|
||||
// we will save the state into extravalue2.
|
||||
f->extravalue2 = state;
|
||||
|
||||
P_SetMobjStateNF(f, state);
|
||||
if (f->state->tics > 0)
|
||||
f->tics++;
|
||||
}
|
||||
|
||||
//
|
||||
//P_HandleFollower
|
||||
//
|
||||
//Handle the follower's spawning and moving along with the player. Do note that some of the stuff like the removal if a player doesn't exist anymore is handled in MT_FOLLOWER's thinker.
|
||||
static void P_HandleFollower(player_t *player)
|
||||
{
|
||||
follower_t fl;
|
||||
angle_t an;
|
||||
fixed_t zoffs;
|
||||
fixed_t sx, sy, sz, deltaz;
|
||||
UINT16 color;
|
||||
|
||||
fixed_t bubble; // bubble scale (0 if no bubble)
|
||||
mobj_t *bmobj; // temp bubble mobj
|
||||
|
||||
|
||||
if (!player->followerready)
|
||||
return; // we aren't ready to perform anything follower related yet.
|
||||
|
||||
// How about making sure our follower exists and is added before trying to spawn it n' all?
|
||||
if (player->followerskin > numfollowers-1 || player->followerskin < -1)
|
||||
{
|
||||
//CONS_Printf("Follower skin invlaid. Setting to -1.\n");
|
||||
player->followerskin = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// don't do anything if we can't have a follower to begin with. (It gets removed under those conditions)
|
||||
if (player->spectator)
|
||||
return;
|
||||
if (player->followerskin < 0)
|
||||
return;
|
||||
// Before we do anything, let's be sure of where we're supposed to be
|
||||
fl = followers[player->followerskin];
|
||||
|
||||
an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things...
|
||||
zoffs = (fl.zoffs)*FRACUNIT;
|
||||
bubble = fl.bubblescale; // 0 if no bubble to spawn.
|
||||
|
||||
// do you like angle maths? I certainly don't...
|
||||
sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT));
|
||||
sy = player->mo->y + FixedMul((player->mo->scale*fl.dist), FINESINE((an)>>ANGLETOFINESHIFT));
|
||||
|
||||
// interp info helps with stretchy fix
|
||||
deltaz = (player->mo->z - player->mo->old_z);
|
||||
|
||||
// for the z coordinate, don't be a doof like Steel and forget that MFE_VERTICALFLIP exists :P
|
||||
sz = player->mo->z + FixedMul(player->mo->scale, zoffs)*P_MobjFlip(player->mo);
|
||||
if (player->mo->eflags & MFE_VERTICALFLIP)
|
||||
sz += fl.height*player->mo->scale;
|
||||
|
||||
// finally, add a cool floating effect to the z height.
|
||||
// not stolen from k_kart I swear!!
|
||||
{
|
||||
const fixed_t pi = (22<<FRACBITS) / 7; // loose approximation, this doesn't need to be incredibly precise
|
||||
fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK);
|
||||
sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo);
|
||||
}
|
||||
|
||||
// Set follower colour
|
||||
switch (player->followercolor)
|
||||
{
|
||||
case FOLLOWERCOLOR_MATCH: // "Match"
|
||||
color = player->skincolor;
|
||||
break;
|
||||
case FOLLOWERCOLOR_OPPOSITE: // "Opposite"
|
||||
color = skincolors[player->skincolor].invcolor;
|
||||
break;
|
||||
default:
|
||||
|
||||
color = player->followercolor;
|
||||
if (!color || color > MAXSKINCOLORS+2) // Make sure this isn't garbage
|
||||
color = player->skincolor; // "Match" as fallback.
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!player->follower) // follower doesn't exist / isn't valid
|
||||
{
|
||||
//CONS_Printf("Spawning follower...\n");
|
||||
// so let's spawn one!
|
||||
P_SetTarget(&player->follower, P_SpawnMobj(sx, sy, sz, MT_FOLLOWER));
|
||||
P_SetFollowerState(player->follower, fl.idlestate);
|
||||
P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear
|
||||
P_InitAngle(player->follower, player->mo->angle);
|
||||
|
||||
// This is safe to only spawn it here, the follower is removed then respawned when switched.
|
||||
if (bubble)
|
||||
{
|
||||
bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_FRONT);
|
||||
P_SetTarget(&player->follower->hnext, bmobj);
|
||||
P_SetTarget(&bmobj->target, player->follower); // Used to know if we have to despawn at some point.
|
||||
|
||||
bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_BACK);
|
||||
P_SetTarget(&player->follower->hnext->hnext, bmobj); // this seems absolutely stupid, I know, but this will make updating the momentums/flags of these a bit easier.
|
||||
P_SetTarget(&bmobj->target, player->follower); // Ditto
|
||||
}
|
||||
|
||||
player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use.
|
||||
/*
|
||||
0 = idle
|
||||
1 = forwards
|
||||
2 = hurt
|
||||
3 = win
|
||||
4 = lose
|
||||
5 = hitconfirm (< this one uses ->movecount as timer to know when to end, and goes back to normal states afterwards, unless hurt)
|
||||
*/
|
||||
}
|
||||
else // follower exists, woo!
|
||||
{
|
||||
|
||||
// Safety net (2)
|
||||
|
||||
if (P_MobjWasRemoved(player->follower))
|
||||
{
|
||||
P_SetTarget(&player->follower, NULL); // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing.
|
||||
return;
|
||||
}
|
||||
|
||||
// first of all, handle states following the same model as above:
|
||||
if (player->follower->tics == 1)
|
||||
P_SetFollowerState(player->follower, player->follower->state->nextstate);
|
||||
|
||||
// move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)!
|
||||
// 02/09/2021: cast lag to int32 otherwise funny things happen since it was changed to uint32 in the struct
|
||||
player->follower->momx = (sx - player->follower->x)/ (INT32)fl.horzlag;
|
||||
player->follower->momy = (sy - player->follower->y)/ (INT32)fl.horzlag;
|
||||
player->follower->z += (deltaz/ (INT32)fl.vertlag);
|
||||
player->follower->momz = (sz - player->follower->z)/ (INT32)fl.vertlag;
|
||||
player->follower->angle = player->mo->angle;
|
||||
|
||||
if (player->mo->colorized)
|
||||
player->follower->color = player->mo->color;
|
||||
else
|
||||
player->follower->color = color;
|
||||
|
||||
player->follower->colorized = player->mo->colorized;
|
||||
|
||||
P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale));
|
||||
K_GenericExtraFlagsNoZAdjust(player->follower, player->mo); // Not K_MatchGenericExtraFlag because the Z adjust it has only works properly if master & mo have the same Z height.
|
||||
|
||||
// Match how the player is being drawn
|
||||
player->follower->renderflags = player->mo->renderflags;
|
||||
|
||||
// Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously.
|
||||
if (player->pflags & PF_NOCONTEST)
|
||||
player->follower->renderflags |= RF_DONTDRAW;
|
||||
|
||||
// if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward.
|
||||
player->follower->angle = K_MomentumAngle(player->follower);
|
||||
|
||||
// Finally, if the follower has bubbles, move them, set their scale, etc....
|
||||
// This is what I meant earlier by it being easier, now we can just use this weird lil loop to get the job done!
|
||||
|
||||
bmobj = player->follower->hnext; // will be NULL if there's no bubble
|
||||
|
||||
while (bmobj && !P_MobjWasRemoved(bmobj))
|
||||
{
|
||||
// match follower's momentums and (e)flags(2).
|
||||
bmobj->momx = player->follower->momx;
|
||||
bmobj->momy = player->follower->momy;
|
||||
bmobj->z += (deltaz/ (INT32)fl.vertlag);
|
||||
bmobj->momz = player->follower->momz;
|
||||
|
||||
P_SetScale(bmobj, FixedMul(bubble, player->mo->scale));
|
||||
K_GenericExtraFlagsNoZAdjust(bmobj, player->follower);
|
||||
bmobj->renderflags = player->mo->renderflags;
|
||||
|
||||
if (player->follower->threshold) // threshold means the follower was "despawned" with S_NULL (is actually just set to S_INVISIBLE)
|
||||
P_SetMobjState(bmobj, S_INVISIBLE); // sooooo... let's do the same!
|
||||
|
||||
bmobj = bmobj->hnext; // switch to other bubble layer or exit
|
||||
}
|
||||
|
||||
|
||||
if (player->follower->threshold)
|
||||
return; // Threshold means the follower was "despanwed" with S_NULL.
|
||||
|
||||
// However with how the code is factored, this is just a special case of S_INVISBLE to avoid having to add other player variables.
|
||||
|
||||
|
||||
// handle follower animations. Could probably be better...
|
||||
// hurt or dead
|
||||
if (player->spinouttimer || player->mo->state == &states[S_KART_SPINOUT] || player->mo->health <= 0)
|
||||
{
|
||||
player->follower->movecount = 0; // cancel hit confirm.
|
||||
player->follower->angle = player->drawangle; // spin out
|
||||
if (player->follower->extravalue1 != 2)
|
||||
{
|
||||
player->follower->extravalue1 = 2;
|
||||
P_SetFollowerState(player->follower, fl.hurtstate);
|
||||
}
|
||||
if (player->mo->health <= 0) // if dead, follow the player's z momentum exactly so they both look like they die at the same speed.
|
||||
player->follower->momz = player->mo->momz;
|
||||
}
|
||||
else if (player->follower->movecount)
|
||||
{
|
||||
if (player->follower->extravalue1 != 5)
|
||||
{
|
||||
player->follower->extravalue1 = 5;
|
||||
P_SetFollowerState(player->follower, fl.hitconfirmstate);
|
||||
}
|
||||
player->follower->movecount--;
|
||||
}
|
||||
else if (player->speed > 10*player->mo->scale) // animation for moving fast enough
|
||||
{
|
||||
|
||||
if (player->follower->extravalue1 != 1)
|
||||
{
|
||||
player->follower->extravalue1 = 1;
|
||||
P_SetFollowerState(player->follower, fl.followstate);
|
||||
}
|
||||
}
|
||||
else // animations when nearly still. This includes winning and losing.
|
||||
{
|
||||
if (player->follower->extravalue1 != 0)
|
||||
{
|
||||
|
||||
if (player->exiting) // win/ loss animations
|
||||
{
|
||||
if (K_IsPlayerLosing(player)) // L
|
||||
{
|
||||
if (player->follower->extravalue1 != 4)
|
||||
{
|
||||
player->follower->extravalue1 = 4;
|
||||
P_SetFollowerState(player->follower, fl.losestate);
|
||||
}
|
||||
}
|
||||
else // W
|
||||
{
|
||||
if (player->follower->extravalue1 != 3)
|
||||
{
|
||||
player->follower->extravalue1 = 3;
|
||||
P_SetFollowerState(player->follower, fl.winstate);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // normal standstill
|
||||
{
|
||||
player->follower->extravalue1 = 0;
|
||||
P_SetFollowerState(player->follower, fl.idlestate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* gaysed script from me, based on Golden's sprite slope roll */
|
||||
|
||||
// holy SHIT
|
||||
|
|
@ -4370,9 +4088,6 @@ void P_PlayerThink(player_t *player)
|
|||
player->awayviewtics = 0; // reset to zero
|
||||
}
|
||||
|
||||
// Run followes here. We need them to run even when we're dead to follow through what we're doing.
|
||||
P_HandleFollower(player);
|
||||
|
||||
if (player->flashcount)
|
||||
player->flashcount--;
|
||||
|
||||
|
|
@ -4767,6 +4482,10 @@ void P_PlayerAfterThink(player_t *player)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Run followers in AfterThink, after the players have moved,
|
||||
// so a lag value of 1 is exactly attached to the player.
|
||||
K_HandleFollower(player);
|
||||
|
||||
#ifdef SECTORSPECIALSAFTERTHINK
|
||||
if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
|
||||
player->onconveyor = 0;
|
||||
|
|
|
|||
|
|
@ -40,9 +40,6 @@ extern INT16 color8to16[256]; // remap color index to highcolor
|
|||
extern INT16 *hicolormaps; // remap high colors to high colors..
|
||||
|
||||
extern CV_PossibleValue_t Color_cons_t[];
|
||||
extern CV_PossibleValue_t Followercolor_cons_t[]; // follower colours table, not a duplicate because of the "Match" option.
|
||||
#define FOLLOWERCOLOR_MATCH UINT16_MAX
|
||||
#define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1)
|
||||
|
||||
// I/O, setting up the stuff.
|
||||
void R_InitTextureData(void);
|
||||
|
|
|
|||
|
|
@ -194,7 +194,6 @@ static INT32 CacheIndexToSkin(INT32 ttc)
|
|||
}
|
||||
|
||||
CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1];
|
||||
CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL
|
||||
|
||||
#define TRANSTAB_AMTMUL10 (255.0f / 10.0f)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@
|
|||
INT32 numskins = 0;
|
||||
skin_t skins[MAXSKINS];
|
||||
|
||||
INT32 numfollowers = 0;
|
||||
|
||||
// FIXTHIS: don't work because it must be inistilised before the config load
|
||||
//#define SKINVALUES
|
||||
#ifdef SKINVALUES
|
||||
|
|
@ -44,9 +42,6 @@ CV_PossibleValue_t skin_cons_t[MAXSKINS+1];
|
|||
|
||||
CV_PossibleValue_t Forceskin_cons_t[MAXSKINS+2];
|
||||
|
||||
// SRB2Kart followers
|
||||
follower_t followers[MAXSKINS];
|
||||
|
||||
//
|
||||
// P_GetSkinSprite2
|
||||
// For non-super players, tries each sprite2's immediate predecessor until it finds one with a number of frames or ends up at standing.
|
||||
|
|
@ -860,97 +855,3 @@ next_token:
|
|||
}
|
||||
|
||||
#undef SYMBOLCONVERT
|
||||
|
||||
// SRB2Kart: Followers!
|
||||
// TODO: put this stuff in its own file?
|
||||
|
||||
// same thing as R_SkinAvailable, but for followers
|
||||
INT32 R_FollowerAvailable(const char *name)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < numfollowers; i++)
|
||||
{
|
||||
if (stricmp(followers[i].skinname,name)==0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// same thing as SetPlayerSkin, but for followers
|
||||
boolean SetPlayerFollower(INT32 playernum, const char *skinname)
|
||||
{
|
||||
INT32 i;
|
||||
player_t *player = &players[playernum];
|
||||
|
||||
if (stricmp("None", skinname) == 0)
|
||||
{
|
||||
SetFollower(playernum, -1); // reminder that -1 is nothing
|
||||
return true;
|
||||
}
|
||||
for (i = 0; i < numfollowers; i++)
|
||||
{
|
||||
// search in the skin list
|
||||
if (stricmp(followers[i].skinname, skinname) == 0)
|
||||
{
|
||||
SetFollower(playernum, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (P_IsLocalPlayer(player))
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found.\n"), skinname);
|
||||
else if(server || IsPlayerAdmin(consoleplayer))
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) follower '%s' not found\n"), playernum, player_names[playernum], skinname);
|
||||
|
||||
SetFollower(playernum, -1); // reminder that -1 is nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
// SetPlayerSkinByNum, for followers
|
||||
void SetFollower(INT32 playernum, INT32 skinnum)
|
||||
{
|
||||
player_t *player = &players[playernum];
|
||||
mobj_t *bub;
|
||||
mobj_t *tmp;
|
||||
|
||||
player->followerready = true; // we are ready to perform follower related actions in the player thinker, now.
|
||||
|
||||
if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists!
|
||||
{
|
||||
/*
|
||||
We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself.
|
||||
However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it.
|
||||
*/
|
||||
|
||||
if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins
|
||||
{
|
||||
// Remove follower's possible hnext list (bubble)
|
||||
bub = player->follower->hnext;
|
||||
|
||||
while (bub && !P_MobjWasRemoved(bub))
|
||||
{
|
||||
tmp = bub->hnext;
|
||||
P_RemoveMobj(bub);
|
||||
bub = tmp;
|
||||
}
|
||||
|
||||
P_RemoveMobj(player->follower);
|
||||
P_SetTarget(&player->follower, NULL);
|
||||
}
|
||||
|
||||
player->followerskin = skinnum;
|
||||
|
||||
// for replays: We have changed our follower mid-game; let the game know so it can do the same in the replay!
|
||||
demo_extradata[playernum] |= DXD_FOLLOWER;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (P_IsLocalPlayer(player))
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower %d not found\n"), skinnum);
|
||||
else if(server || IsPlayerAdmin(consoleplayer))
|
||||
CONS_Alert(CONS_WARNING, "Player %d (%s) follower %d not found\n", playernum, player_names[playernum], skinnum);
|
||||
|
||||
SetFollower(playernum, -1); // Not found, then set -1 (nothing) as our follower.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,51 +90,4 @@ void R_AddSkins(UINT16 wadnum);
|
|||
|
||||
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
|
||||
|
||||
// SRB2Kart Followers
|
||||
|
||||
//
|
||||
// We'll define these here because they're really just a mobj that'll follow some rules behind a player
|
||||
//
|
||||
typedef struct follower_s
|
||||
{
|
||||
char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything.
|
||||
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
|
||||
|
||||
UINT16 defaultcolor; // default color for menus.
|
||||
|
||||
fixed_t scale; // Scale relative to the player's.
|
||||
fixed_t bubblescale; // Bubble scale relative to the player scale. If not set, no bubble will spawn (default)
|
||||
|
||||
// some position shenanigans:
|
||||
INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player.
|
||||
INT32 dist; // distance relative to the player. (In a circle)
|
||||
INT32 height; // height of the follower, this is mostly important for Z flipping.
|
||||
INT32 zoffs; // Z offset relative to the player's height. Cannot be negative.
|
||||
|
||||
// movement options
|
||||
|
||||
UINT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number.
|
||||
UINT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 6. Must be > 0 because we divide by this number.
|
||||
INT32 bobamp; // Bob amplitude. Default is 4.
|
||||
INT32 bobspeed; // Arbitrary modifier for bobbing speed, default is TICRATE*2 (70).
|
||||
|
||||
// from there on out, everything is STATES to allow customization
|
||||
// these are only set once when the action is performed and are then free to animate however they want.
|
||||
|
||||
INT32 idlestate; // state when the player is at a standstill
|
||||
INT32 followstate; // state when the player is moving
|
||||
INT32 hurtstate; // state when the player is being hurt
|
||||
INT32 winstate; // state when the player has won
|
||||
INT32 losestate; // state when the player has lost
|
||||
INT32 hitconfirmstate; // state for hit confirm
|
||||
UINT32 hitconfirmtime; // time to keep the above playing for
|
||||
} follower_t;
|
||||
|
||||
extern INT32 numfollowers;
|
||||
extern follower_t followers[MAXSKINS]; // again, use the same rules as skins, no reason not to.
|
||||
|
||||
INT32 R_FollowerAvailable(const char *name);
|
||||
boolean SetPlayerFollower(INT32 playernum,const char *skinname);
|
||||
void SetFollower(INT32 playernum,INT32 skinnum);
|
||||
|
||||
#endif //__R_SKINS__
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue