mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'master' into bot-rubberband-edit
This commit is contained in:
commit
8815b065b3
45 changed files with 1988 additions and 965 deletions
|
|
@ -302,6 +302,7 @@ target_compile_definitions(SRB2SDL2 PRIVATE -DCMAKECONFIG)
|
|||
#)
|
||||
|
||||
add_subdirectory(sdl)
|
||||
add_subdirectory(objects)
|
||||
|
||||
if(${CMAKE_SYSTEM} MATCHES Windows)
|
||||
add_subdirectory(win32)
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -118,3 +118,4 @@ k_hud.c
|
|||
k_terrain.c
|
||||
k_brightmap.c
|
||||
k_director.c
|
||||
k_follower.c
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@
|
|||
#include "k_respawn.h"
|
||||
#include "k_grandprix.h"
|
||||
#include "k_boss.h"
|
||||
#include "k_follower.h"
|
||||
#include "doomstat.h"
|
||||
#include "deh_tables.h"
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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].bubblescale = 0; // No bubble by default
|
||||
followers[numfollowers].atangle = FixedAngle(230 * FRACUNIT);
|
||||
followers[numfollowers].dist = 32*FRACUNIT; // changed from 16 to 32 to better account for ogl models
|
||||
followers[numfollowers].height = 16*FRACUNIT;
|
||||
followers[numfollowers].zoffs = 32*FRACUNIT;
|
||||
followers[numfollowers].horzlag = 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,16 +3965,19 @@ void readfollower(MYFILE *f)
|
|||
}
|
||||
else if (fastcmp(word, "HITTIME") || (fastcmp(word, "HITCONFIRMTIME")))
|
||||
{
|
||||
followers[numfollowers].hitconfirmtime = (INT32)atoi(word2);
|
||||
followers[numfollowers].hitconfirmtime = (tic_t)get_number(word2);
|
||||
}
|
||||
else
|
||||
{
|
||||
deh_warning("Follower %d: unknown word '%s'", numfollowers, word);
|
||||
}
|
||||
}
|
||||
} while (!myfeof(f)); // finish when the line is empty
|
||||
|
||||
if (!nameset) // well this is problematic.
|
||||
if (!nameset)
|
||||
{
|
||||
strcpy(followers[numfollowers].name, va("Follower%d", numfollowers)); // this is lazy, so what
|
||||
// well this is problematic.
|
||||
strcpy(followers[numfollowers].name, va("Follower%d", numfollowers)); // this is lazy, so what
|
||||
}
|
||||
|
||||
// set skin name (this is just the follower's name in lowercases):
|
||||
|
|
@ -3966,7 +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.
|
||||
|
||||
|
|
@ -4036,7 +4065,7 @@ if (!followers[numfollowers].field) \
|
|||
#undef NOSTATE
|
||||
|
||||
CONS_Printf("Added follower '%s'\n", dname);
|
||||
numfollowers++; // add 1 follower
|
||||
numfollowers++; // add 1 follower
|
||||
Z_Free(s);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
86
src/info.c
86
src/info.c
|
|
@ -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
|
||||
|
|
|
|||
11
src/info.h
11
src/info.h
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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
567
src/k_follower.c
Normal 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
143
src/k_follower.h
Normal 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__
|
||||
269
src/k_kart.c
269
src/k_kart.c
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -2812,7 +2805,7 @@ void K_PlayHitEmSound(mobj_t *source, mobj_t *victim)
|
|||
if (source->player->follower)
|
||||
{
|
||||
follower_t fl = followers[source->player->followerskin];
|
||||
source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers.
|
||||
source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers.
|
||||
}
|
||||
|
||||
if (cv_kartvoices.value)
|
||||
|
|
@ -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,45 +7710,57 @@ 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)
|
||||
{
|
||||
fullbright = true;
|
||||
|
|
@ -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,78 +10259,72 @@ 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
|
||||
{
|
||||
INT16 aimingcompare = abs(cmd->throwdir) - abs(cmd->turning);
|
||||
|
||||
// "COOL" timing n shit.
|
||||
if (cmd->turning || player->throwdir)
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
P_InstaThrust(player->mo, player->mo->angle - lr, max(basespeed, speed*5/2));
|
||||
player->trickpanel = 3;
|
||||
}
|
||||
}
|
||||
else if (aimingcompare > TRICKTHRESHOLD) // forward/back trick
|
||||
{
|
||||
if (cmd->throwdir > 0) // back trick
|
||||
{
|
||||
if (player->mo->momz * P_MobjFlip(player->mo) > 0)
|
||||
{
|
||||
player->mo->momz = 0;
|
||||
}
|
||||
|
||||
P_InstaThrust(player->mo, player->mo->angle, max(basespeed, speed*3));
|
||||
player->trickpanel = 2;
|
||||
}
|
||||
else if (cmd->throwdir < 0)
|
||||
{
|
||||
boolean relative = true;
|
||||
|
||||
player->mo->momx /= 3;
|
||||
player->mo->momy /= 3;
|
||||
|
||||
if (player->mo->momz * P_MobjFlip(player->mo) <= 0)
|
||||
{
|
||||
relative = false;
|
||||
}
|
||||
|
||||
// Calculate speed boost decay:
|
||||
// Base speed boost duration is 35 tics.
|
||||
// At most, lose 3/4th of your boost.
|
||||
player->trickboostdecay = min(TICRATE*3/4, abs(momz/FRACUNIT));
|
||||
//CONS_Printf("decay: %d\n", player->trickboostdecay);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Uses cmd->turning over steering intentionally.
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (player->mo->momz * P_MobjFlip(player->mo) > 0)
|
||||
{
|
||||
player->mo->momz = 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
boolean relative = true;
|
||||
|
||||
player->mo->momx /= 3;
|
||||
player->mo->momy /= 3;
|
||||
|
||||
if (player->mo->momz * P_MobjFlip(player->mo) <= 0)
|
||||
{
|
||||
relative = false;
|
||||
}
|
||||
|
||||
// Calculate speed boost decay:
|
||||
// Base speed boost duration is 35 tics.
|
||||
// At most, lose 3/4th of your boost.
|
||||
player->trickboostdecay = min(TICRATE*3/4, abs(momz/FRACUNIT));
|
||||
//CONS_Printf("decay: %d\n", player->trickboostdecay);
|
||||
|
||||
P_SetObjectMomZ(player->mo, 48*FRACUNIT, relative);
|
||||
player->trickpanel = 4;
|
||||
|
||||
player->mo->hitlag = TRICKLAG;
|
||||
player->mo->eflags &= ~MFE_DAMAGEHITLAG;
|
||||
|
||||
K_trickPanelTimingVisual(player, momz);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (player->trickpanel == 4 && P_IsObjectOnGround(player->mo)) // Upwards trick landed!
|
||||
|
|
@ -10507,7 +10534,7 @@ void K_HandleDirectionalInfluence(player_t *player)
|
|||
}
|
||||
|
||||
// DI attempted!!
|
||||
player->justDI = true;
|
||||
player->justDI = MAXHITLAGTICS;
|
||||
|
||||
cmd = &player->cmd;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
11
src/k_objects.h
Normal 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*/
|
||||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@
|
|||
#define M_TAU_FIXED 411769
|
||||
#endif
|
||||
|
||||
#define M_PI_FIXED (M_TAU_FIXED >> 1)
|
||||
|
||||
typedef INT32 fixed_t;
|
||||
|
||||
/*!
|
||||
|
|
|
|||
49
src/m_menu.c
49
src/m_menu.c
|
|
@ -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,13 +9811,13 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
|
|||
// check followers:
|
||||
if (setupm_fakefollower < -1)
|
||||
{
|
||||
setupm_fakefollower = numfollowers-1;
|
||||
M_GetFollowerState(); // update follower state
|
||||
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
|
||||
M_GetFollowerState(); // update follower state
|
||||
}
|
||||
|
||||
// check color
|
||||
|
|
@ -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
1
src/objects/Sourcefile
Normal file
|
|
@ -0,0 +1 @@
|
|||
hyudoro.c
|
||||
481
src/objects/hyudoro.c
Normal file
481
src/objects/hyudoro.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
390
src/p_map.c
390
src/p_map.c
File diff suppressed because it is too large
Load diff
|
|
@ -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,11 +1122,22 @@ 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,15 +1148,24 @@ 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.
|
||||
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, 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.
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
40
src/p_mobj.c
40
src/p_mobj.c
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
10
src/p_spec.c
10
src/p_spec.c
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -685,8 +685,6 @@ void P_Ticker(boolean run)
|
|||
|
||||
if (indirectitemcooldown > 0)
|
||||
indirectitemcooldown--;
|
||||
if (hyubgone > 0)
|
||||
hyubgone--;
|
||||
|
||||
K_BossInfoTicker();
|
||||
|
||||
|
|
|
|||
303
src/p_user.c
303
src/p_user.c
|
|
@ -56,6 +56,7 @@
|
|||
#include "k_boss.h"
|
||||
#include "k_terrain.h" // K_SpawnSplashForMobj
|
||||
#include "k_color.h"
|
||||
#include "k_follower.h"
|
||||
|
||||
#ifdef HW3SOUND
|
||||
#include "hardware/hw3sound.h"
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -40,9 +40,6 @@ extern INT16 color8to16[256]; // remap color index to highcolor
|
|||
extern INT16 *hicolormaps; // remap high colors to high colors..
|
||||
|
||||
extern CV_PossibleValue_t Color_cons_t[];
|
||||
extern CV_PossibleValue_t Followercolor_cons_t[]; // follower colours table, not a duplicate because of the "Match" option.
|
||||
#define FOLLOWERCOLOR_MATCH UINT16_MAX
|
||||
#define FOLLOWERCOLOR_OPPOSITE (UINT16_MAX-1)
|
||||
|
||||
// I/O, setting up the stuff.
|
||||
void R_InitTextureData(void);
|
||||
|
|
|
|||
|
|
@ -194,7 +194,6 @@ static INT32 CacheIndexToSkin(INT32 ttc)
|
|||
}
|
||||
|
||||
CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1];
|
||||
CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL
|
||||
|
||||
#define TRANSTAB_AMTMUL10 (255.0f / 10.0f)
|
||||
|
||||
|
|
|
|||
111
src/r_skins.c
111
src/r_skins.c
|
|
@ -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.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,51 +90,4 @@ void R_AddSkins(UINT16 wadnum);
|
|||
|
||||
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player);
|
||||
|
||||
// SRB2Kart Followers
|
||||
|
||||
//
|
||||
// We'll define these here because they're really just a mobj that'll follow some rules behind a player
|
||||
//
|
||||
typedef struct follower_s
|
||||
{
|
||||
char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything.
|
||||
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
|
||||
|
||||
UINT16 defaultcolor; // default color for menus.
|
||||
|
||||
fixed_t scale; // Scale relative to the player's.
|
||||
fixed_t bubblescale; // Bubble scale relative to the player scale. If not set, no bubble will spawn (default)
|
||||
|
||||
// some position shenanigans:
|
||||
INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player.
|
||||
INT32 dist; // distance relative to the player. (In a circle)
|
||||
INT32 height; // height of the follower, this is mostly important for Z flipping.
|
||||
INT32 zoffs; // Z offset relative to the player's height. Cannot be negative.
|
||||
|
||||
// movement options
|
||||
|
||||
UINT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number.
|
||||
UINT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 6. Must be > 0 because we divide by this number.
|
||||
INT32 bobamp; // Bob amplitude. Default is 4.
|
||||
INT32 bobspeed; // Arbitrary modifier for bobbing speed, default is TICRATE*2 (70).
|
||||
|
||||
// from there on out, everything is STATES to allow customization
|
||||
// these are only set once when the action is performed and are then free to animate however they want.
|
||||
|
||||
INT32 idlestate; // state when the player is at a standstill
|
||||
INT32 followstate; // state when the player is moving
|
||||
INT32 hurtstate; // state when the player is being hurt
|
||||
INT32 winstate; // state when the player has won
|
||||
INT32 losestate; // state when the player has lost
|
||||
INT32 hitconfirmstate; // state for hit confirm
|
||||
UINT32 hitconfirmtime; // time to keep the above playing for
|
||||
} follower_t;
|
||||
|
||||
extern INT32 numfollowers;
|
||||
extern follower_t followers[MAXSKINS]; // again, use the same rules as skins, no reason not to.
|
||||
|
||||
INT32 R_FollowerAvailable(const char *name);
|
||||
boolean SetPlayerFollower(INT32 playernum,const char *skinname);
|
||||
void SetFollower(INT32 playernum,INT32 skinnum);
|
||||
|
||||
#endif //__R_SKINS__
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue