Merge branch 'master' into rng-states-2

This commit is contained in:
Sally Coolatta 2022-09-23 11:23:49 -04:00
commit d582cf0a3f
38 changed files with 1574 additions and 874 deletions

View file

@ -36,6 +36,7 @@
#include "d_netfil.h" // findfile
#include "r_data.h" // Color_cons_t
#include "r_skins.h"
#include "m_random.h"
//========
// protos.
@ -53,6 +54,8 @@ static void COM_Wait_f(void);
static void COM_Help_f(void);
static void COM_Toggle_f(void);
static void COM_Add_f(void);
static void COM_Choose_f(void);
static void COM_ChooseWeighted_f(void);
static void CV_EnforceExecVersion(void);
static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr);
@ -361,6 +364,8 @@ void COM_Init(void)
COM_AddCommand("help", COM_Help_f);
COM_AddCommand("toggle", COM_Toggle_f);
COM_AddCommand("add", COM_Add_f);
COM_AddCommand("choose", COM_Choose_f);
COM_AddCommand("chooseweighted", COM_ChooseWeighted_f);
RegisterNetXCmd(XD_NETVAR, Got_NetVar);
}
@ -1075,6 +1080,81 @@ static void COM_Add_f(void)
CV_AddValue(cvar, atoi(COM_Argv(2)));
}
static void COM_Choose_f(void)
{
size_t na = COM_Argc();
if (na < 2)
{
CONS_Printf(M_GetText("choose <option1> [<option2>] [<option3>] [...]: Picks a command at random\n"));
return;
}
COM_BufAddText(COM_Argv(M_RandomKey(na - 1) + 1));
COM_BufAddText("\n");
}
static void COM_ChooseWeighted_f(void)
{
size_t na = COM_Argc();
size_t i, cmd;
const char *commands[40];
INT32 weights[40];
INT32 totalWeight = 0;
INT32 roll;
if (na < 3)
{
CONS_Printf(M_GetText("chooseweighted <option1> <weight1> [<option2> <weight2>] [<option3> <weight3>] [...]: Picks a command with weighted randomization\n"));
return;
}
memset(weights, 0, sizeof(weights));
i = 1;
cmd = 0;
while (i < na)
{
commands[cmd] = COM_Argv(i);
i++;
if (i >= na)
{
break;
}
weights[cmd] = atoi(COM_Argv(i));
totalWeight += weights[cmd];
i++;
cmd++;
}
if (cmd == 0 || totalWeight <= 0)
{
return;
}
roll = M_RandomRange(1, totalWeight);
for (i = 0; i < cmd; i++)
{
if (roll <= weights[i])
{
if (commands[i] == NULL)
{
break;
}
COM_BufAddText(commands[i]);
COM_BufAddText("\n");
break;
}
roll -= weights[i];
}
}
// =========================================================================
// VARIABLE SIZE BUFFERS
// =========================================================================

View file

@ -34,7 +34,6 @@
#include "k_menu.h"
#include "filesrch.h"
#include "m_misc.h"
#include "m_random.h"
#ifdef _WINDOWS
#include "win32/win_main.h"
@ -244,81 +243,6 @@ static void CONS_Bind_f(void)
bindtable[key] = Z_StrDup(COM_Argv(2));
}
static void CONS_Choose_f(void)
{
size_t na = COM_Argc();
if (na < 2)
{
CONS_Printf(M_GetText("choose <option1> [<option2>] [<option3>] [...]: Picks a command at random\n"));
return;
}
COM_BufAddText(COM_Argv(M_RandomKey(na - 1) + 1));
COM_BufAddText("\n");
}
static void CONS_ChooseWeighted_f(void)
{
size_t na = COM_Argc();
size_t i, cmd;
const char *commands[40];
INT32 weights[40];
INT32 totalWeight = 0;
INT32 roll;
if (na < 3)
{
CONS_Printf(M_GetText("chooseweighted <option1> <weight1> [<option2> <weight2>] [<option3> <weight3>] [...]: Picks a command with weighted randomization\n"));
return;
}
memset(weights, 0, sizeof(weights));
i = 1;
cmd = 0;
while (i < na)
{
commands[cmd] = COM_Argv(i);
i++;
if (i >= na)
{
break;
}
weights[cmd] = atoi(COM_Argv(i));
totalWeight += weights[cmd];
i++;
cmd++;
}
if (cmd == 0 || totalWeight <= 0)
{
return;
}
roll = M_RandomRange(1, totalWeight);
for (i = 0; i < cmd; i++)
{
if (roll <= weights[i])
{
if (commands[i] == NULL)
{
break;
}
COM_BufAddText(commands[i]);
COM_BufAddText("\n");
break;
}
roll -= weights[i];
}
}
//======================================================================
// CONSOLE SETUP
//======================================================================
@ -521,8 +445,6 @@ void CON_Init(void)
CV_RegisterVar(&cons_backpic);
CV_RegisterVar(&cons_backcolor);
COM_AddCommand("bind", CONS_Bind_f);
COM_AddCommand("choose", CONS_Choose_f);
COM_AddCommand("chooseweighted", CONS_ChooseWeighted_f);
}
else
{

View file

@ -114,6 +114,8 @@ UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
static tic_t lowest_lag;
boolean server_lagless;
static CV_PossibleValue_t mindelay_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}};
consvar_t cv_mindelay = CVAR_INIT ("mindelay", "2", CV_SAVE, mindelay_cons_t, NULL);
SINT8 nodetoplayer[MAXNETNODES];
SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
@ -5660,7 +5662,7 @@ static inline void PingUpdate(void)
if (nodeingame[i])
HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
pingmeasurecount = 1; //Reset count
pingmeasurecount = 0; //Reset count
}
static tic_t gametime = 0;
@ -5674,7 +5676,7 @@ static void UpdatePingTable(void)
if (server)
{
if (netgame && !(gametime % 35)) // update once per second.
if (Playing() && !(gametime % 35)) // update once per second.
PingUpdate();
fastest = 0;
@ -5700,6 +5702,10 @@ static void UpdatePingTable(void)
}
}
// Don't gentleman below your mindelay
if (fastest < (tic_t)cv_mindelay.value)
fastest = (tic_t)cv_mindelay.value;
pingmeasurecount++;
if (server_lagless)
@ -5731,6 +5737,11 @@ static void UpdatePingTable(void)
}
}
}
else // We're a client, handle mindelay on the way out.
{
if ((neededtic - gametic) < (tic_t)cv_mindelay.value)
lowest_lag = cv_mindelay.value - (neededtic - gametic);
}
}
static void RenewHolePunch(void)

View file

@ -445,6 +445,7 @@ extern UINT32 playerpingtable[MAXPLAYERS];
extern tic_t servermaxping;
extern boolean server_lagless;
extern consvar_t cv_mindelay;
extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxconnections, cv_joindelay;
extern consvar_t cv_resynchattempts, cv_blamecfail;

View file

@ -1781,16 +1781,16 @@ void D_SRB2Main(void)
}
}
// Has to be done before anything else so skin, color, etc in command buffer has an affect.
// ttlprofilen used because it's roughly equivalent in functionality - a QoL aid for quickly getting from startup to action
PR_ApplyProfile(cv_ttlprofilen.value, 0);
if (autostart || netgame)
{
gameaction = ga_nothing;
CV_ClearChangedFlags();
// Has to be done before anything else so skin, color, etc in command buffer has an affect.
// ttlprofilen used because it's roughly equivalent in functionality - a QoL aid for quickly getting from startup to action
PR_ApplyProfile(cv_ttlprofilen.value, 0);
// Do this here so if you run SRB2 with eg +timelimit 5, the time limit counts
// as having been modified for the first game.
M_PushSpecialParameters(); // push all "+" parameter at the command buffer
@ -1893,9 +1893,13 @@ void D_SRB2Main(void)
else if (M_CheckParm("-skipintro"))
{
F_StartTitleScreen();
CV_StealthSetValue(&cv_currprofile, -1);
}
else
{
F_StartIntro(); // Tails 03-03-2002
CV_StealthSetValue(&cv_currprofile, -1);
}
CON_ToggleOff();

View file

@ -972,6 +972,7 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_rollingdemos);
CV_RegisterVar(&cv_netstat);
CV_RegisterVar(&cv_netticbuffer);
CV_RegisterVar(&cv_mindelay);
#ifdef NETGAME_DEVMODE
CV_RegisterVar(&cv_fishcake);

View file

@ -268,7 +268,9 @@ typedef enum
#define TUMBLEBOUNCES 3
#define TUMBLEGRAVITY (4*FRACUNIT)
#define TRIPWIRETIME (TICRATE)
#define TRIPWIRETIME (15)
#define BALLHOGINCREMENT (7)
//}
@ -486,9 +488,11 @@ typedef struct player_s
UINT16 flamemeter; // Flame Shield dash meter left
UINT8 flamelength; // Flame Shield dash meter, number of segments
UINT16 ballhogcharge; // Ballhog charge up -- the higher this value, the more projectiles
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
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
@ -586,6 +590,8 @@ typedef struct player_s
UINT8 shrinkLaserDelay;
mobj_t *stumbleIndicator;
#ifdef HWRENDER
fixed_t fovadd; // adjust FOV for hw rendering
#endif

View file

@ -640,242 +640,6 @@ void readlight(MYFILE *f, INT32 num)
}
#endif // HWRENDER
static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word, *word2;
char *tmp;
INT32 value;
char *lastline;
do
{
lastline = f->curpos;
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
// First remove trailing newline, if there is one
tmp = strchr(s, '\n');
if (tmp)
*tmp = '\0';
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
// Set / reset word
word = s;
while ((*word == '\t') || (*word == ' '))
word++;
// Get the part before the " = "
tmp = strchr(s, '=');
if (tmp)
{
*(tmp-1) = '\0';
// Now get the part after
word2 = tmp += 2;
}
else
{
// Get the part before the " "
tmp = strchr(s, ' ');
if (tmp)
{
*tmp = '\0';
// Now get the part after
tmp++;
word2 = tmp;
}
else
break;
}
strupr(word);
value = atoi(word2); // used for numerical settings
if (fastcmp(word, "XPIVOT"))
sprinfo->pivot[frame].x = value;
else if (fastcmp(word, "YPIVOT"))
sprinfo->pivot[frame].y = value;
else if (fastcmp(word, "ROTAXIS"))
sprinfo->pivot[frame].rotaxis = value;
else
{
f->curpos = lastline;
break;
}
}
} while (!myfeof(f)); // finish when the line is empty
Z_Free(s);
}
void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word, *word2;
char *tmp;
#ifdef HWRENDER
INT32 value;
#endif
char *lastline;
INT32 skinnumbers[MAXSKINS];
INT32 foundskins = 0;
// allocate a spriteinfo
spriteinfo_t *info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL);
info->available = true;
do
{
lastline = f->curpos;
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
// First remove trailing newline, if there is one
tmp = strchr(s, '\n');
if (tmp)
*tmp = '\0';
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
// Set / reset word
word = s;
while ((*word == '\t') || (*word == ' '))
word++;
// Get the part before the " = "
tmp = strchr(s, '=');
if (tmp)
{
*(tmp-1) = '\0';
// Now get the part after
word2 = tmp += 2;
}
else
{
// Get the part before the " "
tmp = strchr(s, ' ');
if (tmp)
{
*tmp = '\0';
// Now get the part after
tmp++;
word2 = tmp;
}
else
break;
}
strupr(word);
#ifdef HWRENDER
value = atoi(word2); // used for numerical settings
if (fastcmp(word, "LIGHTTYPE"))
{
if (sprite2)
deh_warning("Sprite2 %s: invalid word '%s'", spr2names[num], word);
else
{
INT32 oldvar;
for (oldvar = 0; t_lspr[num] != &lspr[oldvar]; oldvar++)
;
t_lspr[num] = &lspr[value];
}
}
else
#endif
if (fastcmp(word, "SKIN"))
{
INT32 skinnum = -1;
if (!sprite2)
{
deh_warning("Sprite %s: %s keyword found outside of SPRITE2INFO block, ignoring", spr2names[num], word);
continue;
}
// make lowercase
strlwr(word2);
skinnum = R_SkinAvailable(word2);
if (skinnum == -1)
{
deh_warning("Sprite2 %s: unknown skin %s", spr2names[num], word2);
break;
}
skinnumbers[foundskins] = skinnum;
foundskins++;
}
else if (fastcmp(word, "DEFAULT"))
{
if (!sprite2)
{
deh_warning("Sprite %s: %s keyword found outside of SPRITE2INFO block, ignoring", spr2names[num], word);
continue;
}
if (num < (INT32)free_spr2 && num >= (INT32)SPR2_FIRSTFREESLOT)
spr2defaults[num] = get_number(word2);
else
{
deh_warning("Sprite2 %s: out of range (%d - %d), ignoring", spr2names[num], SPR2_FIRSTFREESLOT, free_spr2-1);
continue;
}
}
else if (fastcmp(word, "FRAME"))
{
UINT8 frame = R_Char2Frame(word2[0]);
// frame number too high
if (frame >= 64)
{
if (sprite2)
deh_warning("Sprite2 %s: invalid frame %s", spr2names[num], word2);
else
deh_warning("Sprite %s: invalid frame %s", sprnames[num], word2);
break;
}
// read sprite frame and store it in the spriteinfo_t struct
readspriteframe(f, info, frame);
if (sprite2)
{
INT32 i;
if (!foundskins)
{
deh_warning("Sprite2 %s: no skins specified", spr2names[num]);
break;
}
for (i = 0; i < foundskins; i++)
{
size_t skinnum = skinnumbers[i];
skin_t *skin = &skins[skinnum];
spriteinfo_t *sprinfo = skin->sprinfo;
M_Memcpy(&sprinfo[num], info, sizeof(spriteinfo_t));
}
}
else
M_Memcpy(&spriteinfo[num], info, sizeof(spriteinfo_t));
}
else
{
//deh_warning("Sprite %s: unknown word '%s'", sprnames[num], word);
f->curpos = lastline;
break;
}
}
} while (!myfeof(f)); // finish when the line is empty
Z_Free(s);
Z_Free(info);
}
void readsprite2(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);

