Merge branch 'master' into bot-rubberband-edit

This commit is contained in:
Sally Coolatta 2022-05-25 23:29:04 -04:00
commit 8815b065b3
45 changed files with 1988 additions and 965 deletions

View file

@ -302,6 +302,7 @@ target_compile_definitions(SRB2SDL2 PRIVATE -DCMAKECONFIG)
#)
add_subdirectory(sdl)
add_subdirectory(objects)
if(${CMAKE_SYSTEM} MATCHES Windows)
add_subdirectory(win32)

View file

@ -208,6 +208,7 @@ objdir:=$(makedir)/objs
sources+=\
$(call List,Sourcefile)\
$(call List,blua/Sourcefile)\
$(call List,objects/Sourcefile)\
depends:=$(basename $(filter %.c %.s,$(sources)))
objects:=$(basename $(filter %.c %.s %.nas,$(sources)))

View file

@ -118,3 +118,4 @@ k_hud.c
k_terrain.c
k_brightmap.c
k_director.c
k_follower.c

View file

@ -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"
@ -1421,7 +1422,7 @@ static void SendNameAndColor(UINT8 n)
CV_StealthSet(&cv_followercolor[n], "Match"); // set it to "Match". I don't care about your stupidity!
// so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game:
if (cv_follower[n].value > numfollowers-1 || cv_follower[n].value < -1)
if (cv_follower[n].value >= numfollowers || cv_follower[n].value < -1)
CV_StealthSet(&cv_follower[n], "-1");
if (!strcmp(cv_playername[n].string, player_names[playernum])
@ -1447,12 +1448,11 @@ static void SendNameAndColor(UINT8 n)
player->skincolor = cv_playercolor[n].value;
if (player->mo && !player->dye)
player->mo->color = player->skincolor;
K_KartResetPlayerColor(player);
// 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 +1632,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)
@ -4364,7 +4366,11 @@ static void Command_Version_f(void)
#endif
// DEVELOP build
#ifdef DEVELOP
#if defined(TESTERS)
CONS_Printf("\x88" "TESTERS " "\x80");
#elif defined(HOSTTESTERS)
CONS_Printf("\x82" "HOSTTESTERS " "\x80");
#elif defined(DEVELOP)
CONS_Printf("\x87" "DEVELOP " "\x80");
#endif
@ -5316,7 +5322,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 +5376,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 +5427,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 +5478,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);

View file

@ -405,7 +405,7 @@ typedef struct player_s
UINT8 justbumped; // Prevent players from endlessly bumping into each other
UINT8 tumbleBounces;
UINT16 tumbleHeight; // In *mobjscaled* fracunits, or mfu, not raw fu
boolean justDI; // Directional Influence ended, true until letting go of turn
UINT8 justDI; // Turn-lockout timer to briefly prevent unintended turning after DI, resets when actionable or no input
boolean flipDI; // Bananas flip the DI direction. Was a bug, but it made bananas much more interesting.
SINT8 drift; // (-5 to 5) - Drifting Left or Right, plus a bigger counter = sharper turn
@ -472,6 +472,7 @@ typedef struct player_s
UINT16 hyudorotimer; // Duration of the Hyudoro offroad effect itself
SINT8 stealingtimer; // if >0 you are stealing, if <0 you are being stolen from
mobj_t *hoverhyudoro; // First hyudoro hovering next to player
UINT16 sneakertimer; // Duration of a Sneaker Boost (from Sneakers or level boosters)
UINT8 numsneakers; // Number of stacked sneaker effects

View file

@ -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)
@ -3818,16 +3819,18 @@ void readfollower(MYFILE *f)
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
// 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].mode = FOLLOWERMODE_FLOAT;
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].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 = 3*FRACUNIT;
followers[numfollowers].vertlag = 6*FRACUNIT;
followers[numfollowers].anglelag = 8*FRACUNIT;
followers[numfollowers].bobspeed = TICRATE*2;
followers[numfollowers].bobamp = 4;
followers[numfollowers].bobamp = 4*FRACUNIT;
followers[numfollowers].hitconfirmtime = TICRATE;
followers[numfollowers].defaultcolor = SKINCOLOR_GREEN;
@ -3863,50 +3866,65 @@ void readfollower(MYFILE *f)
strcpy(followers[numfollowers].name, word2);
nameset = true;
}
else if (fastcmp(word, "MODE"))
{
if (word2)
strupr(word2);
if (fastcmp(word2, "FLOAT") || fastcmp(word2, "DEFAULT"))
followers[numfollowers].mode = FOLLOWERMODE_FLOAT;
else if (fastcmp(word2, "GROUND"))
followers[numfollowers].mode = FOLLOWERMODE_GROUND;
else
deh_warning("Follower %d: unknown follower mode '%s'", numfollowers, word2);
}
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, "ANGLELAG"))
{
followers[numfollowers].anglelag = (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,15 +3965,18 @@ 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)
{
// well this is problematic.
strcpy(followers[numfollowers].name, va("Follower%d", numfollowers)); // this is lazy, so what
}
@ -3966,7 +3987,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);
@ -3989,33 +4010,41 @@ 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.
if (followers[numfollowers].mode < FOLLOWERMODE_FLOAT || followers[numfollowers].mode >= FOLLOWERMODE__MAX)
{
followers[numfollowers].mode = FOLLOWERMODE_FLOAT;
deh_warning("Follower '%s': Value for 'mode' should be between %d and %d.", dname, FOLLOWERMODE_FLOAT, FOLLOWERMODE__MAX-1);
}
#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); \
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", dname, field2, threshold, set); \
} \
FALLBACK(dist, "DIST", 0, 0);
FALLBACK(height, "HEIGHT", 1, 1);
FALLBACK(zoffs, "ZOFFS", 0, 0);
FALLBACK(horzlag, "HORZLAG", 1, 1);
FALLBACK(vertlag, "VERTLAG", 1, 1);
FALLBACK(horzlag, "HORZLAG", FRACUNIT, FRACUNIT);
FALLBACK(vertlag, "VERTLAG", FRACUNIT, FRACUNIT);
FALLBACK(anglelag, "ANGLELAG", FRACUNIT, FRACUNIT);
FALLBACK(bobamp, "BOBAMP", 0, 0);
FALLBACK(bobspeed, "BOBSPEED", 0, 0);
FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1);
FALLBACK(scale, "SCALE", 1, 1); // No null/negative scale
FALLBACK(bubblescale, "BUBBLESCALE", 0, 0); // No negative scale
#undef FALLBACK
// 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);
}
#undef FALLBACK
// also check if we forgot states. If we did, we will set any missing state to the follower's idlestate.
// Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable.

View file

@ -368,8 +368,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_XDEATHSTATE",
"S_RAISESTATE",
// Thok
"S_THOK",
"S_SHADOW",
// SRB2kart Frames
"S_KART_STILL",
@ -3745,6 +3745,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_FLAMESHIELDLINE3",
"S_FLAMESHIELDFLASH",
// Caked-Up Booty-Sheet Ghost
"S_HYUDORO",
// The legend
"S_SINK",
"S_SINK_SHIELD",
@ -4493,6 +4496,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_UNKNOWN",
"MT_THOK", // Thok! mobj
"MT_SHADOW", // Linkdraw Shadow (for invisible objects)
"MT_PLAYER",
"MT_KART_LEFTOVER",
"MT_KART_TIRE",
@ -5315,6 +5319,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_FLAMESHIELDPAPER",
"MT_BUBBLESHIELDTRAP",
"MT_HYUDORO",
"MT_HYUDORO_CENTER",
"MT_SINK", // Kitchen Sink Stuff
"MT_SINK_SHIELD",
"MT_SINKTRAIL",

View file

@ -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

View file

@ -685,7 +685,6 @@ extern boolean comeback;
extern SINT8 battlewanted[4];
extern tic_t wantedcalcdelay;
extern tic_t indirectitemcooldown;
extern tic_t hyubgone;
extern tic_t mapreset;
extern boolean thwompsactive;
extern UINT8 lastLowestLap;

View file

@ -1998,6 +1998,38 @@ void F_TitleScreenDrawer(void)
V_DrawFixedPatch(0, 0, FRACUNIT, 0, kts_bumper, NULL);
V_DrawFixedPatch(0, 0, FRACUNIT, 0, kts_copyright, NULL);
// An adapted thing from old menus - most games have version info on the title screen now...
{
INT32 texty = vid.height - 10*vid.dupy;
#define addtext(f, str) {\
V_DrawThinString(vid.dupx, texty, V_NOSCALESTART|f, str);\
texty -= 10*vid.dupy;\
}
if (customversionstring[0] != '\0')
{
addtext(V_ALLOWLOWERCASE, customversionstring);
addtext(0, "Mod version:");
}
else
{
// Development -- show revision / branch info
#if defined(TESTERS)
addtext(V_ALLOWLOWERCASE|V_SKYMAP, "Tester client");
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate));
#elif defined(HOSTTESTERS)
addtext(V_ALLOWLOWERCASE|V_REDMAP, "Netgame host for testers");
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", compdate));
#elif defined(DEVELOP)
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, comprevision);
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, compbranch);
#else // Regular build
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING));
#endif
}
#undef addtext
}
break;
}

View file

@ -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);

View file

@ -314,7 +314,6 @@ SINT8 pickedvote; // What vote the host rolls
SINT8 battlewanted[4]; // WANTED players in battle, worth x2 points
tic_t wantedcalcdelay; // Time before it recalculates WANTED
tic_t indirectitemcooldown; // Cooldown before any more Shrink, SPB, or any other item that works indirectly is awarded
tic_t hyubgone; // Cooldown before hyudoro is allowed to be rerolled
tic_t mapreset; // Map reset delay when enough players have joined an empty game
boolean thwompsactive; // Thwomps activate on lap 2
UINT8 lastLowestLap; // Last lowest lap, for activating race lap executors

View file