View file

@ -71,7 +71,6 @@ void readcutscene(MYFILE *f, INT32 num);
void readlevelheader(MYFILE *f, INT32 num);
void readgametype(MYFILE *f, char *gtname);
void readsprite2(MYFILE *f, INT32 num);
void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2);
#ifdef HWRENDER
void readlight(MYFILE *f, INT32 num);
#endif

View file

@ -337,6 +337,7 @@ actionpointer_t actionpointers[] =
{{A_ReaperThinker}, "A_REAPERTHINKER"},
{{A_FlameShieldPaper}, "A_FLAMESHIELDPAPER"},
{{A_InvincSparkleRotate}, "A_INVINCSPARKLEROTATE"},
{{A_SpawnItemDebrisCloud}, "A_SPAWNITEMDEBRISCLOUD"},
{{NULL}, "NONE"},
@ -3277,6 +3278,10 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_RANDOMITEMPOP4",
//}
"S_ITEM_DEBRIS",
"S_ITEM_DEBRIS_CLOUD_SPAWNER1",
"S_ITEM_DEBRIS_CLOUD_SPAWNER2",
"S_ITEMICON",
// Item capsules
@ -3832,6 +3837,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_TRIPWIREBOOST_BLAST_TOP",
"S_TRIPWIREBOOST_BLAST_BOTTOM",
"S_SMOOTHLANDING",
// DEZ respawn laser
"S_DEZLASER",
"S_DEZLASER_TRAIL1",
@ -5309,6 +5316,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_BRAKEDRIFT",
"MT_BRAKEDUST",
"MT_DRIFTDUST",
"MT_ITEM_DEBRIS",
"MT_ITEM_DEBRIS_CLOUD_SPAWNER",
"MT_DRIFTELECTRICITY",
"MT_DRIFTELECTRICSPARK",
"MT_JANKSPARK",
@ -5376,6 +5385,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_TRIPWIREBOOST",
"MT_SMOOTHLANDING",
"MT_DEZLASER",
"MT_WAYPOINT",

View file

@ -356,30 +356,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
}
}
#endif
else if (fastcmp(word, "SPRITE") || fastcmp(word, "SPRITEINFO"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_sprite(word2); // find a sprite by name
if (i < NUMSPRITES && i > 0)
readspriteinfo(f, i, false);
else
{
deh_warning("Sprite number %d out of range (0 - %d)", i, NUMSPRITES-1);
ignorelines(f);
}
}
else if (fastcmp(word, "SPRITE2INFO"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_sprite2(word2); // find a sprite by name
if (i < NUMPLAYERSPRITES && i >= 0)
readspriteinfo(f, i, true);
else
{
deh_warning("Sprite2 number %d out of range (0 - %d)", i, NUMPLAYERSPRITES-1);
ignorelines(f);
}
}
else if (fastcmp(word, "LEVEL"))
{
// Support using the actual map name,

View file

@ -79,6 +79,7 @@ typedef enum
patch_t *pinggfx[5]; // small ping graphic
patch_t *mping[5]; // smaller ping graphic
patch_t *pingmeasure[2]; // ping measurement graphic
patch_t *pinglocal[2]; // mindelay indecator
patch_t *framecounter;
patch_t *frameslash; // framerate stuff. Used in screen.c
@ -197,6 +198,9 @@ void HU_LoadGraphics(void)
HU_UpdatePatch(&pingmeasure[0], "PINGD");
HU_UpdatePatch(&pingmeasure[1], "PINGMS");
HU_UpdatePatch(&pinglocal[0], "PINGGFXL");
HU_UpdatePatch(&pinglocal[1], "MPINGL");
// fps stuff
HU_UpdatePatch(&framecounter, "FRAMER");
HU_UpdatePatch(&frameslash, "FRAMESL");
@ -2334,37 +2338,65 @@ void HU_Erase(void)
static int
Ping_gfx_num (int lag)
{
if (lag < 2)
if (lag <= 2)
return 0;
else if (lag < 4)
else if (lag <= 4)
return 1;
else if (lag < 7)
else if (lag <= 7)
return 2;
else if (lag < 10)
else if (lag <= 10)
return 3;
else
return 4;
}
static int
Ping_gfx_color (int lag)
{
if (lag <= 2)
return SKINCOLOR_JAWZ;
else if (lag <= 4)
return SKINCOLOR_MINT;
else if (lag <= 7)
return SKINCOLOR_GOLD;
else if (lag <= 10)
return SKINCOLOR_RASPBERRY;
else
return SKINCOLOR_MAGENTA;
}
//
// HU_drawPing
//
void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags)
void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags, boolean offline)
{
UINT8 *colormap = NULL;
INT32 measureid = cv_pingmeasurement.value ? 1 : 0;
INT32 gfxnum; // gfx to draw
boolean drawlocal = (offline && cv_mindelay.value && lag <= (tic_t)cv_mindelay.value);
if (!server && lag <= (tic_t)cv_mindelay.value)
{
lag = cv_mindelay.value;
drawlocal = true;
}
gfxnum = Ping_gfx_num(lag);
if (measureid == 1)
V_DrawScaledPatch(x+11 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]);
V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]);
if (drawlocal)
V_DrawScaledPatch(x+2, y, flags, pinglocal[0]);
else
V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]);
colormap = R_GetTranslationColormap(TC_RAINBOW, Ping_gfx_color(lag), GTC_CACHE);
if (servermaxping && lag > servermaxping && hu_tick < 4)
{
// flash ping red if too high
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE);
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_WHITE, GTC_CACHE);
}
if (cv_pingmeasurement.value)
@ -2389,7 +2421,16 @@ HU_drawMiniPing (INT32 x, INT32 y, UINT32 lag, INT32 flags)
w /= 2;
}
patch = mping[Ping_gfx_num(lag)];
// This looks kinda dumb, but basically:
// Servers with mindelay set modify the ping table.
// Clients with mindelay unset don't, because they can't.
// Both are affected by mindelay, but a client's lag value is pre-adjustment.
if (server && cv_mindelay.value && (tic_t)cv_mindelay.value <= lag)
patch = pinglocal[1];
else if (!server && cv_mindelay.value && (tic_t)cv_mindelay.value >= lag)
patch = pinglocal[1];
else
patch = mping[Ping_gfx_num(lag)];
if (( flags & V_SNAPTORIGHT ))
x += ( w - SHORT (patch->width) );

View file

@ -138,7 +138,7 @@ void HU_Drawer(void);
char HU_dequeueChatChar(void);
void HU_Erase(void);
void HU_clearChatChars(void);
void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags); // Lat': Ping drawer for scoreboard.
void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags, boolean offline); // Lat': Ping drawer for scoreboard.
void HU_drawMiniPing(INT32 x, INT32 y, UINT32 ping, INT32 flags);
INT32 HU_CreateTeamScoresTbl(playersort_t *tab, UINT32 dmtotals[]);

View file

@ -530,6 +530,7 @@ char sprnames[NUMSPRITES + 1][5] =
"RNDM", // Random Item Box
"SBOX", // Sphere Box (for Battle)
"RPOP", // Random Item Box Pop
"ITRI", // Item Box Debris
"SGNS", // Signpost sparkle
"FAST", // Speed boost trail
"DSHR", // Speed boost dust release
@ -587,6 +588,7 @@ char sprnames[NUMSPRITES + 1][5] =
"BEXB", // Battle Bumper Explosion: Blast
"TWBS", // Tripwire Boost
"TWBT", // Tripwire BLASTER
"SMLD", // Smooth landing
"DEZL", // DEZ Laser respawn
// Additional Kart Objects
@ -3848,7 +3850,7 @@ state_t states[NUMSTATES] =
{SPR_RNDM, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM11}, // S_RANDOMITEM10
{SPR_RNDM, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM12}, // S_RANDOMITEM11
{SPR_RNDM, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM1}, // S_RANDOMITEM12
{SPR_NULL, 0, 0, {A_ItemPop}, 0, 0, S_NULL}, // S_DEADRANDOMITEM
{SPR_NULL, 0, 0, {A_ItemPop}, 0, 0, S_RANDOMITEM1}, // S_DEADRANDOMITEM
{SPR_SBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX2}, // S_SPHEREBOX1
{SPR_SBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX3}, // S_SPHEREBOX2
@ -3869,6 +3871,10 @@ state_t states[NUMSTATES] =
{SPR_RPOP, FF_FULLBRIGHT|2, 5, {NULL}, 0, 0, S_RANDOMITEMPOP4}, // S_RANDOMITEMPOP3
{SPR_RPOP, FF_FULLBRIGHT|3, 5, {NULL}, 0, 0, S_NULL}, // S_RANDOMITEMPOP4
{SPR_ITRI, FF_FULLBRIGHT|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 19, 1, S_NULL}, // S_ITEM_DEBRIS
{SPR_NULL, 0, 0, {A_Repeat}, 16, S_ITEM_DEBRIS_CLOUD_SPAWNER2, S_NULL}, // S_ITEM_DEBRIS_CLOUD_SPAWNER1
{SPR_NULL, 0, 7, {A_SpawnItemDebrisCloud}, 20, 0, S_ITEM_DEBRIS_CLOUD_SPAWNER1}, // S_ITEM_DEBRIS_CLOUD_SPAWNER2
{SPR_NULL, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMICON
{SPR_ICAP, FF_ADD|0, -1, {NULL}, 0, 0, S_NULL}, // S_ITEMCAPSULE
@ -4392,6 +4398,8 @@ state_t states[NUMSTATES] =
{SPR_TWBT, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE, -1, {NULL}, 6, 2, S_NULL}, // S_TRIPWIREBOOST_BLAST_TOP
{SPR_TWBT, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE|FF_VERTICALFLIP|FF_HORIZONTALFLIP, -1, {NULL}, 6, 2, S_NULL}, // S_TRIPWIREBOOST_BLAST_BOTTOM
{SPR_SMLD, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE, -1, {NULL}, 7, 2, S_NULL}, // S_SMOOTHLANDING
{SPR_DEZL, FF_FULLBRIGHT|FF_PAPERSPRITE, 8, {NULL}, 0, 0, S_NULL}, // S_DEZLASER
{SPR_DEZL, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL2}, // S_DEZLASER_TRAIL1
{SPR_DEZL, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL3}, // S_DEZLASER_TRAIL2
@ -22545,7 +22553,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MT_RANDOMITEMPOP, // damage
sfx_None, // activesound
MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
S_RANDOMITEM1 // raisestate
},
{ // MT_SPHEREBOX
@ -23142,6 +23150,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_ITEM_DEBRIS
-1, // doomednum
S_ITEM_DEBRIS, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
32*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_ITEM_DEBRIS_CLOUD_SPAWNER
-1, // doomednum
S_ITEM_DEBRIS_CLOUD_SPAWNER1, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
32*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOSECTOR|MF_NOBLOCKMAP|MF_RUNSPAWNFUNC, // flags
S_NULL // raisestate
},
{ // MT_DRIFTELECTRICITY
-1, // doomednum
S_DRIFTELECTRICITY, // spawnstate
@ -23753,7 +23815,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // xdeathstate
sfx_hogbom, // deathsound
80*FRACUNIT, // speed
16*FRACUNIT, // radius
26*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
100, // mass
@ -24411,6 +24473,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_SMOOTHLANDING
-1, // doomednum
S_SMOOTHLANDING, // 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
8*FRACUNIT, // radius
16*FRACUNIT, // height
-1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_DEZLASER
-1, // doomednum
S_DEZLASER, // spawnstate

View file

@ -290,6 +290,7 @@ enum actionnum
A_REAPERTHINKER,
A_FLAMESHIELDPAPER,
A_INVINCSPARKLEROTATE,
A_SPAWNITEMDEBRISCLOUD,
NUMACTIONS
};
@ -563,6 +564,7 @@ void A_ReaperThinker();
void A_MementosTPParticles();
void A_FlameShieldPaper();
void A_InvincSparkleRotate();
void A_SpawnItemDebrisCloud();
extern boolean actionsoverridden[NUMACTIONS];
@ -1076,6 +1078,7 @@ typedef enum sprite
SPR_RNDM, // Random Item Box
SPR_SBOX, // Sphere Box (for Battle)
SPR_RPOP, // Random Item Box Pop
SPR_ITRI, // Item Box Debris
SPR_SGNS, // Signpost sparkle
SPR_FAST, // Speed boost trail
SPR_DSHR, // Speed boost dust release
@ -1133,6 +1136,7 @@ typedef enum sprite
SPR_BEXB, // Battle Bumper Explosion: Blast
SPR_TWBS, // Tripwire Boost
SPR_TWBT, // Tripwire BLASTER
SPR_SMLD, // Smooth landing
SPR_DEZL, // DEZ Laser respawn
// Additional Kart Objects
@ -4272,6 +4276,10 @@ typedef enum state
S_RANDOMITEMPOP4,
//}
S_ITEM_DEBRIS,
S_ITEM_DEBRIS_CLOUD_SPAWNER1,
S_ITEM_DEBRIS_CLOUD_SPAWNER2,
S_ITEMICON,
// Item capsules
@ -4826,6 +4834,8 @@ typedef enum state
S_TRIPWIREBOOST_BLAST_TOP,
S_TRIPWIREBOOST_BLAST_BOTTOM,
S_SMOOTHLANDING,
// DEZ Laser respawn
S_DEZLASER,
S_DEZLASER_TRAIL1,
@ -6340,6 +6350,8 @@ typedef enum mobj_type
MT_BRAKEDRIFT,
MT_BRAKEDUST,
MT_DRIFTDUST,
MT_ITEM_DEBRIS,
MT_ITEM_DEBRIS_CLOUD_SPAWNER,
MT_DRIFTELECTRICITY,
MT_DRIFTELECTRICSPARK,
MT_JANKSPARK,
@ -6407,6 +6419,8 @@ typedef enum mobj_type
MT_TRIPWIREBOOST,
MT_SMOOTHLANDING,
MT_DEZLASER,
MT_WAYPOINT,

View file

@ -952,6 +952,55 @@ static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController)
}
}
/*--------------------------------------------------
static angle_t K_BotSmoothLanding(player_t *player, angle_t destangle)
Calculates a new destination angle while in the air,
to be able to successfully smooth land.
Input Arguments:-
player - Bot player to check.
destangle - Previous destination angle.
Return:-
New destination angle.
--------------------------------------------------*/
static angle_t K_BotSmoothLanding(player_t *player, angle_t destangle)
{
angle_t newAngle = destangle;
boolean air = !P_IsObjectOnGround(player->mo);
angle_t steepVal = air ? STUMBLE_STEEP_VAL_AIR : STUMBLE_STEEP_VAL;
angle_t slopeSteep = max(AngleDelta(player->mo->pitch, 0), AngleDelta(player->mo->roll, 0));
if (slopeSteep > steepVal)
{
fixed_t pitchMul = -FINESINE(destangle >> ANGLETOFINESHIFT);
fixed_t rollMul = FINECOSINE(destangle >> ANGLETOFINESHIFT);
angle_t testAngles[2];
angle_t testDeltas[2];
UINT8 i;
testAngles[0] = R_PointToAngle2(0, 0, rollMul, pitchMul);
testAngles[1] = R_PointToAngle2(0, 0, -rollMul, -pitchMul);
for (i = 0; i < 2; i++)
{
testDeltas[i] = AngleDelta(testAngles[i], destangle);
}
if (testDeltas[1] < testDeltas[0])
{
return testAngles[1];
}
else
{
return testAngles[0];
}
}
return newAngle;
}
/*--------------------------------------------------
static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *predict)
@ -975,6 +1024,8 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *
I_Assert(predict != NULL);
destangle = K_BotSmoothLanding(player, destangle);
moveangle = player->mo->angle;
anglediff = AngleDeltaSigned(moveangle, destangle);
@ -1105,6 +1156,8 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t
}
}
destangle = K_BotSmoothLanding(player, destangle);
// Calculate turn direction first.
moveangle = player->mo->angle;
angle = (moveangle - destangle);

View file

@ -11,6 +11,7 @@
#include "hu_stuff.h" // Sink snipe print
#include "doomdef.h" // Sink snipe print
#include "g_game.h" // Sink snipe print
#include "k_objects.h"
angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2)
{
@ -265,8 +266,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
}
else
{
mobj_t *poof = P_SpawnMobj(t1->x, t1->y, t1->z, MT_EXPLODE);
S_StartSound(poof, t1->info->deathsound);
Obj_SpawnItemDebrisEffects(t1, t2);
#if 0
// Eggbox snipe!

View file

@ -695,6 +695,40 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
}
}
static patch_t **K_GetItemPatchTable(INT32 item)
{
patch_t **kp[1 + NUMKARTITEMS] = {
kp_sadface,
NULL,
kp_sneaker,
kp_rocketsneaker,
kp_invincibility,
kp_banana,
kp_eggman,
kp_orbinaut,
kp_jawz,
kp_mine,
kp_landmine,
kp_ballhog,
kp_selfpropelledbomb,
kp_grow,
kp_shrink,
kp_lightningshield,
kp_bubbleshield,
kp_flameshield,
kp_hyudoro,
kp_pogospring,
kp_superring,
kp_kitchensink,
kp_droptarget,
};
if (item >= KITEM_SAD && item < NUMKARTITEMS)
return kp[item - KITEM_SAD];
else
return NULL;
}
//}
INT32 ITEM_X, ITEM_Y; // Item Window
@ -1096,90 +1130,23 @@ static void K_drawKartItem(void)
if (stplyr->itemroulette)
{
const INT32 item = K_GetRollingRouletteItem(stplyr);
if (stplyr->skincolor)
localcolor = stplyr->skincolor;
switch((stplyr->itemroulette % (16*3)) / 3)
switch (item)
{
// Each case is handled in threes, to give three frames of in-game time to see the item on the roulette
case 0: // Sneaker
localpatch = kp_sneaker[offset];
//localcolor = SKINCOLOR_RASPBERRY;
break;
case 1: // Banana
localpatch = kp_banana[offset];
//localcolor = SKINCOLOR_YELLOW;
break;
case 2: // Orbinaut
localpatch = kp_orbinaut[3+offset];
//localcolor = SKINCOLOR_STEEL;
break;
case 3: // Mine
localpatch = kp_mine[offset];
//localcolor = SKINCOLOR_JET;
break;
case 4: // Grow
localpatch = kp_grow[offset];
//localcolor = SKINCOLOR_TEAL;
break;
case 5: // Hyudoro
localpatch = kp_hyudoro[offset];
//localcolor = SKINCOLOR_STEEL;
break;
case 6: // Rocket Sneaker
localpatch = kp_rocketsneaker[offset];
//localcolor = SKINCOLOR_TANGERINE;
break;
case 7: // Jawz
localpatch = kp_jawz[offset];
//localcolor = SKINCOLOR_JAWZ;
break;
case 8: // Self-Propelled Bomb
localpatch = kp_selfpropelledbomb[offset];
//localcolor = SKINCOLOR_JET;
break;
case 9: // Shrink
localpatch = kp_shrink[offset];
//localcolor = SKINCOLOR_ORANGE;
break;
case 10: // Invincibility
case KITEM_INVINCIBILITY:
localpatch = localinv;
//localcolor = SKINCOLOR_GREY;
break;
case 11: // Eggman Monitor
localpatch = kp_eggman[offset];
//localcolor = SKINCOLOR_ROSE;
case KITEM_ORBINAUT:
localpatch = kp_orbinaut[3 + offset];
break;
case 12: // Ballhog
localpatch = kp_ballhog[offset];
//localcolor = SKINCOLOR_LILAC;
break;
case 13: // Lightning Shield
localpatch = kp_lightningshield[offset];
//localcolor = SKINCOLOR_CYAN;
break;
case 14: // Super Ring
localpatch = kp_superring[offset];
//localcolor = SKINCOLOR_GOLD;
break;
case 15: // Land Mine
localpatch = kp_landmine[offset];
//localcolor = SKINCOLOR_JET;
break;
case 16: // Drop Target
localpatch = kp_droptarget[offset];
//localcolor = SKINCOLOR_LIME;
break;
/*case 17: // Pogo Spring
localpatch = kp_pogospring[offset];
localcolor = SKINCOLOR_TANGERINE;
break;
case 18: // Kitchen Sink
localpatch = kp_kitchensink[offset];
localcolor = SKINCOLOR_STEEL;
break;*/
default:
break;
localpatch = K_GetItemPatchTable(item)[offset];
}
}
else
@ -1206,6 +1173,16 @@ static void K_drawKartItem(void)
else
localpatch = kp_nodraw;
}
else if (stplyr->ballhogcharge > 0)
{
itembar = stplyr->ballhogcharge;
maxl = (((stplyr->itemamount-1) * BALLHOGINCREMENT) + 1);
if (leveltime & 1)
localpatch = kp_ballhog[offset];
else
localpatch = kp_nodraw;
}
else if (stplyr->rocketsneakertimer > 1)
{
itembar = stplyr->rocketsneakertimer;
@ -1230,79 +1207,27 @@ static void K_drawKartItem(void)
switch(stplyr->itemtype)
{
case KITEM_SNEAKER:
localpatch = kp_sneaker[offset];
break;
case KITEM_ROCKETSNEAKER:
localpatch = kp_rocketsneaker[offset];
break;
case KITEM_INVINCIBILITY:
localpatch = localinv;
localbg = kp_itembg[offset+1];
break;
case KITEM_BANANA:
localpatch = kp_banana[offset];
break;
case KITEM_EGGMAN:
localpatch = kp_eggman[offset];
break;
case KITEM_ORBINAUT:
localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->itemamount-1, 3))];
break;
case KITEM_JAWZ:
localpatch = kp_jawz[offset];
break;
case KITEM_MINE:
localpatch = kp_mine[offset];
break;
case KITEM_LANDMINE:
localpatch = kp_landmine[offset];
break;
case KITEM_DROPTARGET:
localpatch = kp_droptarget[offset];
break;
case KITEM_BALLHOG:
localpatch = kp_ballhog[offset];
break;
case KITEM_SPB:
localpatch = kp_selfpropelledbomb[offset];
localbg = kp_itembg[offset+1];
break;
case KITEM_GROW:
localpatch = kp_grow[offset];
break;
case KITEM_SHRINK:
localpatch = kp_shrink[offset];
break;
case KITEM_LIGHTNINGSHIELD:
localpatch = kp_lightningshield[offset];
localbg = kp_itembg[offset+1];
break;
case KITEM_BUBBLESHIELD:
localpatch = kp_bubbleshield[offset];
localbg = kp_itembg[offset+1];
break;
case KITEM_FLAMESHIELD:
localpatch = kp_flameshield[offset];
localbg = kp_itembg[offset+1];
break;
case KITEM_HYUDORO:
localpatch = kp_hyudoro[offset];
break;
case KITEM_POGOSPRING:
localpatch = kp_pogospring[offset];
break;
case KITEM_SUPERRING:
localpatch = kp_superring[offset];
break;
case KITEM_KITCHENSINK:
localpatch = kp_kitchensink[offset];
break;
case KITEM_SAD:
localpatch = kp_sadface[offset];
break;
/*FALLTHRU*/
default:
localpatch = kp_nodraw; // diagnose underflows
localpatch = K_GetItemPatchTable(stplyr->itemtype)[offset];
if (localpatch == NULL)
localpatch = kp_nodraw; // diagnose underflows
break;
}
@ -2167,7 +2092,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
}
else if (tab[i].num != serverplayer || !server_lagless)
{
HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0);
HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0, false);
}
}
@ -4868,7 +4793,7 @@ void K_drawKartHUD(void)
V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY");
}
if (netgame && r_splitscreen)
if ((netgame || cv_mindelay.value) && r_splitscreen && Playing())
{
K_drawMiniPing();
}

View file