@ -29,6 +29,7 @@
char sprnames[NUMSPRITES + 1][5] =
{
"NULL", // invisible object
"NONE", // invisible but still rendered
"UNKN",
"THOK", // Thok! mobj
@ -570,6 +571,7 @@ char sprnames[NUMSPRITES + 1][5] =
"FLMP", // Flame Shield paper sprites
"FLML", // Flame Shield speed lines
"FLMF", // Flame Shield flash
"HYUU", // Hyudoro
"SINK", // Kitchen Sink
"SITR", // Kitchen Sink Trail
"KBLN", // Battle Mode Bumper
@ -856,6 +858,7 @@ state_t states[NUMSTATES] =
{SPR_UNKN, FF_FULLBRIGHT, -1, {A_InfoState}, 6, 0, S_NULL}, // S_RAISESTATE
{SPR_THOK, FF_TRANS50, 8, {NULL}, 0, 0, S_NULL}, // S_THOK
{SPR_NONE, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHADOW
// Player
{SPR_PLAY, SPR2_STIN, 1, {NULL}, 0, 0, S_KART_STILL}, // S_KART_STILL
@ -4300,6 +4303,8 @@ state_t states[NUMSTATES] =
{SPR_FLML, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE|14, 7, {NULL}, 6, 1, S_NULL}, // S_FLAMESHIELDLINE3
{SPR_FLMF, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_FLAMESHIELDFLASH
{SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO
{SPR_SINK, 0, 1, {A_SmokeTrailer}, MT_SINKTRAIL, 0, S_SINK}, // S_SINK
{SPR_SINK, 0|FF_TRANS80|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_SINK_SHIELD}, // S_SINK_SHIELD
{SPR_SITR, 0, 1, {NULL}, 0, 0, S_SINKTRAIL2}, // S_SINKTRAIL1
@ -5154,6 +5159,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_SHADOW
-1, // doomednum
S_SHADOW, // 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
32*FRACUNIT, // radius
64*FRACUNIT, // height
-1, // display offset
16, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_PLAYER
-1, // doomednum
S_KART_STILL, // spawnstate
@ -23881,6 +23913,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_HYUDORO
-1, // doomednum
S_HYUDORO, // 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
0, // speed
32*FRACUNIT, // radius
24*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_NOCLIP|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_HYUDORO_CENTER
-1, // doomednum
S_INVISIBLE, // 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
0, // speed
64*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_SINK
-1, // doomednum
S_SINK, // spawnstate

View file

@ -575,6 +575,7 @@ extern boolean actionsoverridden[NUMACTIONS];
typedef enum sprite
{
SPR_NULL, // invisible object
SPR_NONE, // invisible but still rendered
SPR_UNKN,
SPR_THOK, // Thok! mobj
@ -1116,6 +1117,7 @@ typedef enum sprite
SPR_FLMP, // Flame Shield paper sprites
SPR_FLML, // Flame Shield speed lines
SPR_FLMF, // Flame Shield flash
SPR_HYUU, // Hyudoro
SPR_SINK, // Kitchen Sink
SPR_SITR, // Kitchen Sink Trail
SPR_KBLN, // Battle Mode Bumper
@ -1355,8 +1357,8 @@ typedef enum state
S_XDEATHSTATE,
S_RAISESTATE,
// Thok
S_THOK,
S_SHADOW,
S_KART_STILL,
S_KART_STILL_L,
@ -4731,6 +4733,9 @@ typedef enum state
S_FLAMESHIELDLINE3,
S_FLAMESHIELDFLASH,
// Caked-Up Booty-Sheet Ghost
S_HYUDORO,
// The legend
S_SINK,
S_SINK_SHIELD,
@ -5516,6 +5521,7 @@ typedef enum mobj_type
MT_UNKNOWN,
MT_THOK, // Thok! mobj
MT_SHADOW, // Linkdraw Shadow (for invisible objects)
MT_PLAYER,
MT_KART_LEFTOVER,
MT_KART_TIRE,
@ -6338,6 +6344,9 @@ typedef enum mobj_type
MT_FLAMESHIELDPAPER,
MT_BUBBLESHIELDTRAP,
MT_HYUDORO,
MT_HYUDORO_CENTER,
MT_SINK, // Kitchen Sink Stuff
MT_SINK_SHIELD,
MT_SINKTRAIL,

View file

@ -672,12 +672,11 @@ void K_RunBattleOvertime(void)
if (battleovertime.radius > 0)
{
const fixed_t pi = (22 * FRACUNIT) / 7; // loose approximation, this doesn't need to be incredibly precise
const INT32 orbs = 32;
const angle_t angoff = ANGLE_MAX / orbs;
const UINT8 spriteSpacing = 128;
fixed_t circumference = FixedMul(pi, battleovertime.radius * 2);
fixed_t circumference = FixedMul(M_PI_FIXED, battleovertime.radius * 2);
fixed_t scale = max(circumference / spriteSpacing / orbs, mapobjectscale);
fixed_t size = FixedMul(mobjinfo[MT_OVERTIME_PARTICLE].radius, scale);

View file

@ -1308,7 +1308,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
K_BotItemGenericOrbitShield(player, cmd);
}
else if (player->position != 1) // Hold onto orbiting items when in 1st :)
/* FALL-THRU */
/* FALLTHRU */
case KITEM_BALLHOG:
{
K_BotItemOrbinaut(player, cmd);

View file

@ -51,7 +51,7 @@ struct globalsmuggle
} globalsmuggle;
/*--------------------------------------------------
static boolean K_FindEggboxes(mobj_t *thing)
static BlockItReturn_t K_FindEggboxes(mobj_t *thing)
Blockmap search function.
Increments the random items and egg boxes counters.
@ -60,27 +60,27 @@ struct globalsmuggle
thing - Object passed in from iteration.
Return:-
true continues searching, false ends the search early.
BlockItReturn_t enum, see its definition for more information.
--------------------------------------------------*/
static boolean K_FindEggboxes(mobj_t *thing)
static BlockItReturn_t K_FindEggboxes(mobj_t *thing)
{
fixed_t dist;
if (thing->type != MT_RANDOMITEM && thing->type != MT_EGGMANITEM)
{
return true;
return BMIT_CONTINUE;
}
if (!thing->health)
{
return true;
return BMIT_CONTINUE;
}
dist = P_AproxDistance(thing->x - globalsmuggle.eggboxx, thing->y - globalsmuggle.eggboxy);
if (dist > globalsmuggle.distancetocheck)
{
return true;
return BMIT_CONTINUE;
}
if (thing->type == MT_RANDOMITEM)
@ -92,7 +92,7 @@ static boolean K_FindEggboxes(mobj_t *thing)
globalsmuggle.eggboxes++;
}
return true;
return BMIT_CONTINUE;
}
/*--------------------------------------------------
@ -347,7 +347,7 @@ static boolean K_PlayerAttackSteer(mobj_t *thing, UINT8 side, UINT8 weight, bool
}
/*--------------------------------------------------
static boolean K_FindObjectsForNudging(mobj_t *thing)
static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
Blockmap search function.
Finds objects around the bot to steer towards/away from.
@ -356,9 +356,9 @@ static boolean K_PlayerAttackSteer(mobj_t *thing, UINT8 side, UINT8 weight, bool
thing - Object passed in from iteration.
Return:-
true continues searching, false ends the search early.
BlockItReturn_t enum, see its definition for more information.
--------------------------------------------------*/
static boolean K_FindObjectsForNudging(mobj_t *thing)
static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
{
INT16 anglediff;
fixed_t fulldist;
@ -367,29 +367,29 @@ static boolean K_FindObjectsForNudging(mobj_t *thing)
if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player)
{
return false;
return BMIT_ABORT;
}
if (thing->health <= 0)
{
return true;
return BMIT_CONTINUE;
}
if (globalsmuggle.botmo == thing)
{
return true;
return BMIT_CONTINUE;
}
fulldist = R_PointToDist2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y) - thing->radius;
if (fulldist > globalsmuggle.distancetocheck)
{
return true;
return BMIT_CONTINUE;
}
if (P_CheckSight(globalsmuggle.botmo, thing) == false)
{
return true;
return BMIT_CONTINUE;
}
predictangle = R_PointToAngle2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, globalsmuggle.predict->x, globalsmuggle.predict->y);
@ -607,7 +607,7 @@ static boolean K_FindObjectsForNudging(mobj_t *thing)
break;
}
return true;
return BMIT_CONTINUE;
}
/*--------------------------------------------------
@ -776,7 +776,7 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player)
}
/*--------------------------------------------------
static boolean K_FindPlayersToBully(mobj_t *thing)
static BlockItReturn_t K_FindPlayersToBully(mobj_t *thing)
Blockmap search function.
Finds players around the bot to bump.
@ -785,9 +785,9 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player)
thing - Object passed in from iteration.
Return:-
true continues searching, false ends the search early.
BlockItReturn_t enum, see its definition for more information.
--------------------------------------------------*/
static boolean K_FindPlayersToBully(mobj_t *thing)
static BlockItReturn_t K_FindPlayersToBully(mobj_t *thing)
{
INT16 anglediff;
fixed_t fulldist;
@ -796,34 +796,34 @@ static boolean K_FindPlayersToBully(mobj_t *thing)
if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player)
{
return false;
return BMIT_ABORT;
}
if (thing->health <= 0)
{
return true;
return BMIT_CONTINUE;
}
if (!thing->player)
{
return true;
return BMIT_CONTINUE;
}
if (globalsmuggle.botmo == thing)
{
return true;
return BMIT_CONTINUE;
}
fulldist = R_PointToDist2(globalsmuggle.botmo->x, globalsmuggle.botmo->y, thing->x, thing->y) - thing->radius;
if (fulldist > globalsmuggle.distancetocheck)
{
return true;
return BMIT_CONTINUE;
}
if (P_CheckSight(globalsmuggle.botmo, thing) == false)
{
return true;
return BMIT_CONTINUE;
}
ourangle = globalsmuggle.botmo->angle;
@ -860,7 +860,7 @@ static boolean K_FindPlayersToBully(mobj_t *thing)
globalsmuggle.annoymo = thing;
}
return true;
return BMIT_CONTINUE;
}
/*--------------------------------------------------

View file

@ -323,26 +323,26 @@ static inline boolean PIT_SSMineChecks(mobj_t *thing)
return false;
}
static inline boolean PIT_SSMineSearch(mobj_t *thing)
static inline BlockItReturn_t PIT_SSMineSearch(mobj_t *thing)
{
if (grenade == NULL || P_MobjWasRemoved(grenade))
return false; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot
return BMIT_ABORT; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot
if (grenade->flags2 & MF2_DEBRIS) // don't explode twice
return false;
return BMIT_ABORT;
if (thing->type != MT_PLAYER) // Don't explode for anything but an actual player.
return true;
return BMIT_CONTINUE;
if (thing == grenade->target && grenade->threshold != 0) // Don't blow up at your owner instantly.
return true;
return BMIT_CONTINUE;
if (PIT_SSMineChecks(thing) == true)
return true;
return BMIT_CONTINUE;
// Explode!
P_SetMobjState(grenade, grenade->info->deathstate);
return false;
return BMIT_ABORT;
}
void K_DoMineSearch(mobj_t *actor, fixed_t size)
@ -364,21 +364,21 @@ void K_DoMineSearch(mobj_t *actor, fixed_t size)
P_BlockThingsIterator(bx, by, PIT_SSMineSearch);
}
static inline boolean PIT_SSMineExplode(mobj_t *thing)
static inline BlockItReturn_t PIT_SSMineExplode(mobj_t *thing)
{
if (grenade == NULL || P_MobjWasRemoved(grenade))
return false; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot
return BMIT_ABORT; // There's the possibility these can chain react onto themselves after they've already died if there are enough all in one spot
#if 0
if (grenade->flags2 & MF2_DEBRIS) // don't explode twice
return false;
return BMIT_ABORT;
#endif
if (PIT_SSMineChecks(thing) == true)
return true;
return BMIT_CONTINUE;
P_DamageMobj(thing, grenade, grenade->target, 1, (explodespin ? DMG_NORMAL : DMG_EXPLODE));
return true;
return BMIT_CONTINUE;
}
void K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin)
@ -667,60 +667,54 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2)
static mobj_t *lightningSource;
static fixed_t lightningDist;
static inline boolean PIT_LightningShieldAttack(mobj_t *thing)
static inline BlockItReturn_t PIT_LightningShieldAttack(mobj_t *thing)
{
if (lightningSource == NULL || P_MobjWasRemoved(lightningSource))
{
// Invalid?
return false;
return BMIT_ABORT;
}
if (thing == lightningSource)
{
// Don't explode yourself!!
return true;
return BMIT_CONTINUE;
}
if (thing->health <= 0)
{
// Dead
return true;
return BMIT_CONTINUE;
}
if (!(thing->flags & MF_SHOOTABLE) || (thing->flags & MF_SCENERY))
{
// Not shootable
return true;
return BMIT_CONTINUE;
}
if (thing->player && thing->player->spectator)
{
// Spectator
return true;
}
if ((lightningSource->eflags & MFE_VERTICALFLIP)
? (thing->z > lightningSource->z + lightningSource->height)
: (thing->z + thing->height < lightningSource->z))
{
// Underneath
return true;
return BMIT_CONTINUE;
}
if (P_AproxDistance(thing->x - lightningSource->x, thing->y - lightningSource->y) > lightningDist + thing->radius)
{
// Too far away
return true;
return BMIT_CONTINUE;
}
#if 0
if (P_CheckSight(lightningSource, thing) == false)
{
// Not in sight
return true;
return BMIT_CONTINUE;
}
#endif
P_DamageMobj(thing, lightningSource, lightningSource, 1, DMG_NORMAL|DMG_CANTHURTSELF|DMG_WOMBO);
return true;
return BMIT_CONTINUE;
}
void K_LightningShieldAttack(mobj_t *actor, fixed_t size)

567
src/k_follower.c Normal file
View file

@ -0,0 +1,567 @@
#include "k_follower.h"
#include "k_kart.h"
#include "doomtype.h"
#include "doomdef.h"
#include "g_game.h"
#include "g_demo.h"
#include "r_main.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 ourheight;
fixed_t sx, sy, sz, deltaz;
fixed_t fh = INT32_MIN, ch = INT32_MAX;
UINT16 color;
fixed_t bubble; // bubble scale (0 if no bubble)
mobj_t *bmobj; // temp bubble mobj
angle_t destAngle;
INT32 angleDiff;
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 || 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 + player->mo->momx + FixedMul(FixedMul(player->mo->scale, fl.dist), FINECOSINE((an) >> ANGLETOFINESHIFT));
sy = player->mo->y + player->mo->momy + 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 + player->mo->momz + FixedMul(player->mo->scale, zoffs * P_MobjFlip(player->mo));
ourheight = FixedMul(fl.height, player->mo->scale);
if (player->mo->eflags & MFE_VERTICALFLIP)
{
sz += ourheight;
}
fh = player->mo->floorz;
ch = player->mo->ceilingz - ourheight;
switch (fl.mode)
{
case FOLLOWERMODE_GROUND:
{
if (player->mo->eflags & MFE_VERTICALFLIP)
{
sz = ch;
}
else
{
sz = fh;
}
break;
}
case FOLLOWERMODE_FLOAT:
default:
{
// finally, add a cool floating effect to the z height.
// not stolen from k_kart I swear!!
fixed_t sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * leveltime) >> ANGLETOFINESHIFT) & FINEMASK));
sz += FixedMul(player->mo->scale, sine) * P_MobjFlip(player->mo);
break;
}
}
// 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)!
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);
if (fl.mode == FOLLOWERMODE_GROUND)
{
sector_t *sec = R_PointInSubsector(sx, sy)->sector;
fh = min(fh, P_GetFloorZ(player->follower, sec, sx, sy, NULL));
ch = max(ch, P_GetCeilingZ(player->follower, sec, sx, sy, NULL) - ourheight);
if (P_IsObjectOnGround(player->mo) == false)
{
// In the air, match their momentum.
player->follower->momz = player->mo->momz;
}
else
{
fixed_t fg = P_GetMobjGravity(player->mo);
fixed_t fz = P_GetMobjZMovement(player->follower);
player->follower->momz = fz;
// Player is on the ground ... try to get the follower
// back to the ground also if it is above it.
player->follower->momz += FixedDiv(fg * 6, fl.vertlag); // Scaled against the default value of vertlag
}
}
else
{
player->follower->momz = FixedDiv(sz - player->follower->z, fl.vertlag);
}
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.
if (FixedHypot(player->follower->momx, player->follower->momy) >= player->mo->scale)
{
destAngle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy);
}
else
{
// Face the player's angle when standing still.
destAngle = player->mo->angle;
}
// Sal: Turn the follower around when looking backwards.
if ( player->cmd.buttons & BT_LOOKBACK )
{
destAngle += ANGLE_180;
}
// Sal: Smoothly rotate angle to the destination value.
angleDiff = AngleDeltaSigned(destAngle, player->follower->angle);
if (angleDiff != 0)
{
player->follower->angle += FixedDiv(angleDiff, fl.anglelag);
}
// Ground follower slope rotation
if (fl.mode == FOLLOWERMODE_GROUND)
{
if (player->follower->z <= fh)
{
player->follower->z = fh;
if (player->follower->momz < 0)
{
player->follower->momz = 0;
}
}
else if (player->follower->z >= ch)
{
player->follower->z = ch;
if (player->follower->momz > 0)
{
player->follower->momz = 0;
}
}
K_CalculateBananaSlope(
player->follower,
player->follower->x, player->follower->y, player->follower->z,
player->follower->radius, ourheight,
(player->mo->eflags & MFE_VERTICALFLIP),
false
);
}
// 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);
}
}
}
}
}

143
src/k_follower.h Normal file
View file

@ -0,0 +1,143 @@
// 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.
typedef enum
{
FOLLOWERMODE_FLOAT, // Default behavior, floats in the position you set it to.
FOLLOWERMODE_GROUND, // Snaps to the ground & rotates with slopes.
FOLLOWERMODE__MAX
} followermode_t;
//
// 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.
followermode_t mode; // Follower behavior modifier.
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 3. 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 anglelag; // Angle rotation lag. Default is 8. 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__

View file

@ -37,6 +37,8 @@
#include "k_terrain.h"
#include "k_director.h"
#include "k_collide.h"
#include "k_follower.h"
#include "k_objects.h"
// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H:
// gamespeed is cc (0 for easy, 1 for normal, 2 for hard)
@ -352,25 +354,25 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
//P-Odds 0 1 2 3 4 5 6 7
/*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker
/*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker
/*Invincibility*/ { 0, 0, 0, 0, 2, 4, 6, 9 }, // Invincibility
/*Banana*/ { 4, 3, 1, 0, 0, 0, 0, 0 }, // Banana
/*Invincibility*/ { 0, 0, 0, 0, 3, 4, 6, 9 }, // Invincibility
/*Banana*/ { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana
/*Eggman Monitor*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
/*Orbinaut*/ { 5, 4, 2, 2, 0, 0, 0, 0 }, // Orbinaut
/*Jawz*/ { 0, 3, 2, 1, 1, 0, 0, 0 }, // Jawz
/*Mine*/ { 0, 2, 3, 1, 0, 0, 0, 0 }, // Mine
/*Land Mine*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine
/*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog
/*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog
/*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb
/*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow
/*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink
/*Lightning Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Lightning Shield
/*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield
/*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield
/*Hyudoro*/ { 0, 0, 0, 1, 1, 0, 0, 0 }, // Hyudoro
/*Hyudoro*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Hyudoro
/*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring
/*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring
/*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
/*Drop Target*/ { 4, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target
/*Drop Target*/ { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target
/*Sneaker x2*/ { 0, 0, 2, 2, 1, 0, 0, 0 }, // Sneaker x2
/*Sneaker x3*/ { 0, 0, 0, 2, 6,10, 5, 0 }, // Sneaker x3
/*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3
@ -457,9 +459,6 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem)
if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) // Indirect items
indirectitemcooldown = 20*TICRATE;
if (getitem == KITEM_HYUDORO) // Hyudoro cooldown
hyubgone = 5*TICRATE;
player->botvars.itemdelay = TICRATE;
player->botvars.itemconfirm = 0;
@ -681,6 +680,7 @@ INT32 K_KartGetItemOdds(
case KITEM_LANDMINE:
case KITEM_DROPTARGET:
case KITEM_BALLHOG:
case KITEM_HYUDORO:
case KRITEM_TRIPLESNEAKER:
case KRITEM_TRIPLEORBINAUT:
case KRITEM_QUADORBINAUT:
@ -741,13 +741,6 @@ INT32 K_KartGetItemOdds(
if (spbplace != -1)
newodds = 0;
break;
case KITEM_HYUDORO:
cooldownOnStart = true;
notNearEnd = true;
if (hyubgone > 0)
newodds = 0;
break;
default:
break;
}
@ -2922,6 +2915,7 @@ boolean K_TripwirePassConditions(player_t *player)
player->sneakertimer ||
player->growshrinktimer > 0 ||
player->flamedash ||
player->hyudorotimer ||
player->speed > 2 * K_GetKartSpeed(player, false, true)
)
return true;
@ -3346,7 +3340,7 @@ fixed_t K_3dKartMovement(player_t *player)
angle_t K_MomentumAngle(mobj_t *mo)
{
if (mo->momx || mo->momy)
if (FixedHypot(mo->momx, mo->momy) >= mo->scale)
{
return R_PointToAngle2(0, 0, mo->momx, mo->momy);
}
@ -3551,8 +3545,7 @@ static void K_RemoveGrowShrink(player_t *player)
else if (player->growshrinktimer < 0) // Play Grow noise
S_StartSound(player->mo, sfx_kc5a);
if (player->invincibilitytimer == 0)
player->mo->color = player->skincolor;
K_KartResetPlayerColor(player);
player->mo->scalespeed = mapobjectscale/TICRATE;
player->mo->destscale = mapobjectscale;
@ -5398,6 +5391,7 @@ static void K_FlameDashLeftoverSmoke(mobj_t *src)
}
}
#if 0
static void K_DoHyudoroSteal(player_t *player)
{
INT32 i, numplayers = 0;
@ -5475,6 +5469,7 @@ static void K_DoHyudoroSteal(player_t *player)
S_StartSound(NULL, sfx_s3k92);
}
}
#endif
void K_DoSneaker(player_t *player, INT32 type)
{
@ -5619,7 +5614,6 @@ static void K_DoShrink(player_t *user)
void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound)
{
const fixed_t vscale = mapobjectscale + (mo->scale - mapobjectscale);
fixed_t thrust = 0;
if (mo->player && mo->player->spectator)
@ -5660,7 +5654,7 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound)
//CONS_Printf("Got boost: %d%\n", mo->player->trickboostpower*100 / FRACUNIT);
}
mo->momz = FixedMul(thrust, vscale);
mo->momz = FixedMul(thrust, mapobjectscale);
if (mo->eflags & MFE_UNDERWATER)
{
@ -6248,7 +6242,7 @@ static fixed_t K_BananaSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t z,
return P_GetZAt(slope, testx, testy, z);
}
static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player)
void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player)
{
fixed_t newz;
sector_t *sec;
@ -6637,12 +6631,10 @@ static void K_MoveHeldObjects(player_t *player)
targy = player->mo->y + P_ReturnThrustY(cur, cur->angle + angoffset, cur->extravalue1);
{ // bobbing, copy pasted from my kimokawaiii entry
const fixed_t pi = (22<<FRACBITS) / 7; // loose approximation, this doesn't need to be incredibly precise
fixed_t sine = FixedMul(player->mo->scale, 8 * FINESINE((((2*pi*(4*TICRATE)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK));
fixed_t sine = FixedMul(player->mo->scale, 8 * FINESINE((((M_TAU_FIXED * (4*TICRATE)) * leveltime) >> ANGLETOFINESHIFT) & FINEMASK));
targz = (player->mo->z + (player->mo->height/2)) + sine;
if (player->mo->eflags & MFE_VERTICALFLIP)
targz += (player->mo->height/2 - 32*player->mo->scale)*6;
}
if (cur->tracer)
@ -7069,57 +7061,57 @@ static mobj_t *attractmo;
static fixed_t attractdist;
static fixed_t attractzdist;
static inline boolean PIT_AttractingRings(mobj_t *thing)
static inline BlockItReturn_t PIT_AttractingRings(mobj_t *thing)
{
if (attractmo == NULL || P_MobjWasRemoved(attractmo) || attractmo->player == NULL)
{
return false;
return BMIT_ABORT;
}
if (thing == NULL || P_MobjWasRemoved(thing))
{
return true; // invalid
return BMIT_CONTINUE; // invalid
}
if (thing == attractmo)
{
return true; // invalid
return BMIT_CONTINUE; // invalid
}
if (!(thing->type == MT_RING || thing->type == MT_FLINGRING))
{
return true; // not a ring
return BMIT_CONTINUE; // not a ring
}
if (thing->health <= 0)
{
return true; // dead
return BMIT_CONTINUE; // dead
}
if (thing->extravalue1)
{
return true; // in special ring animation
return BMIT_CONTINUE; // in special ring animation
}
if (thing->tracer != NULL && P_MobjWasRemoved(thing->tracer) == false)
{
return true; // already attracted
return BMIT_CONTINUE; // already attracted
}
// see if it went over / under
if (attractmo->z - attractzdist > thing->z + thing->height)
{
return true; // overhead
return BMIT_CONTINUE; // overhead
}
if (attractmo->z + attractmo->height + attractzdist < thing->z)
{
return true; // underneath
return BMIT_CONTINUE; // underneath
}
if (P_AproxDistance(attractmo->x - thing->x, attractmo->y - thing->y) > attractdist + thing->radius)
{
return true; // Too far away
return BMIT_CONTINUE; // Too far away
}
if (RINGTOTAL(attractmo->player) >= 20 || (attractmo->player->pflags & PF_RINGLOCK))
@ -7146,7 +7138,7 @@ static inline boolean PIT_AttractingRings(mobj_t *thing)
P_SetTarget(&thing->tracer, attractmo);
}
return true; // find other rings
return BMIT_CONTINUE; // find other rings
}
/** Looks for rings near a player in the blockmap.
@ -7568,6 +7560,15 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->instashield)
player->instashield--;
if (player->justDI)
{
player->justDI--;
// return turning if player is fully actionable, no matter when!
if (!P_PlayerInPain(player))
player->justDI = 0;
}
if (player->eggmanexplode)
{
if (player->spectator || (gametype == GT_BATTLE && !player->bumpers))
@ -7578,6 +7579,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->eggmanexplode <= 0)
{
mobj_t *eggsexplode;
K_KartResetPlayerColor(player);
//player->flashing = 0;
eggsexplode = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SPBEXPLOSION);
if (player->eggmanblame >= 0
@ -7657,37 +7661,45 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
K_HandleDelayedHitByEm(player);
}
void K_KartPlayerAfterThink(player_t *player)
void K_KartResetPlayerColor(player_t *player)
{
boolean forcereset = false;
boolean fullbright = false;
if (player->playerstate == PST_DEAD || (player->respawn.state == RESPAWNST_MOVE)) // Ensure these are set correctly here
if (!player->mo || P_MobjWasRemoved(player->mo)) // Can't do anything
return;
if (player->mo->health <= 0 || player->playerstate == PST_DEAD || (player->respawn.state == RESPAWNST_MOVE)) // Override everything
{
player->mo->colorized = (player->dye != 0);
player->mo->color = player->dye ? player->dye : player->skincolor;
goto finalise;
}
else if (player->eggmanexplode) // You're gonna diiiiie
if (player->eggmanexplode) // You're gonna diiiiie
{
const INT32 flashtime = 4<<(player->eggmanexplode/TICRATE);
if (player->eggmanexplode == 1 || (player->eggmanexplode % (flashtime/2) != 0))
{
player->mo->colorized = (player->dye != 0);
player->mo->color = player->dye ? player->dye : player->skincolor;
forcereset = true;
}
else if (player->eggmanexplode % flashtime == 0)
{
player->mo->colorized = true;
player->mo->color = SKINCOLOR_BLACK;
fullbright = true;
goto finalise;
}
else
{
player->mo->colorized = true;
player->mo->color = SKINCOLOR_CRIMSON;
fullbright = true;
goto finalise;
}
}
else if (player->invincibilitytimer)
if (player->invincibilitytimer) // You're gonna kiiiiill
{
const tic_t defaultTime = itemtime+(2*TICRATE);
tic_t flicker = 2;
@ -7698,44 +7710,56 @@ void K_KartPlayerAfterThink(player_t *player)
{
player->mo->color = K_RainbowColor(leveltime / 2);
player->mo->colorized = true;
forcereset = false;
}
else
{
player->mo->color = player->skincolor;
player->mo->colorized = false;
flicker += (defaultTime - player->invincibilitytimer) / TICRATE / 2;
forcereset = true;
}
if (leveltime % flicker == 0)
{
player->mo->color = SKINCOLOR_INVINCFLASH;
player->mo->colorized = true;
forcereset = false;
}
if (!forcereset)
{
goto finalise;
}
}
else if (player->growshrinktimer) // Ditto, for grow/shrink
if (player->growshrinktimer) // Ditto, for grow/shrink
{
if (player->growshrinktimer % 5 == 0)
{
player->mo->colorized = true;
player->mo->color = (player->growshrinktimer < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE);
fullbright = true;
goto finalise;
}
else
{
player->mo->colorized = (player->dye != 0);
player->mo->color = player->dye ? player->dye : player->skincolor;
forcereset = true;
}
}
else if (player->ringboost && (leveltime & 1)) // ring boosting
if (player->ringboost && (leveltime & 1)) // ring boosting
{
player->mo->colorized = true;
fullbright = true;
goto finalise;
}
else
{
player->mo->colorized = (player->dye != 0);
if (forcereset)
{
player->mo->color = player->dye ? player->dye : player->skincolor;
}
}
finalise:
if (player->curshield)
{
@ -7751,6 +7775,11 @@ void K_KartPlayerAfterThink(player_t *player)
if (!(player->mo->state->frame & FF_FULLBRIGHT))
player->mo->frame &= ~FF_FULLBRIGHT;
}
}
void K_KartPlayerAfterThink(player_t *player)
{
K_KartResetPlayerColor(player);
// Move held objects (Bananas, Orbinaut, etc)
K_MoveHeldObjects(player);
@ -8315,7 +8344,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
return 0;
}
if (player->justDI == true)
if (player->justDI > 0)
{
// No turning until you let go after DI-ing.
return 0;
@ -8877,6 +8906,8 @@ void K_StripOther(player_t *player)
{
player->eggmanexplode = 0;
player->eggmanblame = -1;
K_KartResetPlayerColor(player);
}
}
@ -10042,7 +10073,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
{
player->itemamount--;
K_DoHyudoroSteal(player); // yes. yes they do.
//K_DoHyudoroSteal(player); // yes. yes they do.
Obj_HyudoroDeploy(player->mo);
K_PlayAttackTaunt(player->mo);
}
break;
case KITEM_POGOSPRING:
@ -10226,38 +10259,26 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
else if (!(player->pflags & PF_TRICKDELAY)) // don't allow tricking at the same frame you tumble obv
{
// "COOL" timing n shit.
if (cmd->turning || player->throwdir)
{
if (abs(momz) < FRACUNIT*99) // Let's use that as baseline for PERFECT trick.
{
player->karthud[khud_trickcool] = TICRATE;
}
}
INT16 aimingcompare = abs(cmd->throwdir) - abs(cmd->turning);
// Uses cmd->turning over steering intentionally.
#define TRICKTHRESHOLD (KART_FULLTURN/4)
if (aimingcompare < -TRICKTHRESHOLD) // side trick
{
if (cmd->turning > 0)
{
P_InstaThrust(player->mo, player->mo->angle + lr, max(basespeed, speed*5/2));
player->trickpanel = 2;
player->mo->hitlag = TRICKLAG;
player->mo->eflags &= ~MFE_DAMAGEHITLAG;
K_trickPanelTimingVisual(player, momz);
}
else if (cmd->turning < 0)
else
{
P_InstaThrust(player->mo, player->mo->angle - lr, max(basespeed, speed*5/2));
player->trickpanel = 3;
player->mo->hitlag = TRICKLAG;
player->mo->eflags &= ~MFE_DAMAGEHITLAG;
K_trickPanelTimingVisual(player, momz);
}
else if (cmd->throwdir > 0)
}
else if (aimingcompare > TRICKTHRESHOLD) // forward/back trick
{
if (cmd->throwdir > 0) // back trick
{
if (player->mo->momz * P_MobjFlip(player->mo) > 0)
{
@ -10266,11 +10287,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_InstaThrust(player->mo, player->mo->angle, max(basespeed, speed*3));
player->trickpanel = 2;
player->mo->hitlag = TRICKLAG;
player->mo->eflags &= ~MFE_DAMAGEHITLAG;
K_trickPanelTimingVisual(player, momz);
}
else if (cmd->throwdir < 0)
{
@ -10292,11 +10308,22 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetObjectMomZ(player->mo, 48*FRACUNIT, relative);
player->trickpanel = 4;
}
}
#undef TRICKTHRESHOLD
// Finalise everything.
if (player->trickpanel != 1) // just changed from 1?
{
player->mo->hitlag = TRICKLAG;
player->mo->eflags &= ~MFE_DAMAGEHITLAG;
K_trickPanelTimingVisual(player, momz);
if (abs(momz) < FRACUNIT*99) // Let's use that as baseline for PERFECT trick.
{
player->karthud[khud_trickcool] = TICRATE;
}
}
}
}
@ -10507,7 +10534,7 @@ void K_HandleDirectionalInfluence(player_t *player)
}
// DI attempted!!
player->justDI = true;
player->justDI = MAXHITLAGTICS;
cmd = &player->cmd;

View file

@ -61,6 +61,7 @@ void K_SpawnInvincibilitySpeedLines(mobj_t *mo);
void K_SpawnBumpEffect(mobj_t *mo);
void K_KartMoveAnimation(player_t *player);
void K_KartPlayerHUDUpdate(player_t *player);
void K_KartResetPlayerColor(player_t *player);
void K_KartPlayerThink(player_t *player, ticcmd_t *cmd);
void K_KartPlayerAfterThink(player_t *player);
angle_t K_MomentumAngle(mobj_t *mo);
@ -94,6 +95,7 @@ void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source);
void K_UpdateHnextList(player_t *player, boolean clean);
void K_DropHnextList(player_t *player, boolean keepshields);
void K_RepairOrbitChain(mobj_t *orbit);
void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player);
player_t *K_FindJawzTarget(mobj_t *actor, player_t *source);
INT32 K_GetKartRingPower(player_t *player, boolean boosted);
void K_UpdateDistanceFromFinishLine(player_t *const player);

11
src/k_objects.h Normal file
View file

@ -0,0 +1,11 @@
/* object-specific code */
#ifndef k_objects_H
#define k_objects_H
/* Hyudoro */
void Obj_HyudoroDeploy(mobj_t *master);
void Obj_HyudoroThink(mobj_t *actor);
void Obj_HyudoroCenterThink(mobj_t *actor);
void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher);
#endif/*k_objects_H*/

View file

@ -239,7 +239,7 @@ static int player_get(lua_State *L)
else if (fastcmp(field,"tumbleHeight"))
lua_pushinteger(L, plr->tumbleHeight);
else if (fastcmp(field,"justDI"))
lua_pushboolean(L, plr->justDI);
lua_pushinteger(L, plr->justDI);
else if (fastcmp(field,"flipDI"))
lua_pushboolean(L, plr->flipDI);
else if (fastcmp(field,"drift"))
@ -593,7 +593,7 @@ static int player_set(lua_State *L)
else if (fastcmp(field,"tumbleHeight"))
plr->tumbleHeight = luaL_checkinteger(L, 3);
else if (fastcmp(field,"justDI"))
plr->justDI = luaL_checkboolean(L, 3);
plr->justDI = luaL_checkinteger(L, 3);
else if (fastcmp(field,"flipDI"))
plr->flipDI = luaL_checkboolean(L, 3);
else if (fastcmp(field,"drift"))

View file

@ -378,9 +378,6 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"indirectitemcooldown")) {
lua_pushinteger(L, indirectitemcooldown);
return 1;
} else if (fastcmp(word,"hyubgone")) {
lua_pushinteger(L, hyubgone);
return 1;
} else if (fastcmp(word,"thwompsactive")) {
lua_pushboolean(L, thwompsactive);
return 1;
@ -468,8 +465,6 @@ int LUA_WriteGlobals(lua_State *L, const char *word)
exitcountdown = (tic_t)luaL_checkinteger(L, 2);
else if (fastcmp(word,"indirectitemcooldown"))
indirectitemcooldown = (tic_t)luaL_checkinteger(L, 2);
else if (fastcmp(word,"hyubgone"))
hyubgone = (tic_t)luaL_checkinteger(L, 2);
else
return 0;

View file

@ -37,6 +37,8 @@
#define M_TAU_FIXED 411769
#endif
#define M_PI_FIXED (M_TAU_FIXED >> 1)
typedef INT32 fixed_t;
/*!

View file

@ -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
@ -3119,26 +3120,6 @@ void M_Drawer(void)
M_GetGametypeColor();
currentMenu->drawroutine(); // call current menu Draw routine
}
// Draw version down in corner
// ... but only in the MAIN MENU. I'm a picky bastard.
if (currentMenu == &MainDef)
{
if (customversionstring[0] != '\0')
{
V_DrawThinString(vid.dupx, vid.height - 20*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT, "Mod version:");
V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, customversionstring);
}
else
{
#ifdef DEVELOP // Development -- show revision / branch info
V_DrawThinString(vid.dupx, vid.height - 20*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, compbranch);
V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, comprevision);
#else // Regular build
V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING));
#endif
}
}
}
// focus lost notification goes on top of everything, even the former everything
@ -9665,12 +9646,12 @@ static void M_DrawSetupMultiPlayerMenu(void)
// Fake the follower's in game appearance by now also applying some of its variables! coolio, eh?
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);
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);
// smooth floating, totally not stolen from rocket sneakers.
fixed_t sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * followertimer)>>ANGLETOFINESHIFT) & FINEMASK));
UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, setupm_fakecolor->color, 0); // why does GTC_MENUCACHE not work here...?
V_DrawFixedPatch((mx+65)*FRACUNIT, ((my+131)*FRACUNIT)-fl.zoffs+sine, fl.scale, flags, patch, colormap);
Z_Free(colormap);
}
}
@ -9682,7 +9663,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
static void M_GetFollowerState(void)
{
if (setupm_fakefollower <= -1 || setupm_fakefollower > numfollowers-1) // yikes, there's none!
if (setupm_fakefollower <= -1 || setupm_fakefollower >= numfollowers) // yikes, there's none!
return;
// ^ we don't actually need to set anything since it won't be displayed anyway.
@ -9830,10 +9811,10 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
// check followers:
if (setupm_fakefollower < -1)
{
setupm_fakefollower = numfollowers-1;
setupm_fakefollower = numfollowers;
M_GetFollowerState(); // update follower state
}
if (setupm_fakefollower > numfollowers-1)
if (setupm_fakefollower >= numfollowers)
{
setupm_fakefollower = -1;
M_GetFollowerState(); // update follower state
@ -9878,7 +9859,7 @@ static void M_SetupMultiPlayer(INT32 choice)
setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value
// yikes, we don't want none of that...
if (setupm_fakefollower > numfollowers-1)
if (setupm_fakefollower >= numfollowers)
setupm_fakefollower = -1;
M_GetFollowerState(); // update follower state
@ -9921,7 +9902,7 @@ static void M_SetupMultiPlayer2(INT32 choice)
setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value
// yikes, we don't want none of that...
if (setupm_fakefollower > numfollowers-1)
if (setupm_fakefollower >= numfollowers)
setupm_fakefollower = -1;
M_GetFollowerState(); // update follower state
@ -9964,7 +9945,7 @@ static void M_SetupMultiPlayer3(INT32 choice)
setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value
// yikes, we don't want none of that...
if (setupm_fakefollower > numfollowers-1)
if (setupm_fakefollower >= numfollowers)
setupm_fakefollower = -1;
M_GetFollowerState(); // update follower state
@ -10007,7 +9988,7 @@ static void M_SetupMultiPlayer4(INT32 choice)
setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value
// yikes, we don't want none of that...
if (setupm_fakefollower > numfollowers-1)
if (setupm_fakefollower >= numfollowers)
setupm_fakefollower = -1;
M_GetFollowerState(); // update follower state

1
src/objects/Sourcefile Normal file
View file

@ -0,0 +1 @@
hyudoro.c

481
src/objects/hyudoro.c Normal file
View file

@ -0,0 +1,481 @@
#include "../doomdef.h"
#include "../doomstat.h"
#include "../info.h"
#include "../k_kart.h"
#include "../k_objects.h"
#include "../m_random.h"
#include "../p_local.h"
#include "../r_main.h"
#include "../s_sound.h"
enum {
HYU_PATROL,
HYU_RETURN,
HYU_HOVER,
};
// TODO: make these general functions
static fixed_t
K_GetSpeed (mobj_t *mobj)
{
return FixedHypot(mobj->momx, mobj->momy);
}
static void
K_ChangePlayerItem
( player_t * player,
INT32 itemtype,
INT32 itemamount)
{
player->itemtype = itemtype;
player->itemamount = itemamount;
K_UnsetItemOut(player);
}
#define hyudoro_mode(o) ((o)->extravalue1)
#define hyudoro_itemtype(o) ((o)->movefactor)
#define hyudoro_itemcount(o) ((o)->movecount)
#define hyudoro_hover_stack(o) ((o)->threshold)
#define hyudoro_next(o) ((o)->tracer)
#define hyudoro_stackpos(o) ((o)->reactiontime)
// cannot be combined
#define hyudoro_center(o) ((o)->target)
#define hyudoro_target(o) ((o)->target)
#define hyudoro_center_max_radius(o) ((o)->threshold)
#define hyudoro_center_master(o) ((o)->target)
static angle_t
trace_angle (mobj_t *hyu)
{
mobj_t *center = hyu->target;
if (hyu->x != center->x || hyu->y != center->y)
{
return R_PointToAngle2(
center->x, center->y, hyu->x, hyu->y);
}
else
return hyu->angle;
}
static angle_t
get_look_angle (mobj_t *thing)
{
player_t *player = thing->player;
return player ? player->angleturn : thing->angle;
}
static boolean
is_hyudoro (mobj_t *thing)
{
return thing && thing->type == MT_HYUDORO;
}
static mobj_t *
get_hyudoro_master (mobj_t *hyu)
{
mobj_t *center = hyudoro_center(hyu);
return center ? hyudoro_center_master(center) : NULL;
}
static player_t *
get_hyudoro_target_player (mobj_t *hyu)
{
mobj_t *target = hyudoro_target(hyu);
return target ? target->player : NULL;
}
static void
sine_bob
( mobj_t * hyu,
angle_t a,
fixed_t sineofs)
{
hyu->sprzoff = FixedMul(hyu->height,
sineofs + FINESINE(a >> ANGLETOFINESHIFT));
}
static void
project_hyudoro (mobj_t *hyu)
{
mobj_t *center = hyudoro_center(hyu);
angle_t angleStep = FixedMul(5 * ANG1,
FixedDiv(hyudoro_center_max_radius(center),
center->radius));
angle_t angle = trace_angle(hyu) + angleStep;
fixed_t d = center->radius;
fixed_t x = P_ReturnThrustX(center, angle, d);
fixed_t y = P_ReturnThrustY(center, angle, d);
hyu->momx = (center->x + x) - hyu->x;
hyu->momy = (center->y + y) - hyu->y;
hyu->angle = angle + ANGLE_90;
sine_bob(hyu, angle, FRACUNIT);
}
static void
project_hyudoro_hover (mobj_t *hyu)
{
const INT32 bob_speed = 64;
mobj_t *target = hyudoro_target(hyu);
// Turns a bit toward its target
angle_t ang = get_look_angle(target) + ANGLE_67h;
fixed_t rad = (target->radius * 2) + hyu->radius;
fixed_t zofs = hyudoro_stackpos(hyu) *
((target->height / 2) + (hyu->height * 2));
P_MoveOrigin(hyu,
target->x - P_ReturnThrustX(hyu, ang, rad),
target->y - P_ReturnThrustY(hyu, ang, rad),
target->z + (zofs * P_MobjFlip(target)));
// Cancel momentum from HYU_RETURN.
// (And anything else! I don't trust this game!!)
hyu->momx = 0;
hyu->momy = 0;
hyu->angle = ang;
// copies sprite tilting
hyu->pitch = target->pitch;
hyu->roll = target->roll;
sine_bob(hyu,
(leveltime & (bob_speed - 1)) *
(ANGLE_MAX / bob_speed), -(3*FRACUNIT/4));
}
static void
spawn_hyudoro_shadow (mobj_t *hyu)
{
mobj_t *shadow = P_SpawnMobjFromMobj(
hyu, 0, 0, 0, MT_SHADOW);
shadow->whiteshadow = true;
shadow->shadowscale = hyu->shadowscale;
hyu->shadowscale = 0;
P_SetTarget(&shadow->tracer, hyu);
}
static void
move_to_player (mobj_t *hyu)
{
mobj_t *target = hyudoro_target(hyu);
angle_t angle;
if (!target)
return;
angle = R_PointToAngle2(
hyu->x, hyu->y, target->x, target->y);
P_InstaThrust(hyu, angle, (hyu->radius / 2) +
max(hyu->radius, K_GetSpeed(target)));
hyu->z = target->z; // stay level with target
hyu->angle = angle;
}
static void
deliver_item (mobj_t *hyu)
{
mobj_t *target = hyudoro_target(hyu);
player_t *player = target->player;
P_SetTarget(&hyudoro_target(hyu), NULL);
if (player)
{
K_ChangePlayerItem(player,
hyudoro_itemtype(hyu),
player->itemamount + hyudoro_itemcount(hyu));
}
S_StartSound(target, sfx_itpick);
// Stop moving here
hyu->momx = 0;
hyu->momy = 0;
hyu->tics = 4;
hyu->destscale = target->scale / 4;
hyu->scalespeed =
abs(hyu->scale - hyu->destscale) / hyu->tics;
}
static void
append_hyudoro
( mobj_t ** head,
mobj_t * hyu)
{
INT32 lastpos = 0;
while (is_hyudoro(*head))
{
lastpos = hyudoro_stackpos(*head);
head = &hyudoro_next(*head);
}
hyudoro_stackpos(hyu) = lastpos + 1;
*head = hyu;
}
static void
pop_hyudoro (mobj_t **head)
{
mobj_t *hyu = *head;
INT32 lastpos;
INT32 thispos;
if (is_hyudoro(hyu))
{
lastpos = hyudoro_stackpos(hyu);
hyu = hyudoro_next(hyu);
while (is_hyudoro(hyu))
{
thispos = hyudoro_stackpos(hyu);
hyudoro_stackpos(hyu) = lastpos;
lastpos = thispos;
hyu = hyudoro_next(hyu);
}
}
*head = hyu;
}
static boolean
hyudoro_patrol_hit_player
( mobj_t * hyu,
mobj_t * toucher)
{
player_t *player = toucher->player;
mobj_t *center = hyudoro_center(hyu);
if (!player)
return false;
// Cannot hit its master
if (toucher == get_hyudoro_master(hyu))
return false;
// Don't punish a punished player
if (player->hyudorotimer)
return false;
// NO ITEM?
if (!player->itemamount)
return false;
K_AddHitLag(toucher, TICRATE/2, true);
hyudoro_mode(hyu) = HYU_RETURN;
hyudoro_itemtype(hyu) = player->itemtype;
hyudoro_itemcount(hyu) = player->itemamount;
K_StripItems(player);
player->hyudorotimer = hyudorotime;
player->stealingtimer = hyudorotime;
P_SetTarget(&hyudoro_target(hyu),
hyudoro_center_master(center));
if (center)
P_RemoveMobj(center);
hyu->renderflags &= ~(RF_DONTDRAW);
return true;
}
static boolean
award_immediately (mobj_t *hyu)
{
player_t *player = get_hyudoro_target_player(hyu);
if (player)
{
if (player->itemamount &&
player->itemtype != hyudoro_itemtype(hyu))
{
return false;
}
// Same as picking up paper items; get stacks
// immediately
if (!P_CanPickupItem(player, 3))
return false;
}
deliver_item(hyu);
return true;
}
static boolean
hyudoro_return_hit_player
( mobj_t * hyu,
mobj_t * toucher)
{
if (toucher != hyudoro_target(hyu))
return false;
// If the player already has an item, just hover beside
// them until they use/lose it.
if (!award_immediately(hyu))
{
hyudoro_mode(hyu) = HYU_HOVER;
append_hyudoro(&toucher->player->hoverhyudoro, hyu);
}
return true;
}
static boolean
hyudoro_hover_await_stack (mobj_t *hyu)
{
player_t *player = get_hyudoro_target_player(hyu);
if (!player)
return false;
// First in stack goes first
if (hyu != player->hoverhyudoro)
return false;
if (!award_immediately(hyu))
return false;
pop_hyudoro(&player->hoverhyudoro);
return true;
}
void
Obj_HyudoroDeploy (mobj_t *master)
{
mobj_t *center = P_SpawnMobjFromMobj(
master, 0, 0, 0, MT_HYUDORO_CENTER);
mobj_t *hyu = P_SpawnMobjFromMobj(
center, 0, 0, 0, MT_HYUDORO);
// This allows a Lua override
if (!hyudoro_center_max_radius(center))
{
hyudoro_center_max_radius(center) =
128 * center->scale;
}
center->radius = hyu->radius;
P_InitAngle(hyu, master->angle);
P_SetTarget(&hyudoro_center(hyu), center);
P_SetTarget(&hyudoro_center_master(center), master);
hyudoro_mode(hyu) = HYU_PATROL;
// Set splitscreen player visibility
if (master->player)
{
hyu->renderflags |= RF_DONTDRAW &
~(K_GetPlayerDontDrawFlag(master->player));
}
spawn_hyudoro_shadow(hyu); // this sucks btw
S_StartSound(master, sfx_s3k92); // scary ghost noise
}
void
Obj_HyudoroThink (mobj_t *hyu)
{
// Might get set from clipping slopes
hyu->momz = 0;
switch (hyudoro_mode(hyu))
{
case HYU_PATROL:
if (hyudoro_center(hyu))
project_hyudoro(hyu);
if (leveltime & 1)
{
mobj_t *ghost = P_SpawnGhostMobj(hyu);
// Flickers every frame
ghost->extravalue1 = 1;
ghost->extravalue2 = 2;
// copy per-splitscreen-player visibility
ghost->renderflags =
(hyu->renderflags & RF_DONTDRAW);
ghost->tics = 8;
P_SetTarget(&ghost->tracer, hyu);
}
break;
case HYU_RETURN:
move_to_player(hyu);
break;
case HYU_HOVER:
if (hyudoro_target(hyu))
{
project_hyudoro_hover(hyu);
hyudoro_hover_await_stack(hyu);
}
break;
}
}
void
Obj_HyudoroCenterThink (mobj_t *center)
{
fixed_t max_radius = hyudoro_center_max_radius(center);
if (center->radius < max_radius)
center->radius += max_radius / 64;
}
void
Obj_HyudoroCollide
( mobj_t * hyu,
mobj_t * toucher)
{
switch (hyudoro_mode(hyu))
{
case HYU_PATROL:
hyudoro_patrol_hit_player(hyu, toucher);
break;
case HYU_RETURN:
hyudoro_return_hit_player(hyu, toucher);
break;
}
}

View file

@ -4018,7 +4018,7 @@ void A_AttractChase(mobj_t *actor)
{
fixed_t dist = (4*actor->target->scale) * (16 - actor->extravalue1);
P_SetScale(actor, (actor->destscale = actor->target->scale - ((actor->target->scale/14) * actor->extravalue1)));
P_SetScale(actor, (actor->destscale = mapobjectscale - ((mapobjectscale/14) * actor->extravalue1)));
actor->z = actor->target->z;
K_MatchGenericExtraFlags(actor, actor->target);
P_MoveOrigin(actor,
@ -4573,26 +4573,26 @@ void A_ShootBullet(mobj_t *actor)
static mobj_t *minus;
static boolean PIT_MinusCarry(mobj_t *thing)
static BlockItReturn_t PIT_MinusCarry(mobj_t *thing)
{
if (minus->tracer)
return true;
return BMIT_CONTINUE;
if (minus->type == thing->type)
return true;
return BMIT_CONTINUE;
if (!(thing->flags & (MF_PUSHABLE|MF_ENEMY)))
return true;
return BMIT_CONTINUE;
if (P_AproxDistance(minus->x - thing->x, minus->y - thing->y) >= minus->radius*3)
return true;
return BMIT_CONTINUE;
if (abs(thing->z - minus->z) > minus->height)
return true;
return BMIT_CONTINUE;
P_SetTarget(&minus->tracer, thing);
return true;
return BMIT_CONTINUE;
}
// Function: A_MinusDigging
@ -12145,13 +12145,13 @@ static mobj_t *barrel;
static fixed_t exploderadius;
static fixed_t explodethrust;
static boolean PIT_TNTExplode(mobj_t *nearby)
static BlockItReturn_t PIT_TNTExplode(mobj_t *nearby)
{
fixed_t dx, dy, dz;
fixed_t dm;
if (nearby == barrel)
return true;
return BMIT_CONTINUE;
dx = nearby->x - barrel->x;
dy = nearby->y - barrel->y;
@ -12159,7 +12159,7 @@ static boolean PIT_TNTExplode(mobj_t *nearby)
dm = P_AproxDistance(P_AproxDistance(dx, dy), dz);
if (dm >= exploderadius || !P_CheckSight(barrel, nearby)) // out of range or not visible
return true;
return BMIT_CONTINUE;
if (barrel->type == nearby->type) // nearby is also a barrel
{
@ -12200,7 +12200,7 @@ static boolean PIT_TNTExplode(mobj_t *nearby)
}
}
return true;
return BMIT_CONTINUE;
}
// Function: A_TNTExplode

View file

@ -37,6 +37,7 @@
#include "k_boss.h"
#include "k_respawn.h"
#include "p_spec.h"
#include "k_objects.h"
// CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
@ -483,6 +484,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
S_StartSound(toucher, sfx_s1b2);
return;
case MT_HYUDORO:
Obj_HyudoroCollide(special, toucher);
return;
case MT_RING:
case MT_FLINGRING:
if (special->extravalue1)
@ -1767,8 +1772,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
player->carry = CR_NONE;
player->mo->color = player->skincolor;
player->mo->colorized = false;
K_KartResetPlayerColor(player);
P_ResetPlayer(player);

View file

@ -435,7 +435,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
fixed_t P_FloorzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height);
fixed_t P_CeilingzAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height);
boolean PIT_PushableMoved(mobj_t *thing);
BlockItReturn_t PIT_PushableMoved(mobj_t *thing);
boolean P_DoSpring(mobj_t *spring, mobj_t *object);

File diff suppressed because it is too large Load diff

View file

@ -1096,7 +1096,7 @@ void P_SetPrecipitationThingPosition(precipmobj_t *thing)
// to P_BlockLinesIterator, then make one or more calls
// to it.
//
boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
boolean P_BlockLinesIterator(INT32 x, INT32 y, BlockItReturn_t (*func)(line_t *))
{
INT32 offset;
const INT32 *list; // Big blockmap
@ -1122,12 +1122,23 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
for (i = 0; i < po->numLines; ++i)
{
BlockItReturn_t ret = BMIT_CONTINUE;
if (po->lines[i]->validcount == validcount) // line has been checked
continue;
po->lines[i]->validcount = validcount;
if (!func(po->lines[i]))
ret = func(po->lines[i]);
if (ret == BMIT_ABORT)
{
return false;
}
else if (ret == BMIT_STOP)
{
return true;
}
}
}
plink = (polymaplink_t *)(plink->link.next);
}
@ -1137,16 +1148,25 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
// First index is really empty, so +1 it.
for (list = blockmaplump + offset + 1; *list != -1; list++)
{
BlockItReturn_t ret = BMIT_CONTINUE;
ld = &lines[*list];
if (ld->validcount == validcount)
continue; // Line has already been checked.
ld->validcount = validcount;
ret = func(ld);
if (!func(ld))
if (ret == BMIT_ABORT)
{
return false;
}
else if (ret == BMIT_STOP)
{
return true;
}
}
return true; // Everything was checked.
}
@ -1154,7 +1174,7 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
//
// P_BlockThingsIterator
//
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
boolean P_BlockThingsIterator(INT32 x, INT32 y, BlockItReturn_t (*func)(mobj_t *))
{
mobj_t *mobj, *bnext = NULL;
@ -1164,19 +1184,25 @@ boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
// Check interaction with the objects in the blockmap.
for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
{
BlockItReturn_t ret = BMIT_CONTINUE;
P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
if (!func(mobj))
ret = func(mobj);
if (ret == BMIT_ABORT)
{
P_SetTarget(&bnext, NULL);
return false;
return false; // failure
}
if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue.
if ((ret == BMIT_STOP)
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
{
P_SetTarget(&bnext, NULL);
return true;
return true; // success
}
}
return true;
}
@ -1220,7 +1246,7 @@ static void P_CheckIntercepts(void)
// are on opposite sides of the trace.
// Returns true if earlyout and a solid line hit.
//
static boolean PIT_AddLineIntercepts(line_t *ld)
static BlockItReturn_t PIT_AddLineIntercepts(line_t *ld)
{
INT32 s1, s2;
fixed_t frac;
@ -1243,18 +1269,18 @@ static boolean PIT_AddLineIntercepts(line_t *ld)
}
if (s1 == s2)
return true; // Line isn't crossed.
return BMIT_CONTINUE; // Line isn't crossed.
// Hit the line.
P_MakeDivline(ld, &dl);
frac = P_InterceptVector(&trace, &dl);
if (frac < 0)
return true; // Behind source.
return BMIT_CONTINUE; // Behind source.
// Try to take an early out of the check.
if (earlyout && frac < FRACUNIT && !ld->backsector)
return false; // stop checking
return BMIT_ABORT; // stop checking
P_CheckIntercepts();
@ -1263,13 +1289,13 @@ static boolean PIT_AddLineIntercepts(line_t *ld)
intercept_p->d.line = ld;
intercept_p++;
return true; // continue
return BMIT_CONTINUE; // continue
}
//
// PIT_AddThingIntercepts
//
static boolean PIT_AddThingIntercepts(mobj_t *thing)
static BlockItReturn_t PIT_AddThingIntercepts(mobj_t *thing)
{
fixed_t px1, py1, px2, py2, frac;
INT32 s1, s2;
@ -1300,7 +1326,7 @@ static boolean PIT_AddThingIntercepts(mobj_t *thing)
s2 = P_PointOnDivlineSide(px2, py2, &trace);
if (s1 == s2)
return true; // Line isn't crossed.
return BMIT_CONTINUE; // Line isn't crossed.
dl.x = px1;
dl.y = py1;
@ -1310,7 +1336,7 @@ static boolean PIT_AddThingIntercepts(mobj_t *thing)
frac = P_InterceptVector(&trace, &dl);
if (frac < 0)
return true; // Behind source.
return BMIT_CONTINUE; // Behind source.
P_CheckIntercepts();
@ -1319,7 +1345,7 @@ static boolean PIT_AddThingIntercepts(mobj_t *thing)
intercept_p->d.thing = thing;
intercept_p++;
return true; // Keep going.
return BMIT_CONTINUE; // Keep going.
}
//

View file

@ -67,8 +67,15 @@ extern INT32 opentoppic, openbottompic;
void P_LineOpening(line_t *plinedef, mobj_t *mobj);
boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean(*func)(line_t *));
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *));
typedef enum
{
BMIT_CONTINUE, // Continue blockmap search
BMIT_STOP, // End blockmap search with success
BMIT_ABORT // End blockmap search with failure
} BlockItReturn_t;
boolean P_BlockLinesIterator(INT32 x, INT32 y, BlockItReturn_t(*func)(line_t *));
boolean P_BlockThingsIterator(INT32 x, INT32 y, BlockItReturn_t(*func)(mobj_t *));
#define PT_ADDLINES 1
#define PT_ADDTHINGS 2

View file

@ -44,6 +44,7 @@
#include "k_bot.h"
#include "k_terrain.h"
#include "k_collide.h"
#include "k_objects.h"
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
@ -5300,6 +5301,20 @@ static void P_MobjSceneryThink(mobj_t *mobj)
switch (mobj->type)
{
case MT_SHADOW:
if (mobj->tracer)
{
P_MoveOrigin(mobj,
mobj->tracer->x,
mobj->tracer->y,
mobj->tracer->z);
}
else
{
P_RemoveMobj(mobj);
return;
}
break;
case MT_BOSSJUNK:
mobj->renderflags ^= RF_DONTDRAW;
break;
@ -7671,6 +7686,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
break;
}
case MT_HYUDORO:
{
Obj_HyudoroThink(mobj);
break;
}
case MT_HYUDORO_CENTER:
{
Obj_HyudoroCenterThink(mobj);
break;
}
case MT_ROCKETSNEAKER:
if (!mobj->target || !mobj->target->health)
{
@ -8597,8 +8622,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
if (mobj->extravalue1)
{
const INT32 speed = 6*TICRATE; // longer is slower
const fixed_t pi = 22*FRACUNIT/7; // Inaccurate, but is close enough for our usage
fixed_t sine = FINESINE((((2*pi*speed) * leveltime) >> ANGLETOFINESHIFT) & FINEMASK) * flip;
fixed_t sine = FINESINE((((M_TAU_FIXED * speed) * leveltime) >> ANGLETOFINESHIFT) & FINEMASK) * flip;
// Flying capsules are flipped upside-down, like S3K
flip = -flip;
@ -9186,14 +9210,19 @@ void P_MobjThinker(mobj_t *mobj)
{
if (mobj->extravalue2 >= 2)
{
UINT32 dontdraw = RF_DONTDRAW;
if (mobj->tracer)
dontdraw &= ~(mobj->tracer->renderflags);
if (mobj->extravalue2 == 2) // I don't know why the normal logic doesn't work for this.
mobj->renderflags ^= RF_DONTDRAW;
mobj->renderflags ^= dontdraw;
else
{
if (mobj->fuse == mobj->extravalue2)
mobj->renderflags &= ~RF_DONTDRAW;
mobj->renderflags &= ~(dontdraw);
else
mobj->renderflags |= RF_DONTDRAW;
mobj->renderflags |= dontdraw;
}
}
}
@ -9654,6 +9683,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
case MT_SSMINE_SHIELD:
case MT_LANDMINE:
case MT_BALLHOG:
case MT_HYUDORO:
case MT_SINK:
case MT_ROCKETSNEAKER:
case MT_SPB:

View file

@ -63,6 +63,7 @@ typedef enum
FOLLOWER = 0x04,
SKYBOXVIEW = 0x08,
SKYBOXCENTER = 0x10,
HOVERHYUDORO = 0x20,
} player_saveflags;
static inline void P_ArchivePlayer(void)
@ -196,6 +197,9 @@ static void P_NetArchivePlayers(void)
if (players[i].skybox.centerpoint)
flags |= SKYBOXCENTER;
if (players[i].hoverhyudoro)
flags |= HOVERHYUDORO;
WRITEUINT16(save_p, flags);
if (flags & SKYBOXVIEW)
@ -210,6 +214,9 @@ static void P_NetArchivePlayers(void)
if (flags & FOLLOWITEM)
WRITEUINT32(save_p, players[i].followmobj->mobjnum);
if (flags & HOVERHYUDORO)
WRITEUINT32(save_p, players[i].hoverhyudoro->mobjnum);
WRITEUINT32(save_p, (UINT32)players[i].followitem);
WRITEUINT32(save_p, players[i].charflags);
@ -487,6 +494,9 @@ static void P_NetUnArchivePlayers(void)
if (flags & FOLLOWITEM)
players[i].followmobj = (mobj_t *)(size_t)READUINT32(save_p);
if (flags & HOVERHYUDORO)
players[i].hoverhyudoro = (mobj_t *)(size_t)READUINT32(save_p);
players[i].followitem = (mobjtype_t)READUINT32(save_p);
//SetPlayerSkinByNum(i, players[i].skin);
@ -523,7 +533,7 @@ static void P_NetUnArchivePlayers(void)
players[i].tumbleBounces = READUINT8(save_p);
players[i].tumbleHeight = READUINT16(save_p);
players[i].justDI = (boolean)READUINT8(save_p);
players[i].justDI = READUINT8(save_p);
players[i].flipDI = (boolean)READUINT8(save_p);
players[i].drift = READSINT8(save_p);
@ -4240,6 +4250,13 @@ static void P_RelinkPointers(void)
CONS_Debug(DBG_GAMELOGIC, "respawn.wp not found on %d\n", mobj->type);
}
}
if (mobj->player->hoverhyudoro)
{
temp = (UINT32)(size_t)mobj->player->hoverhyudoro;
mobj->player->hoverhyudoro = NULL;
if (!P_SetTarget(&mobj->player->hoverhyudoro, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "hoverhyudoro not found on %d\n", mobj->type);
}
}
}
}
@ -4492,7 +4509,6 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT32(save_p, wantedcalcdelay);
WRITEUINT32(save_p, indirectitemcooldown);
WRITEUINT32(save_p, hyubgone);
WRITEUINT32(save_p, mapreset);
for (i = 0; i < MAXPLAYERS; i++)
@ -4642,7 +4658,6 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
wantedcalcdelay = READUINT32(save_p);
indirectitemcooldown = READUINT32(save_p);
hyubgone = READUINT32(save_p);
mapreset = READUINT32(save_p);
for (i = 0; i < MAXPLAYERS; i++)

View file

@ -4405,7 +4405,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
wantedcalcdelay = wantedfrequency*2;
indirectitemcooldown = 0;
hyubgone = 0;
mapreset = 0;
for (i = 0; i < MAXPLAYERS; i++)

View file

@ -8390,13 +8390,13 @@ static pusher_t *tmpusher; // pusher structure for blockmap searches
* ::tmpusher won't need to be used.
* \sa T_Pusher
*/
static inline boolean PIT_PushThing(mobj_t *thing)
static inline BlockItReturn_t PIT_PushThing(mobj_t *thing)
{
if (thing->eflags & MFE_PUSHED)
return false;
return BMIT_ABORT;
if (!tmpusher->source)
return false;
return BMIT_ABORT;
// Allow this to affect pushable objects at some point?
if (thing->player && !(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)))
@ -8416,7 +8416,7 @@ static inline boolean PIT_PushThing(mobj_t *thing)
{
// Make sure the Z is in range
if (thing->z < sz - tmpusher->radius || thing->z > sz + tmpusher->radius)
return false;
return BMIT_ABORT;
dist = P_AproxDistance(P_AproxDistance(thing->x - sx, thing->y - sy),
thing->z - sz);
@ -8482,7 +8482,7 @@ static inline boolean PIT_PushThing(mobj_t *thing)
if (tmpusher->exclusive)
thing->eflags |= MFE_PUSHED;
return true;
return BMIT_CONTINUE;
}
/** Applies a pusher to all affected objects.

View file

@ -685,8 +685,6 @@ void P_Ticker(boolean run)
if (indirectitemcooldown > 0)
indirectitemcooldown--;
if (hyubgone > 0)
hyubgone--;
K_BossInfoTicker();

View file

@ -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"
@ -1691,8 +1692,7 @@ static void P_CheckInvincibilityTimer(player_t *player)
// Resume normal music stuff.
if (player->invincibilitytimer == 1)
{
player->mo->color = player->skincolor;
player->mo->colorized = false;
//K_KartResetPlayerColor(player); -- this gets called every tic anyways
G_GhostAddColor((INT32) (player - players), GHC_NORMAL);
P_RestoreMusic(player);
@ -2143,7 +2143,7 @@ void P_MovePlayer(player_t *player)
if (cmd->turning == 0)
{
player->justDI = false;
player->justDI = 0;
}
// Kart frames
@ -2680,8 +2680,7 @@ static void P_DeathThink(player_t *player)
if (!player->mo)
return;
player->mo->colorized = false;
player->mo->color = player->skincolor;
//K_KartResetPlayerColor(player); -- called at death, don't think we need to re-establish
P_CalcHeight(player);
}
@ -3962,289 +3961,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
@ -4371,9 +4087,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--;
@ -4788,11 +4501,15 @@ void P_PlayerAfterThink(player_t *player)
if (player->playerstate == PST_DEAD)
{
// Followers need handled while dead.
K_HandleFollower(player);
if (player->followmobj)
{
P_RemoveMobj(player->followmobj);
P_SetTarget(&player->followmobj, NULL);
}
return;
}
@ -4865,6 +4582,10 @@ void P_PlayerAfterThink(player_t *player)
}
}
}
// Run followers in AfterThink, after the players have moved,
// so a lag value of 1 is exactly attached to the player.
K_HandleFollower(player);
}
void P_SetPlayerAngle(player_t *player, angle_t angle)

View file

@ -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);

View file

@ -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)

View file

@ -27,6 +27,9 @@
#include "p_local.h"
#include "dehacked.h" // get_number (for thok)
#include "m_cond.h"
#if 0
#include "k_kart.h" // K_KartResetPlayerColor
#endif
#ifdef HWRENDER
#include "hardware/hw_md2.h"
#endif
@ -34,8 +37,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 +45,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.
@ -285,7 +283,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
{
player_t *player = &players[playernum];
skin_t *skin = &skins[skinnum];
UINT16 newcolor = 0;
//UINT16 newcolor = 0;
//UINT8 i;
if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists!
@ -311,6 +309,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
}
player->skincolor = newcolor = skin->prefcolor;
K_KartResetPlayerColor(player);
}
#endif
@ -323,12 +322,6 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
if (player->mo)
{
player->mo->skin = skin;
if (newcolor)
{
player->mo->color = newcolor;
}
P_SetScale(player->mo, player->mo->scale);
P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames
}
@ -860,97 +853,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.
}

View file

@ -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__