@ -19,6 +19,7 @@
#include "p_setup.h"
#include "r_draw.h"
#include "r_local.h"
#include "r_things.h"
#include "s_sound.h"
#include "st_stuff.h"
#include "v_video.h"
@ -349,7 +350,7 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] =
#define NUMKARTODDS 80
// Less ugly 2D arrays
static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
static UINT8 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
@ -382,7 +383,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
/*Jawz x2*/ { 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2
};
static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
{
//P-Odds 0 1
/*Sneaker*/ { 2, 1 }, // Sneaker
@ -493,6 +494,10 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem)
player->itemtype = KITEM_JAWZ;
player->itemamount = 2;
break;
case KITEM_BALLHOG: // Ballhog x5
player->itemtype = KITEM_BALLHOG;
player->itemamount = 5;
break;
default:
if (getitem <= 0 || getitem >= NUMKARTRESULTS) // Sad (Fallback)
{
@ -894,6 +899,54 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
return useodds;
}
INT32 K_GetRollingRouletteItem(player_t *player)
{
static UINT8 translation[NUMKARTITEMS-1];
static UINT16 roulette_size;
static INT16 odds_cached = -1;
// Race odds have more columns than Battle
const UINT8 EMPTYODDS[sizeof K_KartItemOddsRace[0]] = {0};
if (odds_cached != gametype)
{
UINT8 *odds_row;
size_t odds_row_size;
UINT8 i;
roulette_size = 0;
if (gametype == GT_BATTLE)
{
odds_row = K_KartItemOddsBattle[0];
odds_row_size = sizeof K_KartItemOddsBattle[0];
}
else
{
odds_row = K_KartItemOddsRace[0];
odds_row_size = sizeof K_KartItemOddsRace[0];
}
for (i = 1; i < NUMKARTITEMS; ++i)
{
if (memcmp(odds_row, EMPTYODDS, odds_row_size))
{
translation[roulette_size] = i;
roulette_size++;
}
odds_row += odds_row_size;
}
roulette_size *= 3;
odds_cached = gametype;
}
return translation[(player->itemroulette % roulette_size) / 3];
}
static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
{
INT32 i;
@ -3793,6 +3846,234 @@ void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source)
P_StartQuake(64<<FRACBITS, 10);
}
angle_t K_StumbleSlope(angle_t angle, angle_t pitch, angle_t roll)
{
fixed_t pitchMul = -FINESINE(angle >> ANGLETOFINESHIFT);
fixed_t rollMul = FINECOSINE(angle >> ANGLETOFINESHIFT);
angle_t slope = FixedMul(pitch, pitchMul) + FixedMul(roll, rollMul);
if (slope > ANGLE_180)
{
slope = InvAngle(slope);
}
return slope;
}
boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir)
{
angle_t steepVal = ANGLE_MAX;
fixed_t gravityadjust;
angle_t oldSlope, newSlope;
angle_t slopeDelta;
// If you don't land upright on a slope, then you tumble,
// kinda like Kirby Air Ride
if (player->tumbleBounces)
{
// Already tumbling.
return false;
}
if ((player->mo->pitch == oldPitch)
&& (player->mo->roll == oldRoll))
{
// No change.
return false;
}
if (fromAir == true)
{
steepVal = STUMBLE_STEEP_VAL_AIR;
}
else
{
steepVal = STUMBLE_STEEP_VAL;
}
oldSlope = K_StumbleSlope(player->mo->angle, oldPitch, oldRoll);
if (oldSlope <= steepVal)
{
// Transferring from flat ground to a steep slope
// is a free action. (The other way around isn't, though.)
return false;
}
newSlope = K_StumbleSlope(player->mo->angle, player->mo->pitch, player->mo->roll);
slopeDelta = AngleDelta(oldSlope, newSlope);
if (slopeDelta <= steepVal)
{
// Needs to be VERY steep before we'll punish this.
return false;
}
// Oh jeez, you landed on your side.
// You get to tumble.
P_ResetPlayer(player);
#if 0
// Single, medium bounce
player->tumbleBounces = TUMBLEBOUNCES;
player->tumbleHeight = 30;
#else
// Two small bounces
player->tumbleBounces = TUMBLEBOUNCES-1;
player->tumbleHeight = 20;
#endif
player->pflags &= ~PF_TUMBLESOUND;
S_StartSound(player->mo, sfx_s3k9b);
gravityadjust = P_GetMobjGravity(player->mo)/2; // so we'll halve it for our calculations.
if (player->mo->eflags & MFE_UNDERWATER)
gravityadjust /= 2; // halve "gravity" underwater
// and then modulate momz like that...
player->mo->momz = -gravityadjust * player->tumbleHeight;
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
if (P_IsDisplayPlayer(player))
P_StartQuake(64<<FRACBITS, 10);
// Reset slope.
player->mo->pitch = player->mo->roll = 0;
return true;
}
void K_InitStumbleIndicator(player_t *player)
{
mobj_t *new = NULL;
if (player == NULL)
{
return;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
return;
}
if (player->stumbleIndicator != NULL && P_MobjWasRemoved(player->stumbleIndicator) == false)
{
P_RemoveMobj(player->stumbleIndicator);
}
new = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SMOOTHLANDING);
P_SetTarget(&player->stumbleIndicator, new);
P_SetTarget(&new->target, player->mo);
}
void K_UpdateStumbleIndicator(player_t *player)
{
const angle_t fudge = ANG15;
mobj_t *mobj = NULL;
boolean air = false;
angle_t steepVal = STUMBLE_STEEP_VAL;
angle_t slopeSteep = 0;
angle_t steepRange = ANGLE_90;
INT32 delta = 0;
INT32 trans = 0;
if (player == NULL)
{
return;
}
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
{
return;
}
if (player->stumbleIndicator == NULL || P_MobjWasRemoved(player->stumbleIndicator) == true)
{
K_InitStumbleIndicator(player);
return;
}
mobj = player->stumbleIndicator;
P_MoveOrigin(mobj, player->mo->x, player->mo->y, player->mo->z + (player->mo->height / 2));
air = !P_IsObjectOnGround(player->mo);
steepVal = (air ? STUMBLE_STEEP_VAL_AIR : STUMBLE_STEEP_VAL) - fudge;
slopeSteep = max(AngleDelta(player->mo->pitch, 0), AngleDelta(player->mo->roll, 0));
delta = 0;
if (slopeSteep > steepVal)
{
angle_t testAngles[2];
INT32 testDeltas[2];
UINT8 i;
testAngles[0] = R_PointToAngle2(0, 0, player->mo->pitch, player->mo->roll);
testAngles[1] = R_PointToAngle2(0, 0, -player->mo->pitch, -player->mo->roll);
for (i = 0; i < 2; i++)
{
testDeltas[i] = AngleDeltaSigned(player->mo->angle, testAngles[i]);
}
if (abs(testDeltas[1]) < abs(testDeltas[0]))
{
delta = testDeltas[1];
}
else
{
delta = testDeltas[0];
}
}
if (delta < 0)
{
mobj->renderflags |= RF_HORIZONTALFLIP;
}
else
{
mobj->renderflags &= ~RF_HORIZONTALFLIP;
}
steepRange = ANGLE_90 - steepVal;
delta = max(0, abs(delta) - ((signed)steepVal));
trans = ((FixedDiv(AngleFixed(delta), AngleFixed(steepRange)) * (NUMTRANSMAPS - 2)) + (FRACUNIT/2)) / FRACUNIT;
if (trans < 0)
{
trans = 0;
}
if (trans > (NUMTRANSMAPS - 2))
{
trans = (NUMTRANSMAPS - 2);
}
// invert
trans = (NUMTRANSMAPS - 2) - trans;
mobj->renderflags |= RF_DONTDRAW;
if (trans < (NUMTRANSMAPS - 2))
{
mobj->renderflags &= ~(RF_TRANSMASK | K_GetPlayerDontDrawFlag(player));
if (trans != 0)
{
mobj->renderflags |= (trans << RF_TRANSSHIFT);
}
}
}
static boolean K_LastTumbleBounceCondition(player_t *player)
{
return (player->tumbleBounces > TUMBLEBOUNCES && player->tumbleHeight < 60);
@ -3827,6 +4108,7 @@ static void K_HandleTumbleBounce(player_t *player)
player->tumbleHeight = 10;
player->pflags |= PF_TUMBLELASTBOUNCE;
player->mo->rollangle = 0; // p_user.c will stop rotating the player automatically
player->mo->pitch = player->mo->roll = 0; // Prevent Kodachrome Void infinite
}
}
@ -4227,7 +4509,6 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I
if (source->player != NULL)
{
if (source->player->itemscale == ITEMSCALE_SHRINK)
{
// Nerf the base item speed a bit.
@ -4340,6 +4621,11 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I
S_StartSound(th, sfx_s3kbfl);
S_StartSound(th, sfx_cdfm35);
break;
case MT_BALLHOG:
// Contra spread shot scale up
th->destscale = th->destscale << 1;
th->scalespeed = abs(th->destscale - th->scale) / (2*TICRATE);
break;
default:
break;
}
@ -5094,7 +5380,7 @@ static mobj_t *K_FindLastTrailMobj(player_t *player)
return trail;
}
mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow)
mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow, angle_t angleOffset)
{
mobj_t *mo;
INT32 dir;
@ -5160,46 +5446,21 @@ mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing,
if (missile) // Shootables
{
if (mapthing == MT_BALLHOG) // Messy
if (dir == -1 && mapthing != MT_SPB)
{
mo = NULL; // can't return multiple projectiles
if (dir == -1)
{
// Shoot backward
K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x06000000, 0, PROJSPEED/8);
K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x03000000, 0, PROJSPEED/8);
K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8);
K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x03000000, 0, PROJSPEED/8);
K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x06000000, 0, PROJSPEED/8);
}
else
{
// Shoot forward
K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED);
K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x03000000, 0, PROJSPEED);
K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED);
K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x03000000, 0, PROJSPEED);
K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x06000000, 0, PROJSPEED);
}
// Shoot backward
mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + angleOffset, 0, PROJSPEED/8);
}
else
{
if (dir == -1 && mapthing != MT_SPB)
{
// Shoot backward
mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8);
}
else
{
// Shoot forward
mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED);
}
// Shoot forward
mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + angleOffset, 0, PROJSPEED);
}
if (mapthing == MT_DROPTARGET && mo)
{
mo->reactiontime = TICRATE/2;
P_SetMobjState(mo, mo->info->painstate);
}
if (mapthing == MT_DROPTARGET && mo)
{
mo->reactiontime = TICRATE/2;
P_SetMobjState(mo, mo->info->painstate);
}
}
else
@ -5561,7 +5822,6 @@ void K_DoSneaker(player_t *player, INT32 type)
if (type != 0)
{
player->pflags |= PF_ATTACKDOWN;
K_PlayBoostTaunt(player->mo);
}
player->sneakertimer = sneakertime;
@ -6071,6 +6331,10 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
newType = KITEM_JAWZ;
newAmount = 2;
break;
case KITEM_BALLHOG: // Ballhog x5
newType = KITEM_BALLHOG;
newAmount = 5;
break;
default:
newType = i;
newAmount = 1;
@ -7832,6 +8096,8 @@ void K_KartPlayerAfterThink(player_t *player)
{
K_KartResetPlayerColor(player);
K_UpdateStumbleIndicator(player);
// Move held objects (Bananas, Orbinaut, etc)
K_MoveHeldObjects(player);
@ -8931,7 +9197,7 @@ void K_StripItems(player_t *player)
player->curshield = KSHIELD_NONE;
player->bananadrag = 0;
player->ballhogcharge = 0;
player->sadtimer = 0;
K_UpdateHnextList(player, true);
@ -9273,31 +9539,7 @@ static void K_KartSpindash(player_t *player)
}
else if (player->fastfall != 0)
{
// Handle fastfall bounce.
const fixed_t maxBounce = player->mo->scale * 10;
const fixed_t minBounce = player->mo->scale;
fixed_t bounce = 2 * abs(player->fastfall) / 3;
if (bounce > maxBounce)
{
bounce = maxBounce;
}
else
{
// Lose speed on bad bounce.
player->mo->momx /= 2;
player->mo->momy /= 2;
if (bounce < minBounce)
{
bounce = minBounce;
}
}
S_StartSound(player->mo, sfx_ffbonc);
player->mo->momz = bounce * P_MobjFlip(player->mo);
player->fastfall = 0;
// Still handling fast-fall bounce.
return;
}
@ -9364,6 +9606,41 @@ static void K_KartSpindash(player_t *player)
#undef SPINDASHTHRUSTTIME
boolean K_FastFallBounce(player_t *player)
{
// Handle fastfall bounce.
if (player->fastfall != 0)
{
const fixed_t maxBounce = player->mo->scale * 10;
const fixed_t minBounce = player->mo->scale;
fixed_t bounce = 2 * abs(player->fastfall) / 3;
if (bounce > maxBounce)
{
bounce = maxBounce;
}
else
{
// Lose speed on bad bounce.
player->mo->momx /= 2;
player->mo->momy /= 2;
if (bounce < minBounce)
{
bounce = minBounce;
}
}
S_StartSound(player->mo, sfx_ffbonc);
player->mo->momz = bounce * P_MobjFlip(player->mo);
player->fastfall = 0;
return true;
}
return false;
}
static void K_AirFailsafe(player_t *player)
{
const fixed_t maxSpeed = 6*player->mo->scale;
@ -9651,7 +9928,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
{
if (ATTACK_IS_DOWN)
{
K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0);
K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->pflags &= ~PF_EGGMANOUT;
K_UpdateHnextList(player, true);
@ -9759,7 +10036,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT)) // Banana x3 thrown
{
K_ThrowKartItem(player, false, MT_BANANA, -1, 0);
K_ThrowKartItem(player, false, MT_BANANA, -1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
K_UpdateHnextList(player, false);
@ -9822,7 +10099,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT)) // Orbinaut x3 thrown
{
K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0);
K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
K_UpdateHnextList(player, false);
@ -9864,9 +10141,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->pflags & PF_ITEMOUT)) // Jawz thrown
{
if (player->throwdir == 1 || player->throwdir == 0)
K_ThrowKartItem(player, true, MT_JAWZ, 1, 0);
K_ThrowKartItem(player, true, MT_JAWZ, 1, 0, 0);
else if (player->throwdir == -1) // Throwing backward gives you a dud that doesn't home in
K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0);
K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
K_UpdateHnextList(player, false);
@ -9892,7 +10169,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT))
{
K_ThrowKartItem(player, false, MT_SSMINE, 1, 1);
K_ThrowKartItem(player, false, MT_SSMINE, 1, 1, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
player->pflags &= ~PF_ITEMOUT;
@ -9927,7 +10204,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT))
{
K_ThrowKartItem(player, (player->throwdir > 0), MT_DROPTARGET, -1, 0);
K_ThrowKartItem(player, (player->throwdir > 0), MT_DROPTARGET, -1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
player->pflags &= ~PF_ITEMOUT;
@ -9935,18 +10212,63 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
break;
case KITEM_BALLHOG:
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
if (!HOLDING_ITEM && NO_HYUDORO)
{
player->itemamount--;
K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0);
K_PlayAttackTaunt(player->mo);
INT32 ballhogmax = ((player->itemamount-1) * BALLHOGINCREMENT) + 1;
if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY)
&& (player->ballhogcharge < ballhogmax))
{
player->ballhogcharge++;
}
else
{
if (cmd->buttons & BT_ATTACK)
{
player->pflags &= ~PF_HOLDREADY;
}
else
{
player->pflags |= PF_HOLDREADY;
}
if (player->ballhogcharge > 0)
{
INT32 numhogs = min((player->ballhogcharge / BALLHOGINCREMENT) + 1, player->itemamount);
if (numhogs <= 1)
{
player->itemamount--;
K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0, 0);
}
else
{
angle_t cone = 0x01800000 * (numhogs-1);
angle_t offsetAmt = (cone * 2) / (numhogs-1);
angle_t angleOffset = cone;
INT32 i;
player->itemamount -= numhogs;
for (i = 0; i < numhogs; i++)
{
K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0, angleOffset);
angleOffset -= offsetAmt;
}
}
player->ballhogcharge = 0;
K_PlayAttackTaunt(player->mo);
player->pflags &= ~PF_HOLDREADY;
}
}
}
break;
case KITEM_SPB:
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
{
player->itemamount--;
K_ThrowKartItem(player, true, MT_SPB, 1, 0);
K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0);
K_PlayAttackTaunt(player->mo);
}
break;
@ -10048,7 +10370,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (player->bubbleblowup > bubbletime*2)
{
K_ThrowKartItem(player, (player->throwdir > 0), MT_BUBBLESHIELDTRAP, -1, 0);
K_ThrowKartItem(player, (player->throwdir > 0), MT_BUBBLESHIELDTRAP, -1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->bubbleblowup = 0;
player->bubblecool = 0;
@ -10198,7 +10520,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->pflags & PF_ITEMOUT)) // Sink thrown
{
K_ThrowKartItem(player, false, MT_SINK, 1, 2);
K_ThrowKartItem(player, false, MT_SINK, 1, 2, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
player->pflags &= ~PF_ITEMOUT;

View file

@ -28,6 +28,9 @@ Make sure this matches the actual number of states
#define GROW_PHYSICS_SCALE (3*FRACUNIT/2)
#define SHRINK_PHYSICS_SCALE (3*FRACUNIT/4)
#define STUMBLE_STEEP_VAL ANG60
#define STUMBLE_STEEP_VAL_AIR (ANG30 + ANG10)
player_t *K_GetItemBoxPlayer(mobj_t *mobj);
angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed);
@ -45,6 +48,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
fixed_t K_ItemOddsScale(UINT8 numPlayers, boolean spbrush);
UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush);
INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, fixed_t mashed, boolean spbrush, boolean bot, boolean rival);
INT32 K_GetRollingRouletteItem(player_t *player);
INT32 K_GetShieldFromItem(INT32 item);
fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against);
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2);
@ -75,6 +79,10 @@ void K_RemoveGrowShrink(player_t *player);
void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type);
void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
void K_TumbleInterrupt(player_t *player);
angle_t K_StumbleSlope(angle_t angle, angle_t pitch, angle_t roll);
boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir);
void K_InitStumbleIndicator(player_t *player);
void K_UpdateStumbleIndicator(player_t *player);
INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
void K_DebtStingPlayer(player_t *player, mobj_t *source);
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers);
@ -90,7 +98,7 @@ void K_SpawnWipeoutTrail(mobj_t *mo);
void K_SpawnDraftDust(mobj_t *mo);
void K_DriftDustHandling(mobj_t *spawner);
void K_Squish(mobj_t *mo);
mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow);
mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow, angle_t angleOffset);
void K_PuntMine(mobj_t *mine, mobj_t *punter);
void K_DoSneaker(player_t *player, INT32 type);
void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound);
@ -142,6 +150,7 @@ fixed_t K_GetNewSpeed(player_t *player);
fixed_t K_3dKartMovement(player_t *player);
boolean K_PlayerEBrake(player_t *player);
SINT8 K_Sliptiding(player_t *player);
boolean K_FastFallBounce(player_t *player);
void K_AdjustPlayerFriction(player_t *player);
void K_MoveKartPlayer(player_t *player, boolean onground);
void K_CheckSpectateStatus(void);

View file

@ -1035,6 +1035,12 @@ menuitem_t OPTIONS_Gameplay[] =
{IT_STRING | IT_CVAR, "Karma Comeback", "Enable Karma Comeback in Battle mode.",
NULL, {.cvar = &cv_kartcomeback}, 0, 0},
{IT_SPACE | IT_NOTHING, NULL, NULL,
NULL, {NULL}, 0, 0},
{IT_STRING | IT_CVAR, "Minimum Input Delay", "Practice for online play! Higher = more delay.",
NULL, {.cvar = &cv_mindelay}, 0, 0},
{IT_SPACE | IT_NOTHING, NULL, NULL,
NULL, {NULL}, 0, 0},

View file

@ -15,4 +15,9 @@ void Obj_ShrinkGunRemoved(mobj_t *gun);
boolean Obj_ShrinkLaserCollide(mobj_t *gun, mobj_t *victim);
void Obj_CreateShrinkPohbees(player_t *owner);
/* Item Debris */
void Obj_SpawnItemDebrisEffects(mobj_t *collectible, mobj_t *collector);
void Obj_ItemDebrisThink(mobj_t *debris);
fixed_t Obj_ItemDebrisBounce(mobj_t *debris, fixed_t momz);
#endif/*k_objects_H*/

View file

@ -242,6 +242,30 @@ static int lib_spr2namelen(lua_State *L)
// SPRITE INFO //
/////////////////
struct PivotFrame {
spriteinfo_t *sprinfo;
UINT8 frame;
};
static UINT8 GetPivotFrame(lua_State *L, int idx)
{
const char *field = luaL_checkstring(L, idx);
UINT8 frame;
if (fastcmp("default", field))
{
frame = SPRINFO_DEFAULT_PIVOT;
}
else
{
frame = R_Char2Frame(field[0]);
if (frame == 255)
luaL_error(L, "invalid frame %s", field);
}
return frame;
}
// spriteinfo[]
static int lib_getSpriteInfo(lua_State *L)
{
@ -349,24 +373,23 @@ static int PopPivotTable(spriteinfo_t *info, lua_State *L, int stk)
while (lua_next(L, stk))
{
int idx = 0;
const char *framestr = NULL;
switch (lua_type(L, stk+1))
{
case LUA_TSTRING:
framestr = lua_tostring(L, stk+1);
idx = R_Char2Frame(framestr[0]);
idx = GetPivotFrame(L, stk+1);
break;
case LUA_TNUMBER:
idx = lua_tonumber(L, stk+1);
if ((idx < 0) || (idx >= 64))
return luaL_error(L, "pivot frame %d out of range (0 - %d)", idx, 63);
break;
default:
TYPEERROR("pivot frame", LUA_TNUMBER, lua_type(L, stk+1));
}
if ((idx < 0) || (idx >= 64))
return luaL_error(L, "pivot frame %d out of range (0 - %d)", idx, 63);
// the values in pivot[] are also tables
if (PopPivotSubTable(info->pivot, L, stk+2, idx))
info->available = true;
set_bit_array(info->available, idx);
lua_pop(L, 1);
}
@ -441,7 +464,7 @@ static int spriteinfo_get(lua_State *L)
{
// bypass LUA_PushUserdata
void **userdata = lua_newuserdata(L, sizeof(void *));
*userdata = &sprinfo->pivot;
*userdata = sprinfo;
luaL_getmetatable(L, META_PIVOTLIST);
lua_setmetatable(L, -2);
@ -480,9 +503,8 @@ static int spriteinfo_set(lua_State *L)
// pivot[] is userdata
else if (lua_isuserdata(L, 1))
{
spriteframepivot_t *pivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
memcpy(&sprinfo->pivot, pivot, sizeof(spriteframepivot_t));
sprinfo->available = true; // Just in case?
spriteinfo_t *copyinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
memcpy(sprinfo, copyinfo, sizeof(spriteinfo_t));
}
}
else
@ -505,20 +527,16 @@ static int spriteinfo_num(lua_State *L)
// framepivot_t
static int pivotlist_get(lua_State *L)
{
void **userdata;
spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
const char *field = luaL_checkstring(L, 2);
UINT8 frame;
struct PivotFrame *container;
spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
UINT8 frame = GetPivotFrame(L, 2);
I_Assert(framepivot != NULL);
frame = R_Char2Frame(field[0]);
if (frame == 255)
luaL_error(L, "invalid frame %s", field);
// bypass LUA_PushUserdata
userdata = lua_newuserdata(L, sizeof(void *));
*userdata = &framepivot[frame];
container = lua_newuserdata(L, sizeof *container);
container->sprinfo = sprinfo;
container->frame = frame;
luaL_getmetatable(L, META_FRAMEPIVOT);
lua_setmetatable(L, -2);
@ -528,11 +546,9 @@ static int pivotlist_get(lua_State *L)
static int pivotlist_set(lua_State *L)
{
// Because I already know it's a spriteframepivot_t anyway
spriteframepivot_t *pivotlist = *((spriteframepivot_t **)lua_touserdata(L, 1));
//spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
const char *field = luaL_checkstring(L, 2);
spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST));
UINT8 frame;
int okcool = 0;
if (!lua_lumploading)
return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!");
@ -543,20 +559,22 @@ static int pivotlist_set(lua_State *L)
I_Assert(pivotlist != NULL);
frame = R_Char2Frame(field[0]);
if (frame == 255)
luaL_error(L, "invalid frame %s", field);
frame = GetPivotFrame(L, 2);
// pivot[] is a table
if (lua_istable(L, 3))
return PopPivotSubTable(pivotlist, L, 3, frame);
okcool = PopPivotSubTable(sprinfo->pivot, L, 3, frame);
// pivot[] is userdata
else if (lua_isuserdata(L, 3))
{
spriteframepivot_t *copypivot = *((spriteframepivot_t **)luaL_checkudata(L, 3, META_FRAMEPIVOT));
memcpy(&pivotlist[frame], copypivot, sizeof(spriteframepivot_t));
struct PivotFrame *container = luaL_checkudata(L, 3, META_FRAMEPIVOT);
memcpy(&sprinfo->pivot[frame], &container->sprinfo->pivot[container->frame], sizeof(spriteframepivot_t));
okcool = 1;
}
if (okcool)
set_bit_array(sprinfo->available, frame);
return 0;
}
@ -568,7 +586,8 @@ static int pivotlist_num(lua_State *L)
static int framepivot_get(lua_State *L)
{
spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
struct PivotFrame *container = luaL_checkudata(L, 1, META_FRAMEPIVOT);
spriteframepivot_t *framepivot = &container->sprinfo->pivot[container->frame];
const char *field = luaL_checkstring(L, 2);
I_Assert(framepivot != NULL);
@ -585,7 +604,9 @@ static int framepivot_get(lua_State *L)
static int framepivot_set(lua_State *L)
{
spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT));
struct PivotFrame *container = luaL_checkudata(L, 1, META_FRAMEPIVOT);
spriteframepivot_t *framepivot = &container->sprinfo->pivot[container->frame];
UINT8 *available = container->sprinfo->available;
const char *field = luaL_checkstring(L, 2);
if (!lua_lumploading)
@ -598,9 +619,15 @@ static int framepivot_set(lua_State *L)
I_Assert(framepivot != NULL);
if (fastcmp("x", field))
{
framepivot->x = luaL_checkinteger(L, 3);
set_bit_array(available, container->frame);
}
else if (fastcmp("y", field))
{
framepivot->y = luaL_checkinteger(L, 3);
set_bit_array(available, container->frame);
}
else
return luaL_error(L, va("Field %s does not exist in spriteframepivot_t", field));

View file

@ -336,6 +336,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->flamemeter);
else if (fastcmp(field,"flamelength"))
lua_pushinteger(L, plr->flamelength);
else if (fastcmp(field,"ballhogcharge"))
lua_pushinteger(L, plr->ballhogcharge);
else if (fastcmp(field,"hyudorotimer"))
lua_pushinteger(L, plr->hyudorotimer);
else if (fastcmp(field,"stealingtimer"))
@ -696,6 +698,8 @@ static int player_set(lua_State *L)
plr->flamemeter = luaL_checkinteger(L, 3);
else if (fastcmp(field,"flamelength"))
plr->flamelength = luaL_checkinteger(L, 3);
else if (fastcmp(field,"ballhogcharge"))
plr->ballhogcharge = luaL_checkinteger(L, 3);
else if (fastcmp(field,"hyudorotimer"))
plr->hyudorotimer = luaL_checkinteger(L, 3);
else if (fastcmp(field,"stealingtimer"))

View file

@ -1,2 +1,3 @@
hyudoro.c
shrink.c
item-debris.c

237
src/objects/item-debris.c Normal file
View file

@ -0,0 +1,237 @@
#include "../doomdef.h"
#include "../d_player.h"
#include "../m_random.h"
#include "../k_kart.h"
#include "../k_objects.h"
#include "../p_local.h"
#include "../r_main.h"
#include "../s_sound.h"
// TODO: general function
static fixed_t K_GetPlayerSpeedRatio(player_t *player)
{
return FixedDiv(player->speed,
K_GetKartSpeed(player, false, false));
}
#define debris_type(o) ((o)->extravalue1)
#define debris_bouncesleft(o) ((o)->threshold)
enum {
DEBRIS_ALPHA,
DEBRIS_BETA,
NUM_DEBRIS_TYPES
};
struct debris_config {
mobj_t * origin;
angle_t angle;
fixed_t speed;
fixed_t scale;
UINT8 type;
};
static fixed_t
get_speed_ratio (mobj_t *thing)
{
return thing->player ?
K_GetPlayerSpeedRatio(thing->player) : FRACUNIT;
}
static void
spawn_debris
( const struct debris_config * config,
INT32 angle)
{
const fixed_t height_table[NUM_DEBRIS_TYPES] = {
50*FRACUNIT,
35*FRACUNIT,
};
mobj_t *debris = P_SpawnMobjFromMobj(
config->origin, 0, 0, 0, MT_ITEM_DEBRIS);
debris_type(debris) = config->type;
debris_bouncesleft(debris) = 1;
P_InstaThrust(debris,
config->angle + angle,
config->speed);
P_SetObjectMomZ(debris,
FixedMul(config->scale,
height_table[config->type]),
false);
debris->destscale =
FixedMul(config->scale, 3 * debris->scale);
P_SetScale(debris, debris->destscale);
// Pass down color to dust particles
debris->color = config->origin->color;
}
static void
spawn_cloud
( mobj_t * collectible,
mobj_t * collector,
fixed_t base_speed)
{
const fixed_t min_speed = 90 * collectible->scale;
const fixed_t scale = FixedDiv(
max(base_speed, min_speed), min_speed);
const INT16 spacing =
(collectible->radius / 2) / collectible->scale;
INT32 i;
// Most of this code is from p_inter.c, MT_ITEMCAPSULE
// dust effects
for (i = 0; i < 10; i++)
{
mobj_t *puff = P_SpawnMobjFromMobj(
collectible,
P_RandomRange(-spacing, spacing) * FRACUNIT,
P_RandomRange(-spacing, spacing) * FRACUNIT,
P_RandomRange(0, 4 * spacing) * FRACUNIT,
MT_SPINDASHDUST
);
puff->color = collector->color;
puff->colorized = true;
puff->destscale = FixedMul(puff->destscale, scale);
P_SetScale(puff, puff->destscale);
puff->momz = puff->scale * P_MobjFlip(puff);
P_InitAngle(puff, R_PointToAngle2(
collectible->x,
collectible->y,
puff->x,
puff->y));
P_Thrust(puff, puff->angle, 3 * puff->scale);
puff->momx += collector->momx;
puff->momy += collector->momy;
puff->momz += collector->momz;
}
}
static void
rotate3d (mobj_t *debris)
{
const UINT8 steps = 30;
debris->rollangle =
M_RandomKey(steps) * (ANGLE_MAX / steps);
}
void
Obj_SpawnItemDebrisEffects
( mobj_t * collectible,
mobj_t * collector)
{
const fixed_t min_speed = 80 * collectible->scale;
fixed_t base_speed = FixedMul(75 * mapobjectscale,
get_speed_ratio(collector));
struct debris_config config;
// Delayed effect for puffs of smoke that stick to and
// glide off of the player
mobj_t *spawner = P_SpawnMobjFromMobj(collectible,
0, 0, 0, MT_ITEM_DEBRIS_CLOUD_SPAWNER);
P_SetTarget(&spawner->target, collector);
config.origin = collectible;
config.angle = K_MomentumAngle(collector);
config.speed = max(base_speed, min_speed);
config.scale = FixedDiv(config.speed, min_speed);
config.type = DEBRIS_ALPHA;
spawn_debris(&config, ANGLE_11hh);
spawn_debris(&config, -(ANGLE_11hh));
config.type = DEBRIS_BETA;
spawn_debris(&config, 3*ANGLE_22h/2);
spawn_debris(&config, 3*ANGLE_22h/4);
spawn_debris(&config, 0);
spawn_debris(&config, -(3*ANGLE_22h/4));
spawn_debris(&config, -(3*ANGLE_22h/2));
spawn_cloud(collectible, collector, base_speed);
S_StartSound(spawner, sfx_kc2e);
S_StartSound(spawner, sfx_s1c9);
}
void
Obj_ItemDebrisThink (mobj_t *debris)
{
const UINT8 frame = (debris->frame & FF_FRAMEMASK);
if (debris->momz == 0)
{
P_KillMobj(debris, NULL, NULL, DMG_NORMAL);
return;
}
rotate3d(debris);
if (frame % 3 == 1)
{
mobj_t *ghost = P_SpawnGhostMobj(debris);
ghost->fuse = 3;
}
if (debris_type(debris) == DEBRIS_ALPHA)
{
mobj_t *dust = P_SpawnMobjFromMobj(
debris, 0, 0, 0, MT_SPINDASHDUST);
P_SetScale(dust, (dust->destscale /= 3));
dust->color = debris->color;
dust->colorized = true;
dust->momx = debris->momx / 4;
dust->momy = debris->momy / 4;
dust->momz = debris->momz / 4;
}
}
fixed_t
Obj_ItemDebrisBounce
( mobj_t * debris,
fixed_t momz)
{
if (debris_bouncesleft(debris) <= 0)
{
P_KillMobj(debris, NULL, NULL, DMG_NORMAL);
return 0;
}
momz = -(momz);
if (debris_type(debris) == DEBRIS_BETA)
{
momz /= 2;
}
debris_bouncesleft(debris)--;
S_StartSound(debris, sfx_cdfm47);
return momz;
}

View file

@ -32,6 +32,7 @@
#include "k_battle.h"
#include "k_respawn.h"
#include "k_collide.h"
#include "k_objects.h"
#ifdef HW3SOUND
#include "hardware/hw3sound.h"
@ -326,6 +327,7 @@ void A_ReaperThinker(mobj_t *actor);
void A_MementosTPParticles(mobj_t *actor);
void A_FlameShieldPaper(mobj_t *actor);
void A_InvincSparkleRotate(mobj_t *actor);
void A_SpawnItemDebrisCloud(mobj_t *actor);
//for p_enemy.c
@ -13166,9 +13168,6 @@ void A_ItemPop(mobj_t *actor)
{
INT32 locvar1 = var1;
mobj_t *remains;
mobjtype_t explode;
if (LUA_CallAction(A_ITEMPOP, actor))
return;
@ -13185,63 +13184,28 @@ void A_ItemPop(mobj_t *actor)
actor->flags |= MF_NOCLIP;
P_SetThingPosition(actor);
// item explosion
explode = mobjinfo[actor->info->damage].mass;
remains = P_SpawnMobj(actor->x, actor->y,
((actor->eflags & MFE_VERTICALFLIP) ? (actor->z + 3*(actor->height/4) - FixedMul(mobjinfo[explode].height, actor->scale)) : (actor->z + actor->height/4)), explode);
if (actor->eflags & MFE_VERTICALFLIP)
{
remains->eflags |= MFE_VERTICALFLIP;
remains->flags2 |= MF2_OBJECTFLIP;
}
remains->destscale = actor->destscale;
P_SetScale(remains, actor->scale);
// RF_DONTDRAW will flicker as the object's fuse gets
// closer to running out (see P_FuseThink)
actor->renderflags |= RF_DONTDRAW|RF_TRANS50;
actor->color = SKINCOLOR_GREY;
actor->colorized = true;
remains = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->damage);
remains->type = actor->type; // Transfer type information
P_UnsetThingPosition(remains);
if (sector_list)
{
P_DelSeclist(sector_list);
sector_list = NULL;
}
P_SetThingPosition(remains);
remains->destscale = actor->destscale;
P_SetScale(remains, actor->scale);
remains->flags = actor->flags; // Transfer flags
remains->flags2 = actor->flags2; // Transfer flags2
remains->fuse = actor->fuse; // Transfer respawn timer
remains->cvmem = leveltime;
remains->threshold = actor->threshold;
if (remains->threshold != 69 && remains->threshold != 70)
{
remains->threshold = 68;
}
// To insure this information doesn't have to be rediscovered every time you look at this function...
// A threshold of 0 is for a "living", ordinary random item.
// 68 means regular popped random item debris.
// 69 used to mean old Karma Item behaviour (now you can replicate this with MF2_DONTRESPAWN).
// 70 is a powered up Overtime item.
remains->skin = NULL;
remains->spawnpoint = actor->spawnpoint;
P_SetTarget(&tmthing, remains);
if (actor->info->deathsound)
S_StartSound(remains, actor->info->deathsound);
Obj_SpawnItemDebrisEffects(actor, actor->target);
if (locvar1 == 1)
P_GivePlayerSpheres(actor->target->player, actor->extravalue1);
else if (locvar1 == 0)
actor->target->player->itemroulette = 1;
remains->flags2 &= ~MF2_AMBUSH;
// Here at mapload in battle?
if ((gametyperules & GTR_BUMPERS) && (actor->flags2 & MF2_BOSSNOTRAP))
{
numgotboxes++;
P_RemoveMobj(actor);
// do not flicker back in just yet, handled by
// P_RespawnBattleBoxes eventually
P_SetMobjState(actor, S_INVISIBLE);
}
}
void A_JawzChase(mobj_t *actor)
@ -14525,3 +14489,81 @@ void A_InvincSparkleRotate(mobj_t *actor)
ghost->fuse = 4;
}
}
// Function: A_SpawnItemDebrisCloud
//
// Description: Spawns the poofs of an exploded item box. Target is a player to spawn the particles around.
//
// var1 = Copy extravalue2 / var1 fraction of target's momentum.
// var2 = unused
//
void
A_SpawnItemDebrisCloud (mobj_t *actor)
{
INT32 locvar1 = var1;
mobj_t *target = actor->target;
player_t *player;
fixed_t kartspeed;
fixed_t fade;
if (target == NULL || target->player == NULL)
{
return;
}
player = target->player;
kartspeed = K_GetKartSpeed(player, false, false);
// Scale around >50% top speed
fade = FixedMul(locvar1, (FixedDiv(player->speed,
kartspeed) - FRACUNIT/2) * 2);
if (fade < 1)
{
fade = 1;
}
if (actor->extravalue2 > fade)
{
actor->extravalue2 = fade;
}
// MT_ITEM_DEBRIS_CLOUD_SPAWNER
// extravalue2 from A_Repeat
fade = actor->extravalue2 * FRACUNIT / locvar1;
// Most of this code is from p_inter.c, MT_ITEMCAPSULE
// dust effects
{
const INT16 spacing =
(target->radius / 2) / target->scale;
mobj_t *puff = P_SpawnMobjFromMobj(
target,
P_RandomRange(-spacing, spacing) * FRACUNIT,
P_RandomRange(-spacing, spacing) * FRACUNIT,
P_RandomRange(0, 4 * spacing) * FRACUNIT,
MT_SPINDASHDUST
);
puff->color = target->color;
puff->colorized = true;
puff->momz = puff->scale * P_MobjFlip(puff);
P_InitAngle(puff, R_PointToAngle2(
target->x,
target->y,
puff->x,
puff->y));
P_Thrust(puff, puff->angle, 3 * puff->scale);
puff->momx += FixedMul(target->momx, fade);
puff->momy += FixedMul(target->momy, fade);
puff->momz += FixedMul(target->momz, fade);
}
}

View file

@ -275,7 +275,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
special->momx = special->momy = special->momz = 0;
P_SetTarget(&special->target, toucher);
P_KillMobj(special, toucher, toucher, DMG_NORMAL);
break;
return;
case MT_SPHEREBOX:
if (!P_CanPickupItem(player, 0))
return;
@ -283,7 +283,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
special->momx = special->momy = special->momz = 0;
P_SetTarget(&special->target, toucher);
P_KillMobj(special, toucher, toucher, DMG_NORMAL);
break;
return;
case MT_ITEMCAPSULE:
if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)
return;
@ -1037,27 +1037,34 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
{
if (target->flags & MF_MONITOR || target->type == MT_RANDOMITEM)
{
UINT8 i;
P_SetTarget(&target->target, source);
for (i = 0; i < MAXPLAYERS; i++)
if (gametyperules & GTR_BUMPERS)
{
if (&players[i] == source->player)
{
continue;
}
if (playeringame[i] && !players[i].spectator && players[i].lives != 0)
{
break;
}
target->fuse = 2;
}
if (i < MAXPLAYERS)
else
{
// Respawn items in multiplayer, don't respawn them when alone
target->fuse = 2*TICRATE + 2;
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (&players[i] == source->player)
{
continue;
}
if (playeringame[i] && !players[i].spectator && players[i].lives != 0)
{
break;
}
}
if (i < MAXPLAYERS)
{
// Respawn items in multiplayer, don't respawn them when alone
target->fuse = 2*TICRATE + 2;
}
}
}
}
@ -1416,7 +1423,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
// special behavior for SPB capsules
if (target->threshold == KITEM_SPB)
{
K_ThrowKartItem(player, true, MT_SPB, 1, 0);
K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0);
break;
}

View file

@ -172,7 +172,7 @@ boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec);
boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec); // SRB2Kart
#define P_IsObjectFlipped(o) ((o)->eflags & MFE_VERTICALFLIP)
boolean P_InQuicksand(mobj_t *mo);
boolean P_PlayerHitFloor(player_t *player, boolean fromAir);
boolean P_PlayerHitFloor(player_t *player, boolean fromAir, angle_t oldPitch, angle_t oldRoll);
void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
void P_RestoreMusic(player_t *player);

View file

@ -2801,12 +2801,15 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
if (thing->momz <= 0)
{
angle_t oldPitch = thing->pitch;
angle_t oldRoll = thing->roll;
thing->standingslope = tmfloorslope;
P_SetPitchRollFromSlope(thing, thing->standingslope);
if (thing->momz == 0 && thing->player && !startingonground)
if (thing->player)
{
P_PlayerHitFloor(thing->player, true);
P_PlayerHitFloor(thing->player, !startingonground, oldPitch, oldRoll);
}
}
}
@ -2821,12 +2824,15 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
if (thing->momz >= 0)
{
angle_t oldPitch = thing->pitch;
angle_t oldRoll = thing->roll;
thing->standingslope = tmceilingslope;
P_SetPitchRollFromSlope(thing, thing->standingslope);
if (thing->momz == 0 && thing->player && !startingonground)
if (thing->player)
{
P_PlayerHitFloor(thing->player, true);
P_PlayerHitFloor(thing->player, !startingonground, oldPitch, oldRoll);
}
}
}
@ -3117,7 +3123,7 @@ static boolean P_ThingHeightClip(mobj_t *thing)
}
if ((P_MobjFlip(thing)*(thing->z - oldz) > 0 || hitfloor) && thing->player)
P_PlayerHitFloor(thing->player, !onfloor);
P_PlayerHitFloor(thing->player, !onfloor, thing->pitch, thing->roll);
// debug: be sure it falls to the floor
thing->eflags &= ~MFE_ONGROUND;

View file

@ -1209,6 +1209,9 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
case MT_KARMAFIREWORK:
gravityadd /= 3;
break;
case MT_ITEM_DEBRIS:
gravityadd *= 6;
break;
default:
break;
}
@ -1531,12 +1534,18 @@ void P_XYMovement(mobj_t *mo)
}
// adjust various things based on slope
if (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8) {
if (!P_IsObjectOnGround(mo)) { // We fell off at some point? Do the twisty thing!
if (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8)
{
if (!P_IsObjectOnGround(mo))
{
// We fell off at some point? Do the twisty thing!
P_SlopeLaunch(mo);
xmove = mo->momx;
ymove = mo->momy;
} else { // Still on the ground.
}
else
{
// Still on the ground.
slopemom.x = xmove;
slopemom.y = ymove;
slopemom.z = 0;
@ -1563,7 +1572,10 @@ void P_XYMovement(mobj_t *mo)
{
mo->health--;
if (mo->health == 0)
{
mo->scalespeed = mo->scale/12;
mo->destscale = 0;
}
}
}
//}
@ -1765,7 +1777,9 @@ void P_XYMovement(mobj_t *mo)
if (P_MobjWasRemoved(mo)) // MF_SPECIAL touched a player! O_o;;
return;
if (moved && oldslope && !(mo->flags & MF_NOCLIPHEIGHT)) { // Check to see if we ran off
if (moved && oldslope && !(mo->flags & MF_NOCLIPHEIGHT))
{
// Check to see if we ran off
if (oldslope != mo->standingslope)
{
@ -2339,6 +2353,15 @@ boolean P_ZMovement(mobj_t *mo)
mom.z = P_MobjFlip(mo)*FixedMul(5*FRACUNIT, mo->scale);
else if (mo->type == MT_SPINFIRE) // elemental shield fire is another exception here
;
else if (mo->type == MT_ITEM_DEBRIS)
{
mom.z = Obj_ItemDebrisBounce(mo, mom.z);
if (mom.z == 0)
{
return false;
}
}
else if (mo->type == MT_DRIFTCLIP)
{
mom.z = -mom.z/2;
@ -2712,6 +2735,7 @@ static boolean P_PlayerPolyObjectZMovement(mobj_t *mo)
void P_PlayerZMovement(mobj_t *mo)
{
boolean onground;
angle_t oldPitch, oldRoll;
I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo));
@ -2719,6 +2743,9 @@ void P_PlayerZMovement(mobj_t *mo)
if (!mo->player)
return;
oldPitch = mo->pitch;
oldRoll = mo->roll;
// Intercept the stupid 'fall through 3dfloors' bug
if (mo->subsector->sector->ffloors)
P_AdjustMobjFloorZ_FFloors(mo, mo->subsector->sector, 0);
@ -2795,7 +2822,7 @@ void P_PlayerZMovement(mobj_t *mo)
mo->pmomz = 0; // We're on a new floor, don't keep doing platform movement.
mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack
clipmomz = P_PlayerHitFloor(mo->player, true);
clipmomz = P_PlayerHitFloor(mo->player, true, oldPitch, oldRoll);
P_PlayerPolyObjectZMovement(mo);
if (clipmomz)
@ -2831,6 +2858,47 @@ void P_PlayerZMovement(mobj_t *mo)
mo->eflags &= ~MFE_JUSTHITFLOOR;
P_CheckGravity(mo, true);
}
// Even out pitch & roll slowly over time when respawning.
if (mo->player->respawn.state != RESPAWNST_NONE)
{
const angle_t speed = ANG2; //FixedMul(ANG2, abs(mo->momz) / 8);
angle_t dest = 0;
INT32 pitchDelta = AngleDeltaSigned(mo->pitch, 0);
INT32 rollDelta = AngleDeltaSigned(mo->roll, 0);
if (abs(pitchDelta) <= speed && dest == 0)
{
mo->pitch = 0;
}
else if (abs(pitchDelta) > dest)
{
if (pitchDelta > 0)
{
mo->pitch -= speed;
}
else
{
mo->pitch += speed;
}
}
if (abs(rollDelta) <= speed && dest == 0)
{
mo->roll = 0;
}
else if (abs(rollDelta) > dest)
{
if (rollDelta > 0)
{
mo->roll -= speed;
}
else
{
mo->roll += speed;
}
}
}
}
if (((mo->eflags & MFE_VERTICALFLIP && mo->z < mo->floorz) || (!(mo->eflags & MFE_VERTICALFLIP) && mo->z + mo->height > mo->ceilingz))
@ -5929,79 +5997,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
{
P_SetMobjState(mobj, S_PLAYERARROW_BOX);
mobj->tracer->sprite = SPR_ITEM;
switch((mobj->target->player->itemroulette % (16*3)) / 3)
{
// Each case is handled in threes, to give three frames of in-game time to see the item on the roulette
case 0: // Sneaker
mobj->tracer->frame = KITEM_SNEAKER;
//localcolor = SKINCOLOR_RASPBERRY;
break;
case 1: // Banana
mobj->tracer->frame = KITEM_BANANA;
//localcolor = SKINCOLOR_YELLOW;
break;
case 2: // Orbinaut
mobj->tracer->frame = KITEM_ORBINAUT;
//localcolor = SKINCOLOR_STEEL;
break;
case 3: // Mine
mobj->tracer->frame = KITEM_MINE;
//localcolor = SKINCOLOR_JET;
break;
case 4: // Grow
mobj->tracer->frame = KITEM_GROW;
//localcolor = SKINCOLOR_TEAL;
break;
case 5: // Hyudoro
mobj->tracer->frame = KITEM_HYUDORO;
//localcolor = SKINCOLOR_STEEL;
break;
case 6: // Rocket Sneaker
mobj->tracer->frame = KITEM_ROCKETSNEAKER;
//localcolor = SKINCOLOR_TANGERINE;
break;
case 7: // Jawz
mobj->tracer->frame = KITEM_JAWZ;
//localcolor = SKINCOLOR_JAWZ;
break;
case 8: // Self-Propelled Bomb
mobj->tracer->frame = KITEM_SPB;
//localcolor = SKINCOLOR_JET;
break;
case 9: // Shrink
mobj->tracer->frame = KITEM_SHRINK;
//localcolor = SKINCOLOR_ORANGE;
break;
case 10: // Invincibility
mobj->tracer->frame = KITEM_INVINCIBILITY;
//localcolor = SKINCOLOR_GREY;
break;
case 11: // Eggman Monitor
mobj->tracer->frame = KITEM_EGGMAN;
//localcolor = SKINCOLOR_ROSE;
break;
case 12: // Ballhog
mobj->tracer->frame = KITEM_BALLHOG;
//localcolor = SKINCOLOR_LILAC;
break;
case 13: // Lightning Shield
mobj->tracer->frame = KITEM_LIGHTNINGSHIELD;
//localcolor = SKINCOLOR_CYAN;
break;
case 14: // Super Ring
mobj->tracer->frame = KITEM_SUPERRING;
//localcolor = SKINCOLOR_GOLD;
break;
case 15: // Land Mine
mobj->tracer->frame = KITEM_LANDMINE;
//localcolor = SKINCOLOR_JET;
break;
case 16: // Drop Target
mobj->tracer->frame = KITEM_DROPTARGET;
//localcolor = SKINCOLOR_LIME;
break;
}
mobj->tracer->frame |= FF_FULLBRIGHT;
mobj->tracer->frame = K_GetRollingRouletteItem(mobj->target->player) | FF_FULLBRIGHT;
mobj->tracer->renderflags &= ~RF_DONTDRAW;
}
else if (mobj->target->player->stealingtimer < 0)
@ -7845,6 +7841,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
Obj_PohbeeThinker(mobj);
break;
}
case MT_ITEM_DEBRIS:
{
Obj_ItemDebrisThink(mobj);
break;
}
case MT_ROCKETSNEAKER:
if (!mobj->target || !mobj->target->health)
{
@ -9212,7 +9213,7 @@ static boolean P_FuseThink(mobj_t *mobj)
{
;
}
else if ((gametyperules & GTR_BUMPERS) && (mobj->threshold != 70))
else if ((gametyperules & GTR_BUMPERS) && (mobj->state == &states[S_INVISIBLE]))
{
break;
}
@ -11055,7 +11056,6 @@ void P_RespawnBattleBoxes(void)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
mobj_t *box;
mobj_t *newmobj;
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
@ -11064,25 +11064,12 @@ void P_RespawnBattleBoxes(void)
if (box->type != MT_RANDOMITEM
|| (box->flags2 & MF2_DONTRESPAWN)
|| box->threshold != 68
|| box->fuse
|| ((tic_t)box->cvmem+1 >= leveltime))
|| box->health > 0
|| box->fuse)
continue; // only popped items
// Respawn from mapthing if you have one!
if (box->spawnpoint)
{
P_SpawnMapThing(box->spawnpoint);
newmobj = box->spawnpoint->mobj; // this is set to the new mobj in P_SpawnMapThing
}
else
{
newmobj = P_SpawnMobj(box->x, box->y, box->z, box->type);
}
// Transfer flags2 (strongbox, objectflip, bossnotrap)
newmobj->flags2 = box->flags2;
P_RemoveMobj(box); // make sure they disappear
box->fuse = TICRATE; // flicker back in (A_ItemPop preps this effect)
P_SetMobjState(box, box->info->raisestate);
if (numgotboxes > 0)
numgotboxes--; // you've restored a box, remove it from the count
@ -11327,6 +11314,8 @@ void P_SpawnPlayer(INT32 playernum)
P_SetScale(mobj, mobj->destscale);
P_FlashPal(p, 0, 0); // Resets
K_InitStumbleIndicator(p);
if (gametyperules & GTR_BUMPERS)
{
mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW);
@ -12666,10 +12655,6 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
P_SetThingPosition(mobj);
}
}
else
{
P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_EXPLODE);
}
break;
}
case MT_ITEMCAPSULE:

View file

@ -64,6 +64,7 @@ typedef enum
SKYBOXVIEW = 0x08,
SKYBOXCENTER = 0x10,
HOVERHYUDORO = 0x20,
STUMBLE = 0x40,
} player_saveflags;
static inline void P_ArchivePlayer(void)
@ -202,6 +203,9 @@ static void P_NetArchivePlayers(void)
if (players[i].hoverhyudoro)
flags |= HOVERHYUDORO;
if (players[i].stumbleIndicator)
flags |= STUMBLE;
WRITEUINT16(save_p, flags);
if (flags & SKYBOXVIEW)
@ -219,6 +223,9 @@ static void P_NetArchivePlayers(void)
if (flags & HOVERHYUDORO)
WRITEUINT32(save_p, players[i].hoverhyudoro->mobjnum);
if (flags & STUMBLE)
WRITEUINT32(save_p, players[i].stumbleIndicator->mobjnum);
WRITEUINT32(save_p, (UINT32)players[i].followitem);
WRITEUINT32(save_p, players[i].charflags);
@ -320,6 +327,8 @@ static void P_NetArchivePlayers(void)
WRITEUINT16(save_p, players[i].flamemeter);
WRITEUINT8(save_p, players[i].flamelength);
WRITEUINT16(save_p, players[i].ballhogcharge);
WRITEUINT16(save_p, players[i].hyudorotimer);
WRITESINT8(save_p, players[i].stealingtimer);
@ -507,6 +516,9 @@ static void P_NetUnArchivePlayers(void)
if (flags & HOVERHYUDORO)
players[i].hoverhyudoro = (mobj_t *)(size_t)READUINT32(save_p);
if (flags & STUMBLE)
players[i].stumbleIndicator = (mobj_t *)(size_t)READUINT32(save_p);
players[i].followitem = (mobjtype_t)READUINT32(save_p);
//SetPlayerSkinByNum(i, players[i].skin);
@ -609,6 +621,8 @@ static void P_NetUnArchivePlayers(void)
players[i].flamemeter = READUINT16(save_p);
players[i].flamelength = READUINT8(save_p);
players[i].ballhogcharge = READUINT16(save_p);
players[i].hyudorotimer = READUINT16(save_p);
players[i].stealingtimer = READSINT8(save_p);
@ -4285,6 +4299,13 @@ static void P_RelinkPointers(void)
if (!P_SetTarget(&mobj->player->hoverhyudoro, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "hoverhyudoro not found on %d\n", mobj->type);
}
if (mobj->player->stumbleIndicator)
{
temp = (UINT32)(size_t)mobj->player->stumbleIndicator;
mobj->player->stumbleIndicator = NULL;
if (!P_SetTarget(&mobj->player->stumbleIndicator, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "stumbleIndicator not found on %d\n", mobj->type);
}
}
}
}

View file

@ -1339,7 +1339,7 @@ void P_DoPlayerExit(player_t *player)
//
// Handles player hitting floor surface.
// Returns whether to clip momz.
boolean P_PlayerHitFloor(player_t *player, boolean fromAir)
boolean P_PlayerHitFloor(player_t *player, boolean fromAir, angle_t oldPitch, angle_t oldRoll)
{
boolean clipmomz;
@ -1347,9 +1347,32 @@ boolean P_PlayerHitFloor(player_t *player, boolean fromAir)
clipmomz = !(P_CheckDeathPitCollide(player->mo));
if (fromAir == true && clipmomz == true)
if (clipmomz == true)
{
K_SpawnSplashForMobj(player->mo, abs(player->mo->momz));
if (fromAir == true)
{
K_SpawnSplashForMobj(player->mo, abs(player->mo->momz));
}
if (player->mo->health > 0)
{
boolean air = fromAir;
if (P_IsObjectOnGround(player->mo) && (player->mo->eflags & MFE_JUSTHITFLOOR))
{
air = true;
}
if (K_CheckStumble(player, oldPitch, oldRoll, air) == true)
{
return false;
}
if (air == false && K_FastFallBounce(player) == true)
{
return false;
}
}
}
return clipmomz;
@ -1662,7 +1685,7 @@ static void P_CheckQuicksand(player_t *player)
player->mo->z = ceilingheight - player->mo->height;
if (player->mo->momz <= 0)
P_PlayerHitFloor(player, false);
P_PlayerHitFloor(player, false, player->mo->roll, player->mo->pitch);
}
else
{
@ -1674,7 +1697,7 @@ static void P_CheckQuicksand(player_t *player)
player->mo->z = floorheight;
if (player->mo->momz >= 0)
P_PlayerHitFloor(player, false);
P_PlayerHitFloor(player, false, player->mo->roll, player->mo->pitch);
}
friction = abs(rover->master->v1->y - rover->master->v2->y)>>6;

View file

@ -141,11 +141,16 @@ patch_t *Patch_GetRotatedSprite(
patch = W_CachePatchNum(lump, PU_SPRITE);
if (sprinfo->available)
if (in_bit_array(sprinfo->available, frame))
{
xpivot = sprinfo->pivot[frame].x;
ypivot = sprinfo->pivot[frame].y;
}
else if (in_bit_array(sprinfo->available, SPRINFO_DEFAULT_PIVOT))
{
xpivot = sprinfo->pivot[SPRINFO_DEFAULT_PIVOT].x;
ypivot = sprinfo->pivot[SPRINFO_DEFAULT_PIVOT].y;
}
else
{
xpivot = patch->leftoffset;

View file

@ -1396,12 +1396,79 @@ boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *to
#endif
#endif
//
// R_ParseSpriteInfoFrame
//
// Parse a SPRTINFO frame.
//
static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
struct ParseSpriteInfoState {
boolean spr2;
spriteinfo_t *info;
spritenum_t sprnum;
playersprite_t spr2num;
boolean any;
INT32 skinnumbers[MAXSKINS];
INT32 foundskins;
};
#define PARSER_FRAME (false)
#define PARSER_DEFAULT (true)
static void R_ParseSpriteInfoSkin(struct ParseSpriteInfoState *parser)
{
char *sprinfoToken;
size_t sprinfoTokenLength;
INT32 skinnum;
char *skinName = NULL;
// Skin name
sprinfoToken = M_GetToken(NULL);
if (sprinfoToken == NULL)
{
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where skin frame should be");
}
if (strcmp(sprinfoToken, "*")==0) // All skins
{
parser->foundskins = -1;
}
else
{
// copy skin name yada yada
sprinfoTokenLength = strlen(sprinfoToken);
skinName = (char *)Z_Malloc((sprinfoTokenLength+1)*sizeof(char),PU_STATIC,NULL);
M_Memcpy(skinName,sprinfoToken,sprinfoTokenLength*sizeof(char));
skinName[sprinfoTokenLength] = '\0';
strlwr(skinName);
skinnum = R_SkinAvailable(skinName);
if (skinnum == -1)
I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName);
parser->skinnumbers[parser->foundskins] = skinnum;
parser->foundskins++;
}
Z_Free(sprinfoToken);
}
static void copy_to_skin (struct ParseSpriteInfoState *parser, INT32 skinnum)
{
skin_t *skin = &skins[skinnum];
spriteinfo_t *sprinfo = skin->sprinfo;
if (parser->any)
{
playersprite_t spr2num;
for (spr2num = 0; spr2num < NUMPLAYERSPRITES; ++spr2num)
{
M_Memcpy(&sprinfo[spr2num], parser->info, sizeof(spriteinfo_t));
}
}
else
{
M_Memcpy(&sprinfo[parser->spr2num], parser->info, sizeof(spriteinfo_t));
}
}
static void R_ParseSpriteInfoFrame(struct ParseSpriteInfoState *parser, boolean all)
{
char *sprinfoToken;
size_t sprinfoTokenLength;
@ -1411,22 +1478,29 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
INT16 frameYPivot = 0;
rotaxis_t frameRotAxis = 0;
// Sprite identifier
sprinfoToken = M_GetToken(NULL);
if (sprinfoToken == NULL)
if (all)
{
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite frame should be");
}
sprinfoTokenLength = strlen(sprinfoToken);
if (sprinfoTokenLength != 1)
{
I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",sprinfoToken);
frameFrame = SPRINFO_DEFAULT_PIVOT;
}
else
frameChar = sprinfoToken;
{
// Sprite identifier
sprinfoToken = M_GetToken(NULL);
if (sprinfoToken == NULL)
{
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite frame should be");
}
sprinfoTokenLength = strlen(sprinfoToken);
if (sprinfoTokenLength != 1)
{
I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",sprinfoToken);
}
else
frameChar = sprinfoToken;
frameFrame = R_Char2Frame(frameChar[0]);
Z_Free(sprinfoToken);
frameFrame = R_Char2Frame(frameChar[0]);
Z_Free(sprinfoToken);
}
// Left Curly Brace
sprinfoToken = M_GetToken(NULL);
@ -1480,9 +1554,50 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
}
// set fields
info->pivot[frameFrame].x = frameXPivot;
info->pivot[frameFrame].y = frameYPivot;
info->pivot[frameFrame].rotaxis = frameRotAxis;
parser->info->pivot[frameFrame].x = frameXPivot;
parser->info->pivot[frameFrame].y = frameYPivot;
parser->info->pivot[frameFrame].rotaxis = frameRotAxis;
set_bit_array(parser->info->available, frameFrame);
if (parser->spr2)
{
INT32 i;
if (!parser->foundskins)
I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition");
if (parser->foundskins < 0)
{
for (i = 0; i < numskins; i++)
{
copy_to_skin(parser, i);
}
}
else
{
for (i = 0; i < parser->foundskins; i++)
{
copy_to_skin(parser, parser->skinnumbers[i]);
}
}
}
else
{
if (parser->any)
{
spritenum_t sprnum;
for (sprnum = 0; sprnum < NUMSPRITES; ++sprnum)
{
M_Memcpy(&spriteinfo[sprnum], parser->info, sizeof(spriteinfo_t));
}
}
else
{
M_Memcpy(&spriteinfo[parser->sprnum], parser->info, sizeof(spriteinfo_t));
}
}
}
//
@ -1492,15 +1607,19 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
//
static void R_ParseSpriteInfo(boolean spr2)
{
spriteinfo_t *info;
char *sprinfoToken;
size_t sprinfoTokenLength;
char newSpriteName[5]; // no longer dynamically allocated
spritenum_t sprnum = NUMSPRITES;
playersprite_t spr2num = NUMPLAYERSPRITES;
struct ParseSpriteInfoState parser = {
.spr2 = spr2,
.sprnum = NUMSPRITES,
.spr2num = NUMPLAYERSPRITES,
.any = false,
.foundskins = 0,
};
INT32 i;
INT32 skinnumbers[MAXSKINS];
INT32 foundskins = 0;
// Sprite name
sprinfoToken = M_GetToken(NULL);
@ -1508,21 +1627,32 @@ static void R_ParseSpriteInfo(boolean spr2)
{
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite name should be");
}
sprinfoTokenLength = strlen(sprinfoToken);
if (sprinfoTokenLength != 4)
if (!strcmp(sprinfoToken, "*")) // All sprites
{
I_Error("Error parsing SPRTINFO lump: Sprite name \"%s\" isn't 4 characters long",sprinfoToken);
parser.any = true;
}
else
{
memset(&newSpriteName, 0, 5);
M_Memcpy(newSpriteName, sprinfoToken, sprinfoTokenLength);
// ^^ we've confirmed that the token is == 4 characters so it will never overflow a 5 byte char buffer
strupr(newSpriteName); // Just do this now so we don't have to worry about it
sprinfoTokenLength = strlen(sprinfoToken);
if (sprinfoTokenLength != 4)
{
I_Error("Error parsing SPRTINFO lump: Sprite name \"%s\" isn't 4 characters long",sprinfoToken);
}
else
{
memset(&newSpriteName, 0, 5);
M_Memcpy(newSpriteName, sprinfoToken, sprinfoTokenLength);
// ^^ we've confirmed that the token is == 4 characters so it will never overflow a 5 byte char buffer
strupr(newSpriteName); // Just do this now so we don't have to worry about it
}
}
Z_Free(sprinfoToken);
if (!spr2)
if (parser.any)
;
else if (!spr2)
{
for (i = 0; i <= NUMSPRITES; i++)
{
@ -1530,7 +1660,7 @@ static void R_ParseSpriteInfo(boolean spr2)
I_Error("Error parsing SPRTINFO lump: Unknown sprite name \"%s\"", newSpriteName);
if (!memcmp(newSpriteName,sprnames[i],4))
{
sprnum = i;
parser.sprnum = i;
break;
}
}
@ -1543,15 +1673,14 @@ static void R_ParseSpriteInfo(boolean spr2)
I_Error("Error parsing SPRTINFO lump: Unknown sprite2 name \"%s\"", newSpriteName);
if (!memcmp(newSpriteName,spr2names[i],4))
{
spr2num = i;
parser.spr2num = i;
break;
}
}
}
// allocate a spriteinfo
info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL);
info->available = true;
parser.info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL);
// Left Curly Brace
sprinfoToken = M_GetToken(NULL);
@ -1571,53 +1700,21 @@ static void R_ParseSpriteInfo(boolean spr2)
{
if (stricmp(sprinfoToken, "SKIN")==0)
{
INT32 skinnum;
char *skinName = NULL;
if (!spr2)
I_Error("Error parsing SPRTINFO lump: \"SKIN\" token found outside of a sprite2 definition");
Z_Free(sprinfoToken);
// Skin name
sprinfoToken = M_GetToken(NULL);
if (sprinfoToken == NULL)
{
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where skin frame should be");
}
// copy skin name yada yada
sprinfoTokenLength = strlen(sprinfoToken);
skinName = (char *)Z_Malloc((sprinfoTokenLength+1)*sizeof(char),PU_STATIC,NULL);
M_Memcpy(skinName,sprinfoToken,sprinfoTokenLength*sizeof(char));
skinName[sprinfoTokenLength] = '\0';
strlwr(skinName);
Z_Free(sprinfoToken);
skinnum = R_SkinAvailable(skinName);
if (skinnum == -1)
I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName);
skinnumbers[foundskins] = skinnum;
foundskins++;
R_ParseSpriteInfoSkin(&parser);
}
else if (stricmp(sprinfoToken, "FRAME")==0)
{
R_ParseSpriteInfoFrame(info);
Z_Free(sprinfoToken);
if (spr2)
{
if (!foundskins)
I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition");
for (i = 0; i < foundskins; i++)
{
size_t skinnum = skinnumbers[i];
skin_t *skin = &skins[skinnum];
spriteinfo_t *sprinfo = skin->sprinfo;
M_Memcpy(&sprinfo[spr2num], info, sizeof(spriteinfo_t));
}
}
else
M_Memcpy(&spriteinfo[sprnum], info, sizeof(spriteinfo_t));
R_ParseSpriteInfoFrame(&parser, PARSER_FRAME);
}
else if (stricmp(sprinfoToken, "DEFAULT")==0)
{
Z_Free(sprinfoToken);
R_ParseSpriteInfoFrame(&parser, PARSER_DEFAULT);
}
else
{
@ -1636,7 +1733,7 @@ static void R_ParseSpriteInfo(boolean spr2)
I_Error("Error parsing SPRTINFO lump: Expected \"{\" for sprite \"%s\", got \"%s\"",newSpriteName,sprinfoToken);
}
Z_Free(sprinfoToken);
Z_Free(info);
Z_Free(parser.info);
}
//

View file

@ -100,8 +100,9 @@ typedef struct
typedef struct
{
spriteframepivot_t pivot[64];
boolean available;
spriteframepivot_t pivot[64 + 1];
#define SPRINFO_DEFAULT_PIVOT (64)
UINT8 available[BIT_ARRAY_SIZE(64 + 1)]; // 1 extra for default_pivot
} spriteinfo_t;
// Portable Network Graphics

View file

@ -628,11 +628,14 @@ void SCR_DisplayTicRate(void)
void SCR_DisplayLocalPing(void)
{
boolean offline;
UINT32 ping = playerpingtable[consoleplayer]; // consoleplayer's ping is everyone's ping in a splitnetgame :P
if (! r_splitscreen && ( cv_showping.value == 1 || (cv_showping.value == 2 && ping > servermaxping) )) // only show 2 (warning) if our ping is at a bad level
{
INT32 dispy = cv_ticrate.value ? 160 : 181;
HU_drawPing(307, dispy, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM | V_HUDTRANS);
offline = (consoleplayer == serverplayer);
HU_drawPing(307, dispy, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM | V_HUDTRANS, offline);
}
}

View file

@ -1219,6 +1219,8 @@ void I_FinishUpdate(void)
}
}
}
if (cv_mindelay.value && consoleplayer == serverplayer && Playing())
SCR_DisplayLocalPing();
}
if (marathonmode)