mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'save_p-unglobal' into acs
This commit is contained in:
commit
7a5b276ece
33 changed files with 4038 additions and 2650 deletions
|
|
@ -1128,8 +1128,6 @@ static boolean SV_SendServerConfig(INT32 node)
|
|||
return waspacketsent;
|
||||
}
|
||||
|
||||
#define SAVEGAMESIZE (768*1024)
|
||||
|
||||
static boolean SV_ResendingSavegameToAnyone(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
|
@ -1143,28 +1141,29 @@ static boolean SV_ResendingSavegameToAnyone(void)
|
|||
static void SV_SendSaveGame(INT32 node, boolean resending)
|
||||
{
|
||||
size_t length, compressedlen;
|
||||
UINT8 *savebuffer;
|
||||
savebuffer_t save;
|
||||
UINT8 *compressedsave;
|
||||
UINT8 *buffertosend;
|
||||
|
||||
// first save it in a malloced buffer
|
||||
savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
|
||||
if (!savebuffer)
|
||||
save.size = NETSAVEGAMESIZE;
|
||||
save.buffer = (UINT8 *)malloc(save.size);
|
||||
if (!save.buffer)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Leave room for the uncompressed length.
|
||||
save_p = savebuffer + sizeof(UINT32);
|
||||
save.p = save.buffer + sizeof(UINT32);
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
P_SaveNetGame(resending);
|
||||
P_SaveNetGame(&save, resending);
|
||||
|
||||
length = save_p - savebuffer;
|
||||
if (length > SAVEGAMESIZE)
|
||||
length = save.p - save.buffer;
|
||||
if (length > NETSAVEGAMESIZE)
|
||||
{
|
||||
free(savebuffer);
|
||||
save_p = NULL;
|
||||
free(save.buffer);
|
||||
I_Error("Savegame buffer overrun");
|
||||
}
|
||||
|
||||
|
|
@ -1178,11 +1177,10 @@ static void SV_SendSaveGame(INT32 node, boolean resending)
|
|||
}
|
||||
|
||||
// Attempt to compress it.
|
||||
if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
|
||||
if ((compressedlen = lzf_compress(save.buffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
|
||||
{
|
||||
// Compressing succeeded; send compressed data
|
||||
|
||||
free(savebuffer);
|
||||
free(save.buffer);
|
||||
|
||||
// State that we're compressed.
|
||||
buffertosend = compressedsave;
|
||||
|
|
@ -1192,16 +1190,14 @@ static void SV_SendSaveGame(INT32 node, boolean resending)
|
|||
else
|
||||
{
|
||||
// Compression failed to make it smaller; send original
|
||||
|
||||
free(compressedsave);
|
||||
|
||||
// State that we're not compressed
|
||||
buffertosend = savebuffer;
|
||||
WRITEUINT32(savebuffer, 0);
|
||||
buffertosend = save.buffer;
|
||||
WRITEUINT32(save.buffer, 0);
|
||||
}
|
||||
|
||||
AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
|
||||
save_p = NULL;
|
||||
|
||||
// Remember when we started sending the savegame so we can handle timeouts
|
||||
sendingsavegame[node] = true;
|
||||
|
|
@ -1215,7 +1211,7 @@ static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SA
|
|||
static void SV_SavedGame(void)
|
||||
{
|
||||
size_t length;
|
||||
UINT8 *savebuffer;
|
||||
savebuffer_t save;
|
||||
char tmpsave[256];
|
||||
|
||||
if (!cv_dumpconsistency.value)
|
||||
|
|
@ -1224,29 +1220,30 @@ static void SV_SavedGame(void)
|
|||
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
|
||||
|
||||
// first save it in a malloced buffer
|
||||
save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
|
||||
if (!save_p)
|
||||
save.size = NETSAVEGAMESIZE;
|
||||
save.p = save.buffer = (UINT8 *)malloc(save.size);
|
||||
if (!save.p)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
P_SaveNetGame(false);
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
length = save_p - savebuffer;
|
||||
if (length > SAVEGAMESIZE)
|
||||
P_SaveNetGame(&save, false);
|
||||
|
||||
length = save.p - save.buffer;
|
||||
if (length > NETSAVEGAMESIZE)
|
||||
{
|
||||
free(savebuffer);
|
||||
save_p = NULL;
|
||||
free(save.buffer);
|
||||
I_Error("Savegame buffer overrun");
|
||||
}
|
||||
|
||||
// then save it!
|
||||
if (!FIL_WriteFile(tmpsave, savebuffer, length))
|
||||
if (!FIL_WriteFile(tmpsave, save.buffer, length))
|
||||
CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
|
||||
|
||||
free(savebuffer);
|
||||
save_p = NULL;
|
||||
free(save.buffer);
|
||||
}
|
||||
|
||||
#undef TMPSAVENAME
|
||||
|
|
@ -1256,13 +1253,13 @@ static void SV_SavedGame(void)
|
|||
|
||||
static void CL_LoadReceivedSavegame(boolean reloading)
|
||||
{
|
||||
UINT8 *savebuffer = NULL;
|
||||
savebuffer_t save;
|
||||
size_t length, decompressedlen;
|
||||
char tmpsave[256];
|
||||
|
||||
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
|
||||
|
||||
length = FIL_ReadFile(tmpsave, &savebuffer);
|
||||
length = FIL_ReadFile(tmpsave, &save.buffer);
|
||||
|
||||
CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length));
|
||||
if (!length)
|
||||
|
|
@ -1271,16 +1268,22 @@ static void CL_LoadReceivedSavegame(boolean reloading)
|
|||
return;
|
||||
}
|
||||
|
||||
save_p = savebuffer;
|
||||
save.p = save.buffer;
|
||||
save.size = length;
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
// Decompress saved game if necessary.
|
||||
decompressedlen = READUINT32(save_p);
|
||||
decompressedlen = READUINT32(save.p);
|
||||
if(decompressedlen > 0)
|
||||
{
|
||||
UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
|
||||
lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
|
||||
Z_Free(savebuffer);
|
||||
save_p = savebuffer = decompressedbuffer;
|
||||
|
||||
lzf_decompress(save.p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
|
||||
Z_Free(save.buffer);
|
||||
|
||||
save.p = save.buffer = decompressedbuffer;
|
||||
save.size = decompressedlen;
|
||||
save.end = save.buffer + decompressedlen;
|
||||
}
|
||||
|
||||
paused = false;
|
||||
|
|
@ -1290,7 +1293,7 @@ static void CL_LoadReceivedSavegame(boolean reloading)
|
|||
automapactive = false;
|
||||
|
||||
// load a base level
|
||||
if (P_LoadNetGame(reloading))
|
||||
if (P_LoadNetGame(&save, reloading))
|
||||
{
|
||||
if (!reloading)
|
||||
{
|
||||
|
|
@ -1312,8 +1315,8 @@ static void CL_LoadReceivedSavegame(boolean reloading)
|
|||
}
|
||||
|
||||
// done
|
||||
Z_Free(savebuffer);
|
||||
save_p = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = NULL;
|
||||
if (unlink(tmpsave) == -1)
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
|
||||
consistancy[gametic%BACKUPTICS] = Consistancy();
|
||||
|
|
@ -6064,6 +6067,7 @@ void CL_ClearRewinds(void)
|
|||
|
||||
rewind_t *CL_SaveRewindPoint(size_t demopos)
|
||||
{
|
||||
savebuffer_t save;
|
||||
rewind_t *rewind;
|
||||
|
||||
if (rewindhead && rewindhead->leveltime + REWIND_POINT_INTERVAL > leveltime)
|
||||
|
|
@ -6073,8 +6077,12 @@ rewind_t *CL_SaveRewindPoint(size_t demopos)
|
|||
if (!rewind)
|
||||
return NULL;
|
||||
|
||||
save_p = rewind->savebuffer;
|
||||
P_SaveNetGame(false);
|
||||
save.buffer = save.p = rewind->savebuffer;
|
||||
save.size = NETSAVEGAMESIZE;
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
P_SaveNetGame(&save, false);
|
||||
|
||||
rewind->leveltime = leveltime;
|
||||
rewind->next = rewindhead;
|
||||
rewind->demopos = demopos;
|
||||
|
|
@ -6085,6 +6093,7 @@ rewind_t *CL_SaveRewindPoint(size_t demopos)
|
|||
|
||||
rewind_t *CL_RewindToTime(tic_t time)
|
||||
{
|
||||
savebuffer_t save;
|
||||
rewind_t *rewind;
|
||||
|
||||
while (rewindhead && rewindhead->leveltime > time)
|
||||
|
|
@ -6097,8 +6106,12 @@ rewind_t *CL_RewindToTime(tic_t time)
|
|||
if (!rewindhead)
|
||||
return NULL;
|
||||
|
||||
save_p = rewindhead->savebuffer;
|
||||
P_LoadNetGame(false);
|
||||
save.buffer = save.p = rewindhead->savebuffer;
|
||||
save.size = NETSAVEGAMESIZE;
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
P_LoadNetGame(&save, false);
|
||||
|
||||
wipegamestate = gamestate; // No fading back in!
|
||||
timeinmap = leveltime;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "mserv.h"
|
||||
|
||||
#include "k_pwrlv.h" // PWRLV_NUMTYPES
|
||||
#include "p_saveg.h" // NETSAVEGAMESIZE
|
||||
|
||||
/*
|
||||
The 'packet version' is used to distinguish packet formats.
|
||||
|
|
@ -530,7 +531,7 @@ extern boolean hu_stopped;
|
|||
//
|
||||
|
||||
struct rewind_t {
|
||||
UINT8 savebuffer[(768*1024)];
|
||||
UINT8 savebuffer[NETSAVEGAMESIZE];
|
||||
tic_t leveltime;
|
||||
size_t demopos;
|
||||
|
||||
|
|
|
|||
12
src/d_main.c
12
src/d_main.c
|
|
@ -1872,6 +1872,18 @@ void D_SRB2Main(void)
|
|||
G_SetUsedCheats();
|
||||
}
|
||||
|
||||
if (grandprixinfo.gp == true && mapheaderinfo[pstartmap-1])
|
||||
{
|
||||
if (mapheaderinfo[pstartmap-1]->typeoflevel & TOL_SPECIAL)
|
||||
{
|
||||
specialStage.active = true;
|
||||
specialStage.encore = grandprixinfo.encore;
|
||||
grandprixinfo.eventmode = GPEVENT_SPECIAL;
|
||||
}
|
||||
|
||||
G_SetUsedCheats();
|
||||
}
|
||||
|
||||
D_MapChange(pstartmap, gametype, (cv_kartencore.value == 1), true, 0, false, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
142
src/d_netcmd.c
142
src/d_netcmd.c
|
|
@ -2853,7 +2853,7 @@ static void Command_Map_f(void)
|
|||
if (mapheaderinfo[newmapnum-1])
|
||||
{
|
||||
// Let's just guess so we don't have to specify the gametype EVERY time...
|
||||
newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & TOL_RACE) ? GT_RACE : GT_BATTLE;
|
||||
newgametype = (mapheaderinfo[newmapnum-1]->typeoflevel & (TOL_BATTLE|TOL_BOSS)) ? GT_BATTLE : GT_RACE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2902,11 +2902,59 @@ static void Command_Map_f(void)
|
|||
|
||||
if (!(netgame || multiplayer))
|
||||
{
|
||||
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
|
||||
grandprixinfo.masterbots = false;
|
||||
|
||||
if (option_skill)
|
||||
{
|
||||
const char *skillname = COM_Argv(option_skill + 1);
|
||||
INT32 newskill = -1;
|
||||
INT32 j;
|
||||
|
||||
for (j = 0; gpdifficulty_cons_t[j].strvalue; j++)
|
||||
{
|
||||
if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname))
|
||||
{
|
||||
newskill = (INT16)gpdifficulty_cons_t[j].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
|
||||
{
|
||||
INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too
|
||||
if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER)
|
||||
newskill = (INT16)num;
|
||||
}
|
||||
|
||||
if (newskill != -1)
|
||||
{
|
||||
if (newskill == KARTGP_MASTER)
|
||||
{
|
||||
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
||||
grandprixinfo.masterbots = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
grandprixinfo.gamespeed = newskill;
|
||||
grandprixinfo.masterbots = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grandprixinfo.encore = newencoremode;
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.roundnum = 0;
|
||||
grandprixinfo.cup = NULL;
|
||||
grandprixinfo.wonround = false;
|
||||
grandprixinfo.initalize = true;
|
||||
|
||||
grandprixinfo.eventmode = GPEVENT_NONE;
|
||||
|
||||
if (newgametype == GT_BATTLE)
|
||||
{
|
||||
grandprixinfo.gp = false;
|
||||
specialStage.active = false;
|
||||
K_ResetBossInfo();
|
||||
grandprixinfo.eventmode = GPEVENT_BONUS;
|
||||
|
||||
if (mapheaderinfo[newmapnum-1] &&
|
||||
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_BOSS)
|
||||
|
|
@ -2914,71 +2962,24 @@ static void Command_Map_f(void)
|
|||
bossinfo.boss = true;
|
||||
bossinfo.encore = newencoremode;
|
||||
}
|
||||
else
|
||||
{
|
||||
bossinfo.boss = false;
|
||||
K_ResetBossInfo();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mapheaderinfo[newmapnum-1] &&
|
||||
mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage
|
||||
{
|
||||
grandprixinfo.gp = false;
|
||||
bossinfo.boss = false;
|
||||
|
||||
specialStage.active = true;
|
||||
specialStage.encore = newencoremode;
|
||||
grandprixinfo.eventmode = GPEVENT_SPECIAL;
|
||||
}
|
||||
else // default GP
|
||||
else
|
||||
{
|
||||
grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value);
|
||||
grandprixinfo.masterbots = false;
|
||||
|
||||
if (option_skill)
|
||||
{
|
||||
const char *skillname = COM_Argv(option_skill + 1);
|
||||
INT32 newskill = -1;
|
||||
INT32 j;
|
||||
|
||||
for (j = 0; gpdifficulty_cons_t[j].strvalue; j++)
|
||||
{
|
||||
if (!strcasecmp(gpdifficulty_cons_t[j].strvalue, skillname))
|
||||
{
|
||||
newskill = (INT16)gpdifficulty_cons_t[j].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gpdifficulty_cons_t[j].strvalue) // reached end of the list with no match
|
||||
{
|
||||
INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too
|
||||
if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER)
|
||||
newskill = (INT16)num;
|
||||
}
|
||||
|
||||
if (newskill != -1)
|
||||
{
|
||||
if (newskill == KARTGP_MASTER)
|
||||
{
|
||||
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
||||
grandprixinfo.masterbots = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
grandprixinfo.gamespeed = newskill;
|
||||
grandprixinfo.masterbots = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grandprixinfo.encore = newencoremode;
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.roundnum = 0;
|
||||
grandprixinfo.cup = NULL;
|
||||
grandprixinfo.wonround = false;
|
||||
|
||||
bossinfo.boss = false;
|
||||
specialStage.active = false;
|
||||
|
||||
grandprixinfo.initalize = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5775,10 +5776,9 @@ static void Command_Togglemodified_f(void)
|
|||
modifiedgame = !modifiedgame;
|
||||
}
|
||||
|
||||
extern UINT8 *save_p;
|
||||
static void Command_Archivetest_f(void)
|
||||
{
|
||||
UINT8 *buf;
|
||||
savebuffer_t save;
|
||||
UINT32 i, wrote;
|
||||
thinker_t *th;
|
||||
if (gamestate != GS_LEVEL)
|
||||
|
|
@ -5794,28 +5794,30 @@ static void Command_Archivetest_f(void)
|
|||
((mobj_t *)th)->mobjnum = i++;
|
||||
|
||||
// allocate buffer
|
||||
buf = save_p = ZZ_Alloc(1024);
|
||||
save.size = 1024;
|
||||
save.buffer = save.p = ZZ_Alloc(save.size);
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
// test archive
|
||||
CONS_Printf("LUA_Archive...\n");
|
||||
LUA_Archive(&save_p);
|
||||
WRITEUINT8(save_p, 0x7F);
|
||||
wrote = (UINT32)(save_p-buf);
|
||||
LUA_Archive(&save, true);
|
||||
WRITEUINT8(save.p, 0x7F);
|
||||
wrote = (UINT32)(save.p - save.buffer);
|
||||
|
||||
// clear Lua state, so we can really see what happens!
|
||||
CONS_Printf("Clearing state!\n");
|
||||
LUA_ClearExtVars();
|
||||
|
||||
// test unarchive
|
||||
save_p = buf;
|
||||
save.p = save.buffer;
|
||||
CONS_Printf("LUA_UnArchive...\n");
|
||||
LUA_UnArchive(&save_p);
|
||||
i = READUINT8(save_p);
|
||||
if (i != 0x7F || wrote != (UINT32)(save_p-buf))
|
||||
CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(save_p-buf));
|
||||
LUA_UnArchive(&save, true);
|
||||
i = READUINT8(save.p);
|
||||
if (i != 0x7F || wrote != (UINT32)(save.p - save.buffer))
|
||||
CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(save.p - save.buffer));
|
||||
|
||||
// free buffer
|
||||
Z_Free(buf);
|
||||
Z_Free(save.buffer);
|
||||
CONS_Printf("Done. No crash.\n");
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4534,6 +4534,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
// Broly Ki Orb
|
||||
"S_BROLY1",
|
||||
"S_BROLY2",
|
||||
|
||||
"S_SPECIAL_UFO_POD",
|
||||
"S_SPECIAL_UFO_OVERLAY",
|
||||
"S_SPECIAL_UFO_ARM",
|
||||
"S_SPECIAL_UFO_STEM",
|
||||
};
|
||||
|
||||
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
|
||||
|
|
@ -5631,6 +5636,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
"MT_BEAMPOINT",
|
||||
|
||||
"MT_BROLY",
|
||||
|
||||
"MT_SPECIAL_UFO",
|
||||
"MT_SPECIAL_UFO_PIECE",
|
||||
};
|
||||
|
||||
const char *const MOBJFLAG_LIST[] = {
|
||||
|
|
|
|||
|
|
@ -549,7 +549,7 @@ void DRPC_UpdatePresence(void)
|
|||
if (gamestate == GS_LEVEL && Playing())
|
||||
{
|
||||
const time_t currentTime = time(NULL);
|
||||
const time_t mapTimeStart = currentTime - ((leveltime + (modeattacking ? starttime : 0)) / TICRATE);
|
||||
const time_t mapTimeStart = currentTime - ((leveltime + starttime) / TICRATE);
|
||||
|
||||
discordPresence.startTimestamp = mapTimeStart;
|
||||
|
||||
|
|
|
|||
668
src/g_demo.c
668
src/g_demo.c
File diff suppressed because it is too large
Load diff
|
|
@ -18,8 +18,6 @@
|
|||
#include "doomstat.h"
|
||||
#include "d_event.h"
|
||||
|
||||
extern UINT8 *demo_p;
|
||||
|
||||
// ======================================
|
||||
// DEMO playback/recording related stuff.
|
||||
// ======================================
|
||||
|
|
|
|||
210
src/g_game.c
210
src/g_game.c
|
|
@ -73,9 +73,6 @@ UINT8 ultimatemode = false;
|
|||
|
||||
JoyType_t Joystick[MAXSPLITSCREENPLAYERS];
|
||||
|
||||
// 1024 bytes is plenty for a savegame
|
||||
#define SAVEGAMESIZE (1024)
|
||||
|
||||
// SRB2kart
|
||||
char gamedatafilename[64] =
|
||||
#ifdef DEVELOP
|
||||
|
|
@ -344,8 +341,6 @@ boolean precache = true; // if true, load all graphics at start
|
|||
|
||||
INT16 prevmap, nextmap;
|
||||
|
||||
static UINT8 *savebuffer;
|
||||
|
||||
static void weaponPrefChange(void);
|
||||
static void weaponPrefChange2(void);
|
||||
static void weaponPrefChange3(void);
|
||||
|
|
@ -4314,6 +4309,7 @@ void G_LoadGameData(void)
|
|||
UINT32 versionID;
|
||||
UINT8 versionMinor;
|
||||
UINT8 rtemp;
|
||||
savebuffer_t save;
|
||||
|
||||
//For records
|
||||
UINT32 numgamedatamapheaders;
|
||||
|
|
@ -4342,7 +4338,7 @@ void G_LoadGameData(void)
|
|||
return;
|
||||
}
|
||||
|
||||
length = FIL_ReadFile(va(pandf, srb2home, gamedatafilename), &savebuffer);
|
||||
length = FIL_ReadFile(va(pandf, srb2home, gamedatafilename), &save.buffer);
|
||||
if (!length)
|
||||
{
|
||||
// No gamedata. We can save a new one.
|
||||
|
|
@ -4350,35 +4346,35 @@ void G_LoadGameData(void)
|
|||
return;
|
||||
}
|
||||
|
||||
save_p = savebuffer;
|
||||
save.p = save.buffer;
|
||||
|
||||
// Version check
|
||||
versionID = READUINT32(save_p);
|
||||
versionID = READUINT32(save.p);
|
||||
if (versionID != GD_VERSIONCHECK)
|
||||
{
|
||||
const char *gdfolder = "the Ring Racers folder";
|
||||
if (strcmp(srb2home,"."))
|
||||
gdfolder = srb2home;
|
||||
|
||||
Z_Free(savebuffer);
|
||||
save_p = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = NULL;
|
||||
I_Error("Game data is not for Ring Racers v2.0.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
|
||||
}
|
||||
|
||||
versionMinor = READUINT8(save_p);
|
||||
versionMinor = READUINT8(save.p);
|
||||
if (versionMinor > GD_VERSIONMINOR)
|
||||
{
|
||||
Z_Free(savebuffer);
|
||||
save_p = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = NULL;
|
||||
I_Error("Game data is from the future! (expected %d, got %d)", GD_VERSIONMINOR, versionMinor);
|
||||
}
|
||||
|
||||
gamedata->totalplaytime = READUINT32(save_p);
|
||||
gamedata->matchesplayed = READUINT32(save_p);
|
||||
gamedata->totalplaytime = READUINT32(save.p);
|
||||
gamedata->matchesplayed = READUINT32(save.p);
|
||||
|
||||
{
|
||||
// Quick & dirty hash for what mod this save file is for.
|
||||
UINT32 modID = READUINT32(save_p);
|
||||
UINT32 modID = READUINT32(save.p);
|
||||
UINT32 expectedID = quickncasehash(timeattackfolder, 64);
|
||||
|
||||
if (modID != expectedID)
|
||||
|
|
@ -4391,34 +4387,34 @@ void G_LoadGameData(void)
|
|||
// To save space, use one bit per collected/achieved/unlocked flag
|
||||
for (i = 0; i < MAXEMBLEMS;)
|
||||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
rtemp = READUINT8(save.p);
|
||||
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
|
||||
gamedata->collected[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
rtemp = READUINT8(save.p);
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
gamedata->unlocked[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
rtemp = READUINT8(save.p);
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
gamedata->unlockpending[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXCONDITIONSETS;)
|
||||
{
|
||||
rtemp = READUINT8(save_p);
|
||||
rtemp = READUINT8(save.p);
|
||||
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
|
||||
gamedata->achieved[j+i] = ((rtemp >> j) & 1);
|
||||
i += j;
|
||||
}
|
||||
|
||||
gamedata->challengegridwidth = READUINT16(save_p);
|
||||
gamedata->challengegridwidth = READUINT16(save.p);
|
||||
Z_Free(gamedata->challengegrid);
|
||||
if (gamedata->challengegridwidth)
|
||||
{
|
||||
|
|
@ -4427,7 +4423,7 @@ void G_LoadGameData(void)
|
|||
PU_STATIC, NULL);
|
||||
for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++)
|
||||
{
|
||||
gamedata->challengegrid[i] = READUINT8(save_p);
|
||||
gamedata->challengegrid[i] = READUINT8(save.p);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -4435,10 +4431,10 @@ void G_LoadGameData(void)
|
|||
gamedata->challengegrid = NULL;
|
||||
}
|
||||
|
||||
gamedata->timesBeaten = READUINT32(save_p);
|
||||
gamedata->timesBeaten = READUINT32(save.p);
|
||||
|
||||
// Main records
|
||||
numgamedatamapheaders = READUINT32(save_p);
|
||||
numgamedatamapheaders = READUINT32(save.p);
|
||||
if (numgamedatamapheaders >= NEXTMAP_SPECIAL)
|
||||
goto datacorrupt;
|
||||
|
||||
|
|
@ -4449,12 +4445,12 @@ void G_LoadGameData(void)
|
|||
tic_t rectime;
|
||||
tic_t reclap;
|
||||
|
||||
READSTRINGN(save_p, mapname, sizeof(mapname));
|
||||
READSTRINGN(save.p, mapname, sizeof(mapname));
|
||||
mapnum = G_MapNumber(mapname);
|
||||
|
||||
rtemp = READUINT8(save_p);
|
||||
rectime = (tic_t)READUINT32(save_p);
|
||||
reclap = (tic_t)READUINT32(save_p);
|
||||
rtemp = READUINT8(save.p);
|
||||
rectime = (tic_t)READUINT32(save.p);
|
||||
reclap = (tic_t)READUINT32(save.p);
|
||||
|
||||
if (mapnum < nummapheaders && mapheaderinfo[mapnum])
|
||||
{
|
||||
|
|
@ -4480,8 +4476,8 @@ void G_LoadGameData(void)
|
|||
}
|
||||
|
||||
// done
|
||||
Z_Free(savebuffer);
|
||||
save_p = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = NULL;
|
||||
|
||||
// Don't consider loaded until it's a success!
|
||||
// It used to do this much earlier, but this would cause the gamedata to
|
||||
|
|
@ -4501,8 +4497,8 @@ void G_LoadGameData(void)
|
|||
if (strcmp(srb2home,"."))
|
||||
gdfolder = srb2home;
|
||||
|
||||
Z_Free(savebuffer);
|
||||
save_p = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = NULL;
|
||||
|
||||
I_Error("Corrupt game data file.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
|
||||
}
|
||||
|
|
@ -4515,6 +4511,7 @@ void G_SaveGameData(void)
|
|||
size_t length;
|
||||
INT32 i, j;
|
||||
UINT8 btemp;
|
||||
savebuffer_t save;
|
||||
|
||||
if (!gamedata->loaded)
|
||||
return; // If never loaded (-nodata), don't save
|
||||
|
|
@ -4534,20 +4531,22 @@ void G_SaveGameData(void)
|
|||
}
|
||||
length += nummapheaders * (MAXMAPLUMPNAME+1+4+4);
|
||||
|
||||
save_p = savebuffer = (UINT8 *)malloc(length);
|
||||
if (!save_p)
|
||||
save.size = length;
|
||||
save.p = save.buffer = (UINT8 *)malloc(save.size);
|
||||
if (!save.p)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for saving game data\n"));
|
||||
return;
|
||||
}
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
// Version test
|
||||
|
||||
WRITEUINT32(save_p, GD_VERSIONCHECK); // 4
|
||||
WRITEUINT8(save_p, GD_VERSIONMINOR); // 1
|
||||
WRITEUINT32(save_p, gamedata->totalplaytime); // 4
|
||||
WRITEUINT32(save_p, gamedata->matchesplayed); // 4
|
||||
WRITEUINT32(save_p, quickncasehash(timeattackfolder, 64));
|
||||
WRITEUINT32(save.p, GD_VERSIONCHECK); // 4
|
||||
WRITEUINT8(save.p, GD_VERSIONMINOR); // 1
|
||||
WRITEUINT32(save.p, gamedata->totalplaytime); // 4
|
||||
WRITEUINT32(save.p, gamedata->matchesplayed); // 4
|
||||
WRITEUINT32(save.p, quickncasehash(timeattackfolder, 64));
|
||||
|
||||
// To save space, use one bit per collected/achieved/unlocked flag
|
||||
for (i = 0; i < MAXEMBLEMS;) // MAXEMBLEMS * 1;
|
||||
|
|
@ -4555,7 +4554,7 @@ void G_SaveGameData(void)
|
|||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
|
||||
btemp |= (gamedata->collected[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
WRITEUINT8(save.p, btemp);
|
||||
i += j;
|
||||
}
|
||||
|
||||
|
|
@ -4565,7 +4564,7 @@ void G_SaveGameData(void)
|
|||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
btemp |= (gamedata->unlocked[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
WRITEUINT8(save.p, btemp);
|
||||
i += j;
|
||||
}
|
||||
for (i = 0; i < MAXUNLOCKABLES;)
|
||||
|
|
@ -4573,7 +4572,7 @@ void G_SaveGameData(void)
|
|||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
|
||||
btemp |= (gamedata->unlockpending[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
WRITEUINT8(save.p, btemp);
|
||||
i += j;
|
||||
}
|
||||
|
||||
|
|
@ -4582,52 +4581,51 @@ void G_SaveGameData(void)
|
|||
btemp = 0;
|
||||
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
|
||||
btemp |= (gamedata->achieved[j+i] << j);
|
||||
WRITEUINT8(save_p, btemp);
|
||||
WRITEUINT8(save.p, btemp);
|
||||
i += j;
|
||||
}
|
||||
|
||||
if (gamedata->challengegrid) // 2 + gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT
|
||||
{
|
||||
WRITEUINT16(save_p, gamedata->challengegridwidth);
|
||||
WRITEUINT16(save.p, gamedata->challengegridwidth);
|
||||
for (i = 0; i < (gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT); i++)
|
||||
{
|
||||
WRITEUINT8(save_p, gamedata->challengegrid[i]);
|
||||
WRITEUINT8(save.p, gamedata->challengegrid[i]);
|
||||
}
|
||||
}
|
||||
else // 2
|
||||
{
|
||||
WRITEUINT16(save_p, 0);
|
||||
WRITEUINT16(save.p, 0);
|
||||
}
|
||||
|
||||
WRITEUINT32(save_p, gamedata->timesBeaten); // 4
|
||||
WRITEUINT32(save.p, gamedata->timesBeaten); // 4
|
||||
|
||||
// Main records
|
||||
WRITEUINT32(save_p, nummapheaders); // 4
|
||||
WRITEUINT32(save.p, nummapheaders); // 4
|
||||
|
||||
for (i = 0; i < nummapheaders; i++) // nummapheaders * (255+1+4+4)
|
||||
{
|
||||
// For figuring out which header to assing it to on load
|
||||
WRITESTRINGN(save_p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME);
|
||||
WRITESTRINGN(save.p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME);
|
||||
|
||||
WRITEUINT8(save_p, (mapheaderinfo[i]->mapvisited & MV_MAX));
|
||||
WRITEUINT8(save.p, (mapheaderinfo[i]->mapvisited & MV_MAX));
|
||||
|
||||
if (mapheaderinfo[i]->mainrecord)
|
||||
{
|
||||
WRITEUINT32(save_p, mapheaderinfo[i]->mainrecord->time);
|
||||
WRITEUINT32(save_p, mapheaderinfo[i]->mainrecord->lap);
|
||||
WRITEUINT32(save.p, mapheaderinfo[i]->mainrecord->time);
|
||||
WRITEUINT32(save.p, mapheaderinfo[i]->mainrecord->lap);
|
||||
}
|
||||
else
|
||||
{
|
||||
WRITEUINT32(save_p, 0);
|
||||
WRITEUINT32(save_p, 0);
|
||||
WRITEUINT32(save.p, 0);
|
||||
WRITEUINT32(save.p, 0);
|
||||
}
|
||||
}
|
||||
|
||||
length = save_p - savebuffer;
|
||||
length = save.p - save.buffer;
|
||||
|
||||
FIL_WriteFile(va(pandf, srb2home, gamedatafilename), savebuffer, length);
|
||||
free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
FIL_WriteFile(va(pandf, srb2home, gamedatafilename), save.buffer, length);
|
||||
free(save.buffer);
|
||||
|
||||
// Also save profiles here.
|
||||
PR_SaveProfiles();
|
||||
|
|
@ -4644,6 +4642,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
size_t length;
|
||||
char vcheck[VERSIONSIZE];
|
||||
char savename[255];
|
||||
savebuffer_t save;
|
||||
|
||||
// memset savedata to all 0, fixes calling perfectly valid saves corrupt because of bots
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
|
|
@ -4658,18 +4657,20 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
|
||||
length = FIL_ReadFile(savename, &savebuffer);
|
||||
length = FIL_ReadFile(savename, &save.buffer);
|
||||
if (!length)
|
||||
{
|
||||
CONS_Printf(M_GetText("Couldn't read file %s\n"), savename);
|
||||
return;
|
||||
}
|
||||
|
||||
save_p = savebuffer;
|
||||
save.p = save.buffer;
|
||||
save.size = length;
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
if (strcmp((const char *)save_p, (const char *)vcheck))
|
||||
if (strcmp((const char *)save.p, (const char *)vcheck))
|
||||
{
|
||||
#ifdef SAVEGAME_OTHERVERSIONS
|
||||
M_StartMessage(M_GetText("Save game from different version.\nYou can load this savegame, but\nsaving afterwards will be disabled.\n\nDo you want to continue anyway?\n\n(Press 'Y' to confirm)\n"),
|
||||
|
|
@ -4679,15 +4680,14 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
M_ClearMenus(true); // so ESC backs out to title
|
||||
M_StartMessage(M_GetText("Save game from different version\n\nPress ESC\n"), NULL, MM_NOTHING);
|
||||
Command_ExitGame_f();
|
||||
Z_Free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
Z_Free(save.buffer);
|
||||
|
||||
// no cheating!
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
#endif
|
||||
return; // bad version
|
||||
}
|
||||
save_p += VERSIONSIZE;
|
||||
save.p += VERSIONSIZE;
|
||||
|
||||
if (demo.playback) // reset game engine
|
||||
G_StopDemo();
|
||||
|
|
@ -4696,13 +4696,13 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
// automapactive = false;
|
||||
|
||||
// dearchive all the modifications
|
||||
if (!P_LoadGame(mapoverride))
|
||||
if (!P_LoadGame(&save, mapoverride))
|
||||
{
|
||||
M_ClearMenus(true); // so ESC backs out to title
|
||||
M_StartMessage(M_GetText("Savegame file corrupted\n\nPress ESC\n"), NULL, MM_NOTHING);
|
||||
Command_ExitGame_f();
|
||||
Z_Free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = save.buffer = NULL;
|
||||
|
||||
// no cheating!
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
|
|
@ -4710,13 +4710,12 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
}
|
||||
if (marathonmode)
|
||||
{
|
||||
marathontime = READUINT32(save_p);
|
||||
marathonmode |= READUINT8(save_p);
|
||||
marathontime = READUINT32(save.p);
|
||||
marathonmode |= READUINT8(save.p);
|
||||
}
|
||||
|
||||
// done
|
||||
Z_Free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
Z_Free(save.buffer);
|
||||
|
||||
// gameaction = ga_nothing;
|
||||
// G_SetGamestate(GS_LEVEL);
|
||||
|
|
@ -4742,6 +4741,7 @@ void G_SaveGame(UINT32 slot, INT16 mapnum)
|
|||
boolean saved;
|
||||
char savename[256] = "";
|
||||
const char *backup;
|
||||
savebuffer_t save;
|
||||
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
|
|
@ -4754,31 +4754,32 @@ void G_SaveGame(UINT32 slot, INT16 mapnum)
|
|||
char name[VERSIONSIZE];
|
||||
size_t length;
|
||||
|
||||
save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
|
||||
if (!save_p)
|
||||
save.size = SAVEGAMESIZE;
|
||||
save.p = save.buffer = (UINT8 *)malloc(save.size);
|
||||
if (!save.p)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for saving game data\n"));
|
||||
return;
|
||||
}
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
memset(name, 0, sizeof (name));
|
||||
sprintf(name, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
WRITEMEM(save_p, name, VERSIONSIZE);
|
||||
WRITEMEM(save.p, name, VERSIONSIZE);
|
||||
|
||||
P_SaveGame(mapnum);
|
||||
P_SaveGame(&save, mapnum);
|
||||
if (marathonmode)
|
||||
{
|
||||
UINT32 writetime = marathontime;
|
||||
if (!(marathonmode & MA_INGAME))
|
||||
writetime += TICRATE*5; // live event backup penalty because we don't know how long it takes to get to the next map
|
||||
WRITEUINT32(save_p, writetime);
|
||||
WRITEUINT8(save_p, (marathonmode & ~MA_INIT));
|
||||
WRITEUINT32(save.p, writetime);
|
||||
WRITEUINT8(save.p, (marathonmode & ~MA_INIT));
|
||||
}
|
||||
|
||||
length = save_p - savebuffer;
|
||||
saved = FIL_WriteFile(backup, savebuffer, length);
|
||||
free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
length = save.p - save.buffer;
|
||||
saved = FIL_WriteFile(backup, save.buffer, length);
|
||||
free(save.buffer);
|
||||
}
|
||||
|
||||
gameaction = ga_nothing;
|
||||
|
|
@ -4790,7 +4791,7 @@ void G_SaveGame(UINT32 slot, INT16 mapnum)
|
|||
}
|
||||
|
||||
#define BADSAVE goto cleanup;
|
||||
#define CHECKPOS if (save_p >= end_p) BADSAVE
|
||||
#define CHECKPOS if (save.p >= save.end) BADSAVE
|
||||
void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
||||
{
|
||||
boolean saved = false;
|
||||
|
|
@ -4798,6 +4799,7 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
|||
char vcheck[VERSIONSIZE];
|
||||
char savename[255];
|
||||
const char *backup;
|
||||
savebuffer_t save;
|
||||
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
|
|
@ -4805,7 +4807,7 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
|||
sprintf(savename, savegamename, slot);
|
||||
backup = va("%s",savename);
|
||||
|
||||
length = FIL_ReadFile(savename, &savebuffer);
|
||||
length = FIL_ReadFile(savename, &save.buffer);
|
||||
if (!length)
|
||||
{
|
||||
CONS_Printf(M_GetText("Couldn't read file %s\n"), savename);
|
||||
|
|
@ -4814,35 +4816,37 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
|||
|
||||
{
|
||||
char temp[sizeof(timeattackfolder)];
|
||||
UINT8 *end_p = savebuffer + length;
|
||||
UINT8 *lives_p;
|
||||
SINT8 pllives;
|
||||
|
||||
save_p = savebuffer;
|
||||
save.p = save.buffer;
|
||||
save.size = length;
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
// Version check
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE
|
||||
save_p += VERSIONSIZE;
|
||||
if (strcmp((const char *)save.p, (const char *)vcheck)) BADSAVE
|
||||
save.p += VERSIONSIZE;
|
||||
|
||||
// P_UnArchiveMisc()
|
||||
(void)READINT16(save_p);
|
||||
(void)READINT16(save.p);
|
||||
CHECKPOS
|
||||
(void)READUINT16(save_p); // emeralds
|
||||
(void)READUINT16(save.p); // emeralds
|
||||
CHECKPOS
|
||||
READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to
|
||||
READSTRINGN(save.p, temp, sizeof(temp)); // mod it belongs to
|
||||
if (strcmp(temp, timeattackfolder)) BADSAVE
|
||||
|
||||
// P_UnArchivePlayer()
|
||||
CHECKPOS
|
||||
(void)READUINT16(save_p);
|
||||
(void)READUINT16(save.p);
|
||||
CHECKPOS
|
||||
|
||||
WRITEUINT8(save_p, numgameovers);
|
||||
WRITEUINT8(save.p, numgameovers);
|
||||
CHECKPOS
|
||||
|
||||
lives_p = save_p;
|
||||
pllives = READSINT8(save_p); // lives
|
||||
lives_p = save.p;
|
||||
pllives = READSINT8(save.p); // lives
|
||||
CHECKPOS
|
||||
if (modifylives && pllives < startinglivesbalance[numgameovers])
|
||||
{
|
||||
|
|
@ -4850,28 +4854,28 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
|||
WRITESINT8(lives_p, pllives);
|
||||
}
|
||||
|
||||
(void)READINT32(save_p); // Score
|
||||
(void)READINT32(save.p); // Score
|
||||
CHECKPOS
|
||||
(void)READINT32(save_p); // continues
|
||||
(void)READINT32(save.p); // continues
|
||||
|
||||
// File end marker check
|
||||
CHECKPOS
|
||||
switch (READUINT8(save_p))
|
||||
switch (READUINT8(save.p))
|
||||
{
|
||||
case 0xb7:
|
||||
{
|
||||
UINT8 i, banksinuse;
|
||||
CHECKPOS
|
||||
banksinuse = READUINT8(save_p);
|
||||
banksinuse = READUINT8(save.p);
|
||||
CHECKPOS
|
||||
if (banksinuse > NUM_LUABANKS)
|
||||
BADSAVE
|
||||
for (i = 0; i < banksinuse; i++)
|
||||
{
|
||||
(void)READINT32(save_p);
|
||||
(void)READINT32(save.p);
|
||||
CHECKPOS
|
||||
}
|
||||
if (READUINT8(save_p) != 0x1d)
|
||||
if (READUINT8(save.p) != 0x1d)
|
||||
BADSAVE
|
||||
}
|
||||
case 0x1d:
|
||||
|
|
@ -4881,7 +4885,7 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
|||
}
|
||||
|
||||
// done
|
||||
saved = FIL_WriteFile(backup, savebuffer, length);
|
||||
saved = FIL_WriteFile(backup, save.buffer, length);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
|
@ -4889,8 +4893,8 @@ cleanup:
|
|||
CONS_Printf(M_GetText("Game saved.\n"));
|
||||
else if (!saved)
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
|
||||
Z_Free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = save.buffer = NULL;
|
||||
|
||||
}
|
||||
#undef CHECKPOS
|
||||
|
|
|
|||
65
src/info.c
65
src/info.c
|
|
@ -785,6 +785,10 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
|
||||
"FLBM",
|
||||
|
||||
"UFOB",
|
||||
"UFOA",
|
||||
"UFOS",
|
||||
|
||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||
"VIEW",
|
||||
};
|
||||
|
|
@ -5146,6 +5150,11 @@ state_t states[NUMSTATES] =
|
|||
// Broly Ki Orb
|
||||
{SPR_LSSJ, FF_REVERSESUBTRACT|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_BROLY2}, // S_BROLY1
|
||||
{SPR_NULL, 0, 5*TICRATE, {A_SSMineFlash}, 0, 0, S_NULL}, // S_BROLY2
|
||||
|
||||
{SPR_UFOB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_POD
|
||||
{SPR_UFOB, 1|FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_SPECIAL_UFO_OVERLAY
|
||||
{SPR_UFOA, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_ARM
|
||||
{SPR_UFOS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_STEM
|
||||
};
|
||||
|
||||
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
||||
|
|
@ -23622,7 +23631,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
100, // mass
|
||||
1, // damage
|
||||
sfx_kc64, // activesound
|
||||
MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
|
||||
MF_SOLID|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
|
|
@ -29084,6 +29093,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_SPECIAL_UFO
|
||||
-1, // doomednum
|
||||
S_CHAOSEMERALD1, // spawnstate
|
||||
101, // 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
|
||||
108*FRACUNIT, // radius
|
||||
72*FRACUNIT, // height
|
||||
0, // display offset
|
||||
16, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SHOOTABLE|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_SPECIAL_UFO_PIECE
|
||||
-1, // doomednum
|
||||
S_INVISIBLE, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
8*FRACUNIT, // radius
|
||||
16*FRACUNIT, // height
|
||||
1, // display offset
|
||||
100, // mass
|
||||
1, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOSQUISH, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
};
|
||||
|
||||
skincolor_t skincolors[MAXSKINCOLORS] = {
|
||||
|
|
|
|||
12
src/info.h
12
src/info.h
|
|
@ -1332,6 +1332,10 @@ typedef enum sprite
|
|||
|
||||
SPR_FLBM, // Finish line beam
|
||||
|
||||
SPR_UFOB,
|
||||
SPR_UFOA,
|
||||
SPR_UFOS,
|
||||
|
||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||
SPR_VIEW,
|
||||
|
||||
|
|
@ -5569,6 +5573,11 @@ typedef enum state
|
|||
S_BROLY1,
|
||||
S_BROLY2,
|
||||
|
||||
S_SPECIAL_UFO_POD,
|
||||
S_SPECIAL_UFO_OVERLAY,
|
||||
S_SPECIAL_UFO_ARM,
|
||||
S_SPECIAL_UFO_STEM,
|
||||
|
||||
S_FIRSTFREESLOT,
|
||||
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
|
||||
NUMSTATES
|
||||
|
|
@ -6686,6 +6695,9 @@ typedef enum mobj_type
|
|||
|
||||
MT_BROLY,
|
||||
|
||||
MT_SPECIAL_UFO,
|
||||
MT_SPECIAL_UFO_PIECE,
|
||||
|
||||
MT_FIRSTFREESLOT,
|
||||
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
|
||||
NUMMOBJTYPES
|
||||
|
|
|
|||
374
src/k_kart.c
374
src/k_kart.c
|
|
@ -184,6 +184,9 @@ UINT32 K_GetPlayerDontDrawFlag(player_t *player)
|
|||
{
|
||||
UINT32 flag = 0;
|
||||
|
||||
if (player == NULL)
|
||||
return flag;
|
||||
|
||||
if (player == &players[displayplayers[0]])
|
||||
flag = RF_DONTDRAWP1;
|
||||
else if (r_splitscreen >= 1 && player == &players[displayplayers[1]])
|
||||
|
|
@ -507,6 +510,51 @@ void K_RunItemCooldowns(void)
|
|||
}
|
||||
}
|
||||
|
||||
boolean K_TimeAttackRules(void)
|
||||
{
|
||||
UINT8 playing = 0;
|
||||
UINT8 i;
|
||||
|
||||
if (specialStage.active == true)
|
||||
{
|
||||
// Kind of a hack -- Special Stages
|
||||
// are expected to be 1-player, so
|
||||
// we won't use the Time Attack changes
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modeattacking != ATTACKING_NONE)
|
||||
{
|
||||
// Time Attack obviously uses Time Attack rules :p
|
||||
return true;
|
||||
}
|
||||
|
||||
if (battlecapsules == true)
|
||||
{
|
||||
// Break the Capsules always uses Time Attack
|
||||
// rules, since you can bring 2-4 players in
|
||||
// via Grand Prix.
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false || players[i].spectator == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
playing++;
|
||||
if (playing > 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Use Time Attack gameplay rules with only 1P.
|
||||
return (playing <= 1);
|
||||
}
|
||||
|
||||
//}
|
||||
|
||||
//{ SRB2kart p_user.c Stuff
|
||||
|
|
@ -530,6 +578,10 @@ static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against)
|
|||
{
|
||||
weight = 0; // This player does not cause any bump action
|
||||
}
|
||||
else if (against && against->type == MT_SPECIAL_UFO)
|
||||
{
|
||||
weight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Applies rubberbanding, to prevent rubberbanding bots
|
||||
|
|
@ -1029,7 +1081,7 @@ static void K_UpdateOffroad(player_t *player)
|
|||
player->offroad = 0;
|
||||
}
|
||||
|
||||
static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent)
|
||||
static void K_DrawDraftCombiring(player_t *player, mobj_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent)
|
||||
{
|
||||
#define CHAOTIXBANDLEN 15
|
||||
#define CHAOTIXBANDCOLORS 9
|
||||
|
|
@ -1060,9 +1112,9 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur
|
|||
c = FixedMul(CHAOTIXBANDCOLORS<<FRACBITS, FixedDiv(curdist-minimumdist, maxdist-minimumdist)) >> FRACBITS;
|
||||
}
|
||||
|
||||
stepx = (victim->mo->x - player->mo->x) / CHAOTIXBANDLEN;
|
||||
stepy = (victim->mo->y - player->mo->y) / CHAOTIXBANDLEN;
|
||||
stepz = ((victim->mo->z + (victim->mo->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN;
|
||||
stepx = (victim->x - player->mo->x) / CHAOTIXBANDLEN;
|
||||
stepy = (victim->y - player->mo->y) / CHAOTIXBANDLEN;
|
||||
stepz = ((victim->z + (victim->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN;
|
||||
|
||||
curx = player->mo->x + stepx;
|
||||
cury = player->mo->y + stepy;
|
||||
|
|
@ -1096,7 +1148,7 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur
|
|||
if (transparent)
|
||||
band->renderflags |= RF_GHOSTLY;
|
||||
|
||||
band->renderflags |= RF_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim));
|
||||
band->renderflags |= RF_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim->player));
|
||||
}
|
||||
|
||||
curx += stepx;
|
||||
|
|
@ -1121,6 +1173,129 @@ static boolean K_HasInfiniteTether(player_t *player)
|
|||
return false;
|
||||
}
|
||||
|
||||
static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed_t draftdistance, UINT8 leniency)
|
||||
{
|
||||
//#define EASYDRAFTTEST
|
||||
fixed_t dist, olddraft;
|
||||
fixed_t theirSpeed = 0;
|
||||
#ifndef EASYDRAFTTEST
|
||||
angle_t yourangle, theirangle, diff;
|
||||
#endif
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
// Don't draft on yourself :V
|
||||
if (dest->player && dest->player == player)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dest->player != NULL)
|
||||
{
|
||||
// No tethering off of the guy who got the starting bonus :P
|
||||
if (dest->player->startboost > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
theirSpeed = dest->player->speed;
|
||||
}
|
||||
else
|
||||
{
|
||||
theirSpeed = R_PointToDist2(0, 0, dest->momx, dest->momy);
|
||||
}
|
||||
|
||||
// They're not enough speed to draft off of them.
|
||||
if (theirSpeed < 20 * dest->scale)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
yourangle = K_MomentumAngle(player->mo);
|
||||
theirangle = K_MomentumAngle(dest);
|
||||
|
||||
// Not in front of this player.
|
||||
diff = AngleDelta(R_PointToAngle2(player->mo->x, player->mo->y, dest->x, dest->y), yourangle);
|
||||
if (diff > ANG10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not moving in the same direction.
|
||||
diff = AngleDelta(yourangle, theirangle);
|
||||
if (diff > ANGLE_90)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
dist = P_AproxDistance(P_AproxDistance(dest->x - player->mo->x, dest->y - player->mo->y), dest->z - player->mo->z);
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
// TOO close to draft.
|
||||
if (dist < minDist)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not close enough to draft.
|
||||
if (dist > draftdistance && draftdistance > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
olddraft = player->draftpower;
|
||||
|
||||
player->draftleeway = leniency;
|
||||
|
||||
if (dest->player != NULL)
|
||||
{
|
||||
player->lastdraft = dest->player - players;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->lastdraft = MAXPLAYERS;
|
||||
}
|
||||
|
||||
// Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed.
|
||||
// How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic)
|
||||
if (player->draftpower < FRACUNIT)
|
||||
{
|
||||
fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));;
|
||||
player->draftpower += add;
|
||||
|
||||
if (player->bot && player->botvars.rival)
|
||||
{
|
||||
// Double speed for the rival!
|
||||
player->draftpower += add;
|
||||
}
|
||||
|
||||
if (gametype == GT_BATTLE)
|
||||
{
|
||||
// TODO: gametyperules
|
||||
// Double speed in Battle
|
||||
player->draftpower += add;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->draftpower > FRACUNIT)
|
||||
{
|
||||
player->draftpower = FRACUNIT;
|
||||
}
|
||||
|
||||
// Play draft finish noise
|
||||
if (olddraft < FRACUNIT && player->draftpower >= FRACUNIT)
|
||||
{
|
||||
S_StartSound(player->mo, sfx_cdfm62);
|
||||
}
|
||||
|
||||
// Spawn in the visual!
|
||||
K_DrawDraftCombiring(player, dest, dist, draftdistance, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \brief Updates the player's drafting values once per frame
|
||||
|
||||
\param player player object passed from K_KartPlayerThink
|
||||
|
|
@ -1129,6 +1304,9 @@ static boolean K_HasInfiniteTether(player_t *player)
|
|||
*/
|
||||
static void K_UpdateDraft(player_t *player)
|
||||
{
|
||||
const boolean addUfo = ((specialStage.active == true)
|
||||
&& (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false));
|
||||
|
||||
fixed_t topspd = K_GetKartSpeed(player, false, false);
|
||||
fixed_t draftdistance;
|
||||
fixed_t minDist;
|
||||
|
|
@ -1166,104 +1344,43 @@ static void K_UpdateDraft(player_t *player)
|
|||
}
|
||||
|
||||
// Not enough speed to draft.
|
||||
if (player->speed >= 20*player->mo->scale)
|
||||
if (player->speed >= 20 * player->mo->scale)
|
||||
{
|
||||
//#define EASYDRAFTTEST
|
||||
if (addUfo == true)
|
||||
{
|
||||
// Tether off of the UFO!
|
||||
if (K_TryDraft(player, specialStage.ufo, minDist, draftdistance, leniency) == true)
|
||||
{
|
||||
return; // Finished doing our draft.
|
||||
}
|
||||
}
|
||||
|
||||
// Let's hunt for players to draft off of!
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
fixed_t dist, olddraft;
|
||||
#ifndef EASYDRAFTTEST
|
||||
angle_t yourangle, theirangle, diff;
|
||||
#endif
|
||||
player_t *otherPlayer = NULL;
|
||||
|
||||
if (!playeringame[i] || players[i].spectator || !players[i].mo)
|
||||
continue;
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
// Don't draft on yourself :V
|
||||
if (&players[i] == player)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Not enough speed to draft off of.
|
||||
if (players[i].speed < 20*players[i].mo->scale)
|
||||
continue;
|
||||
|
||||
// No tethering off of the guy who got the starting bonus :P
|
||||
if (players[i].startboost > 0)
|
||||
continue;
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
yourangle = K_MomentumAngle(player->mo);
|
||||
theirangle = K_MomentumAngle(players[i].mo);
|
||||
|
||||
diff = R_PointToAngle2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y) - yourangle;
|
||||
if (diff > ANGLE_180)
|
||||
diff = InvAngle(diff);
|
||||
|
||||
// Not in front of this player.
|
||||
if (diff > ANG10)
|
||||
continue;
|
||||
|
||||
diff = yourangle - theirangle;
|
||||
if (diff > ANGLE_180)
|
||||
diff = InvAngle(diff);
|
||||
|
||||
// Not moving in the same direction.
|
||||
if (diff > ANGLE_90)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
dist = P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x, players[i].mo->y - player->mo->y), players[i].mo->z - player->mo->z);
|
||||
|
||||
#ifndef EASYDRAFTTEST
|
||||
// TOO close to draft.
|
||||
if (dist < minDist)
|
||||
continue;
|
||||
|
||||
// Not close enough to draft.
|
||||
if (dist > draftdistance && draftdistance > 0)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
olddraft = player->draftpower;
|
||||
|
||||
player->draftleeway = leniency;
|
||||
player->lastdraft = i;
|
||||
|
||||
// Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed.
|
||||
// How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic)
|
||||
if (player->draftpower < FRACUNIT)
|
||||
if (playeringame[i] == false)
|
||||
{
|
||||
fixed_t add = (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600));;
|
||||
player->draftpower += add;
|
||||
|
||||
if (player->bot && player->botvars.rival)
|
||||
{
|
||||
// Double speed for the rival!
|
||||
player->draftpower += add;
|
||||
}
|
||||
|
||||
if (gametype == GT_BATTLE)
|
||||
{
|
||||
// TODO: gametyperules
|
||||
// Double speed in Battle
|
||||
player->draftpower += add;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->draftpower > FRACUNIT)
|
||||
player->draftpower = FRACUNIT;
|
||||
otherPlayer = &players[i];
|
||||
|
||||
// Play draft finish noise
|
||||
if (olddraft < FRACUNIT && player->draftpower >= FRACUNIT)
|
||||
S_StartSound(player->mo, sfx_cdfm62);
|
||||
if (otherPlayer->spectator == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Spawn in the visual!
|
||||
K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false);
|
||||
if (otherPlayer->mo == NULL || P_MobjWasRemoved(otherPlayer->mo) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return; // Finished doing our draft.
|
||||
if (K_TryDraft(player, otherPlayer->mo, minDist, draftdistance, leniency) == true)
|
||||
{
|
||||
return; // Finished doing our draft.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1284,7 +1401,13 @@ static void K_UpdateDraft(player_t *player)
|
|||
{
|
||||
player_t *victim = &players[player->lastdraft];
|
||||
fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z);
|
||||
K_DrawDraftCombiring(player, victim, dist, draftdistance, true);
|
||||
K_DrawDraftCombiring(player, victim->mo, dist, draftdistance, true);
|
||||
}
|
||||
else if (addUfo == true)
|
||||
{
|
||||
// kind of a hack to not have to mess with how lastdraft works
|
||||
fixed_t dist = P_AproxDistance(P_AproxDistance(specialStage.ufo->x - player->mo->x, specialStage.ufo->y - player->mo->y), specialStage.ufo->z - player->mo->z);
|
||||
K_DrawDraftCombiring(player, specialStage.ufo, dist, draftdistance, true);
|
||||
}
|
||||
}
|
||||
else // Remove draft speed boost.
|
||||
|
|
@ -3135,7 +3258,7 @@ boolean K_PlayerShrinkCheat(player_t *player)
|
|||
return (
|
||||
(player->pflags & PF_SHRINKACTIVE)
|
||||
&& (player->bot == false)
|
||||
&& (modeattacking == false) // Anyone want to make another record attack category?
|
||||
&& (modeattacking == ATTACKING_NONE) // Anyone want to make another record attack category?
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -6664,12 +6787,18 @@ static void K_MoveHeldObjects(player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
||||
mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
||||
{
|
||||
fixed_t best = INT32_MAX;
|
||||
player_t *wtarg = NULL;
|
||||
mobj_t *wtarg = NULL;
|
||||
INT32 i;
|
||||
|
||||
if (specialStage.active == true)
|
||||
{
|
||||
// Always target the UFO.
|
||||
return specialStage.ufo;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
angle_t thisang = ANGLE_MAX;
|
||||
|
|
@ -6685,7 +6814,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
|||
player = &players[i];
|
||||
|
||||
// Don't target yourself, stupid.
|
||||
if (player == source)
|
||||
if (source != NULL && player == source)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -6724,7 +6853,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
|||
|
||||
if (gametyperules & GTR_CIRCUIT)
|
||||
{
|
||||
if (player->position >= source->position)
|
||||
if (source != NULL && player->position >= source->position)
|
||||
{
|
||||
// Don't pay attention to people who aren't above your position
|
||||
continue;
|
||||
|
|
@ -6766,7 +6895,7 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
|||
|
||||
if (thisScore < best)
|
||||
{
|
||||
wtarg = player;
|
||||
wtarg = player->mo;
|
||||
best = thisScore;
|
||||
}
|
||||
}
|
||||
|
|
@ -7895,24 +8024,32 @@ void K_KartPlayerAfterThink(player_t *player)
|
|||
// Jawz reticule (seeking)
|
||||
if (player->itemtype == KITEM_JAWZ && (player->pflags & PF_ITEMOUT))
|
||||
{
|
||||
INT32 lastTargID = player->lastjawztarget;
|
||||
player_t *lastTarg = NULL;
|
||||
player_t *targ = NULL;
|
||||
const INT32 lastTargID = player->lastjawztarget;
|
||||
mobj_t *lastTarg = NULL;
|
||||
|
||||
INT32 targID = MAXPLAYERS;
|
||||
mobj_t *targ = NULL;
|
||||
|
||||
mobj_t *ret = NULL;
|
||||
|
||||
if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS)
|
||||
if (specialStage.active == true && lastTargID == MAXPLAYERS)
|
||||
{
|
||||
// Aiming at the UFO.
|
||||
lastTarg = specialStage.ufo;
|
||||
}
|
||||
else if ((lastTargID >= 0 && lastTargID <= MAXPLAYERS)
|
||||
&& playeringame[lastTargID] == true)
|
||||
{
|
||||
if (players[lastTargID].spectator == false)
|
||||
{
|
||||
lastTarg = &players[lastTargID];
|
||||
lastTarg = players[lastTargID].mo;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->throwdir == -1)
|
||||
{
|
||||
// Backwards Jawz targets yourself.
|
||||
targ = player;
|
||||
targ = player->mo;
|
||||
player->jawztargetdelay = 0;
|
||||
}
|
||||
else
|
||||
|
|
@ -7921,9 +8058,14 @@ void K_KartPlayerAfterThink(player_t *player)
|
|||
targ = K_FindJawzTarget(player->mo, player, ANGLE_45);
|
||||
}
|
||||
|
||||
if (targ != NULL && targ->mo != NULL && P_MobjWasRemoved(targ->mo) == false)
|
||||
if (targ != NULL && P_MobjWasRemoved(targ) == false)
|
||||
{
|
||||
if (targ - players == lastTargID)
|
||||
if (targ->player != NULL)
|
||||
{
|
||||
targID = targ->player - players;
|
||||
}
|
||||
|
||||
if (targID == lastTargID)
|
||||
{
|
||||
// Increment delay.
|
||||
if (player->jawztargetdelay < 10)
|
||||
|
|
@ -7942,33 +8084,33 @@ void K_KartPlayerAfterThink(player_t *player)
|
|||
else
|
||||
{
|
||||
// Allow a swap.
|
||||
if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ))
|
||||
if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ->player))
|
||||
{
|
||||
S_StartSound(NULL, sfx_s3k89);
|
||||
}
|
||||
else
|
||||
{
|
||||
S_StartSound(targ->mo, sfx_s3k89);
|
||||
S_StartSound(targ, sfx_s3k89);
|
||||
}
|
||||
|
||||
player->lastjawztarget = targ - players;
|
||||
player->lastjawztarget = targID;
|
||||
player->jawztargetdelay = 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targ == NULL || targ->mo == NULL || P_MobjWasRemoved(targ->mo) == true)
|
||||
if (targ == NULL || P_MobjWasRemoved(targ) == true)
|
||||
{
|
||||
player->lastjawztarget = -1;
|
||||
player->jawztargetdelay = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE);
|
||||
ret->old_x = targ->mo->old_x;
|
||||
ret->old_y = targ->mo->old_y;
|
||||
ret->old_z = targ->mo->old_z;
|
||||
P_SetTarget(&ret->target, targ->mo);
|
||||
ret = P_SpawnMobj(targ->x, targ->y, targ->z, MT_PLAYERRETICULE);
|
||||
ret->old_x = targ->old_x;
|
||||
ret->old_y = targ->old_y;
|
||||
ret->old_z = targ->old_z;
|
||||
P_SetTarget(&ret->target, targ);
|
||||
ret->frame |= ((leveltime % 10) / 2);
|
||||
ret->tics = 1;
|
||||
ret->color = player->skincolor;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ UINT8 K_ItemResultToAmount(SINT8 getitem);
|
|||
tic_t K_GetItemCooldown(SINT8 itemResult);
|
||||
void K_SetItemCooldown(SINT8 itemResult, tic_t time);
|
||||
void K_RunItemCooldowns(void);
|
||||
|
||||
boolean K_TimeAttackRules(void);
|
||||
|
||||
fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against);
|
||||
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2);
|
||||
boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj);
|
||||
|
|
@ -118,7 +121,7 @@ void K_UpdateHnextList(player_t *player, boolean clean);
|
|||
void K_DropHnextList(player_t *player, boolean keepshields);
|
||||
void K_RepairOrbitChain(mobj_t *orbit);
|
||||
void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player);
|
||||
player_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range);
|
||||
mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range);
|
||||
INT32 K_GetKartRingPower(player_t *player, boolean boosted);
|
||||
void K_UpdateDistanceFromFinishLine(player_t *const player);
|
||||
boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y);
|
||||
|
|
|
|||
|
|
@ -58,4 +58,15 @@ void Obj_DuelBombInit(mobj_t *bomb);
|
|||
mobj_t *Obj_SpawnBrolyKi(mobj_t *source, tic_t duration);
|
||||
void Obj_BrolyKiThink(mobj_t *ki);
|
||||
|
||||
/* Special Stage UFO */
|
||||
waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo);
|
||||
void Obj_SpecialUFOThinker(mobj_t *ufo);
|
||||
boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType);
|
||||
void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other);
|
||||
void Obj_UFOPieceThink(mobj_t *piece);
|
||||
void Obj_UFOPieceDead(mobj_t *piece);
|
||||
void Obj_UFOPieceRemoved(mobj_t *piece);
|
||||
mobj_t *Obj_CreateSpecialUFO(void);
|
||||
UINT32 K_GetSpecialUFODistance(void);
|
||||
|
||||
#endif/*k_objects_H*/
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@
|
|||
/// \file k_profiles.c
|
||||
/// \brief implements methods for profiles etc.
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "d_main.h" // pandf
|
||||
#include "byteptr.h" // READ/WRITE macros
|
||||
#include "p_saveg.h" // save_p
|
||||
#include "p_saveg.h" // savebuffer_t
|
||||
#include "m_misc.h" //FIL_WriteFile()
|
||||
#include "k_profiles.h"
|
||||
#include "z_zone.h"
|
||||
|
|
@ -211,68 +212,68 @@ void PR_InitNewProfile(void)
|
|||
PR_AddProfile(dprofile);
|
||||
}
|
||||
|
||||
static UINT8 *savebuffer;
|
||||
|
||||
void PR_SaveProfiles(void)
|
||||
{
|
||||
size_t length = 0;
|
||||
const size_t headerlen = strlen(PROFILEHEADER);
|
||||
UINT8 i, j, k;
|
||||
savebuffer_t save;
|
||||
|
||||
save_p = savebuffer = (UINT8 *)malloc(sizeof(UINT32) + (numprofiles * sizeof(profile_t)));
|
||||
if (!save_p)
|
||||
save.size = sizeof(UINT32) + (numprofiles * sizeof(profile_t));
|
||||
save.p = save.buffer = (UINT8 *)malloc(save.size);
|
||||
if (!save.p)
|
||||
{
|
||||
I_Error("No more free memory for saving profiles\n");
|
||||
return;
|
||||
}
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
// Add header.
|
||||
WRITESTRINGN(save_p, PROFILEHEADER, headerlen);
|
||||
WRITEUINT8(save_p, PROFILEVER);
|
||||
WRITEUINT8(save_p, numprofiles);
|
||||
WRITESTRINGN(save.p, PROFILEHEADER, headerlen);
|
||||
WRITEUINT8(save.p, PROFILEVER);
|
||||
WRITEUINT8(save.p, numprofiles);
|
||||
|
||||
for (i = 1; i < numprofiles; i++)
|
||||
{
|
||||
// Names.
|
||||
WRITESTRINGN(save_p, profilesList[i]->profilename, PROFILENAMELEN);
|
||||
WRITESTRINGN(save_p, profilesList[i]->playername, MAXPLAYERNAME);
|
||||
WRITESTRINGN(save.p, profilesList[i]->profilename, PROFILENAMELEN);
|
||||
WRITESTRINGN(save.p, profilesList[i]->playername, MAXPLAYERNAME);
|
||||
|
||||
// Character and colour.
|
||||
WRITESTRINGN(save_p, profilesList[i]->skinname, SKINNAMESIZE);
|
||||
WRITEUINT16(save_p, profilesList[i]->color);
|
||||
WRITESTRINGN(save.p, profilesList[i]->skinname, SKINNAMESIZE);
|
||||
WRITEUINT16(save.p, profilesList[i]->color);
|
||||
|
||||
// Follower and colour.
|
||||
WRITESTRINGN(save_p, profilesList[i]->follower, SKINNAMESIZE);
|
||||
WRITEUINT16(save_p, profilesList[i]->followercolor);
|
||||
WRITESTRINGN(save.p, profilesList[i]->follower, SKINNAMESIZE);
|
||||
WRITEUINT16(save.p, profilesList[i]->followercolor);
|
||||
|
||||
// PWR.
|
||||
for (j = 0; j < PWRLV_NUMTYPES; j++)
|
||||
{
|
||||
WRITEUINT16(save_p, profilesList[i]->powerlevels[j]);
|
||||
WRITEUINT16(save.p, profilesList[i]->powerlevels[j]);
|
||||
}
|
||||
|
||||
// Consvars.
|
||||
WRITEUINT8(save_p, profilesList[i]->kickstartaccel);
|
||||
WRITEUINT8(save.p, profilesList[i]->kickstartaccel);
|
||||
|
||||
// Controls.
|
||||
for (j = 0; j < num_gamecontrols; j++)
|
||||
{
|
||||
for (k = 0; k < MAXINPUTMAPPING; k++)
|
||||
{
|
||||
WRITEINT32(save_p, profilesList[i]->controls[j][k]);
|
||||
WRITEINT32(save.p, profilesList[i]->controls[j][k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
length = save_p - savebuffer;
|
||||
length = save.p - save.buffer;
|
||||
|
||||
if (!FIL_WriteFile(va(pandf, srb2home, PROFILESFILE), savebuffer, length))
|
||||
if (!FIL_WriteFile(va(pandf, srb2home, PROFILESFILE), save.buffer, length))
|
||||
{
|
||||
free(savebuffer);
|
||||
free(save.buffer);
|
||||
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?");
|
||||
}
|
||||
free(savebuffer);
|
||||
save_p = savebuffer = NULL;
|
||||
free(save.buffer);
|
||||
}
|
||||
|
||||
void PR_LoadProfiles(void)
|
||||
|
|
@ -288,8 +289,9 @@ void PR_LoadProfiles(void)
|
|||
gamecontroldefault,
|
||||
true
|
||||
);
|
||||
savebuffer_t save;
|
||||
|
||||
length = FIL_ReadFile(va(pandf, srb2home, PROFILESFILE), &savebuffer);
|
||||
length = FIL_ReadFile(va(pandf, srb2home, PROFILESFILE), &save.buffer);
|
||||
if (!length)
|
||||
{
|
||||
// No profiles. Add the default one.
|
||||
|
|
@ -297,29 +299,29 @@ void PR_LoadProfiles(void)
|
|||
return;
|
||||
}
|
||||
|
||||
save_p = savebuffer;
|
||||
save.p = save.buffer;
|
||||
|
||||
if (strncmp(PROFILEHEADER, (const char *)savebuffer, headerlen))
|
||||
if (strncmp(PROFILEHEADER, (const char *)save.buffer, headerlen))
|
||||
{
|
||||
const char *gdfolder = "the Ring Racers folder";
|
||||
if (strcmp(srb2home,"."))
|
||||
gdfolder = srb2home;
|
||||
|
||||
Z_Free(savebuffer);
|
||||
save_p = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = NULL;
|
||||
I_Error("Not a valid Profile file.\nDelete %s (maybe in %s) and try again.", PROFILESFILE, gdfolder);
|
||||
}
|
||||
save_p += headerlen;
|
||||
save.p += headerlen;
|
||||
|
||||
version = READUINT8(save_p);
|
||||
version = READUINT8(save.p);
|
||||
if (version > PROFILEVER)
|
||||
{
|
||||
Z_Free(savebuffer);
|
||||
save_p = NULL;
|
||||
Z_Free(save.buffer);
|
||||
save.p = NULL;
|
||||
I_Error("Existing %s is from the future! (expected %d, got %d)", PROFILESFILE, PROFILEVER, version);
|
||||
}
|
||||
|
||||
numprofiles = READUINT8(save_p);
|
||||
numprofiles = READUINT8(save.p);
|
||||
if (numprofiles > MAXPROFILES)
|
||||
numprofiles = MAXPROFILES;
|
||||
|
||||
|
|
@ -331,12 +333,12 @@ void PR_LoadProfiles(void)
|
|||
profilesList[i]->version = PROFILEVER;
|
||||
|
||||
// Names.
|
||||
READSTRINGN(save_p, profilesList[i]->profilename, PROFILENAMELEN);
|
||||
READSTRINGN(save_p, profilesList[i]->playername, MAXPLAYERNAME);
|
||||
READSTRINGN(save.p, profilesList[i]->profilename, PROFILENAMELEN);
|
||||
READSTRINGN(save.p, profilesList[i]->playername, MAXPLAYERNAME);
|
||||
|
||||
// Character and colour.
|
||||
READSTRINGN(save_p, profilesList[i]->skinname, SKINNAMESIZE);
|
||||
profilesList[i]->color = READUINT16(save_p);
|
||||
READSTRINGN(save.p, profilesList[i]->skinname, SKINNAMESIZE);
|
||||
profilesList[i]->color = READUINT16(save.p);
|
||||
|
||||
if (profilesList[i]->color == SKINCOLOR_NONE)
|
||||
{
|
||||
|
|
@ -349,8 +351,8 @@ void PR_LoadProfiles(void)
|
|||
}
|
||||
|
||||
// Follower and colour.
|
||||
READSTRINGN(save_p, profilesList[i]->follower, SKINNAMESIZE);
|
||||
profilesList[i]->followercolor = READUINT16(save_p);
|
||||
READSTRINGN(save.p, profilesList[i]->follower, SKINNAMESIZE);
|
||||
profilesList[i]->followercolor = READUINT16(save.p);
|
||||
|
||||
if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH
|
||||
|| profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE)
|
||||
|
|
@ -367,7 +369,7 @@ void PR_LoadProfiles(void)
|
|||
// PWR.
|
||||
for (j = 0; j < PWRLV_NUMTYPES; j++)
|
||||
{
|
||||
profilesList[i]->powerlevels[j] = READUINT16(save_p);
|
||||
profilesList[i]->powerlevels[j] = READUINT16(save.p);
|
||||
if (profilesList[i]->powerlevels[j] < PWRLVRECORD_MIN
|
||||
|| profilesList[i]->powerlevels[j] > PWRLVRECORD_MAX)
|
||||
{
|
||||
|
|
@ -377,7 +379,7 @@ void PR_LoadProfiles(void)
|
|||
}
|
||||
|
||||
// Consvars.
|
||||
profilesList[i]->kickstartaccel = (boolean)READUINT8(save_p);
|
||||
profilesList[i]->kickstartaccel = (boolean)READUINT8(save.p);
|
||||
|
||||
// Controls.
|
||||
for (j = 0; j < num_gamecontrols; j++)
|
||||
|
|
@ -396,7 +398,7 @@ void PR_LoadProfiles(void)
|
|||
|
||||
for (k = 0; k < MAXINPUTMAPPING; k++)
|
||||
{
|
||||
profilesList[i]->controls[j][k] = READINT32(save_p);
|
||||
profilesList[i]->controls[j][k] = READINT32(save.p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
150
src/k_roulette.c
150
src/k_roulette.c
|
|
@ -110,7 +110,6 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
|
|||
|
||||
static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] =
|
||||
{
|
||||
//K L
|
||||
{ 2, 1 }, // Sneaker
|
||||
{ 0, 0 }, // Rocket Sneaker
|
||||
{ 4, 1 }, // Invincibility
|
||||
|
|
@ -143,6 +142,40 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] =
|
|||
{ 0, 0 } // Gachabom x3
|
||||
};
|
||||
|
||||
static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] =
|
||||
{
|
||||
{ 1, 1, 0, 0 }, // Sneaker
|
||||
{ 0, 0, 0, 0 }, // Rocket Sneaker
|
||||
{ 0, 0, 0, 0 }, // Invincibility
|
||||
{ 0, 0, 0, 0 }, // Banana
|
||||
{ 0, 0, 0, 0 }, // Eggman Monitor
|
||||
{ 1, 1, 0, 0 }, // Orbinaut
|
||||
{ 1, 1, 0, 0 }, // Jawz
|
||||
{ 0, 0, 0, 0 }, // Mine
|
||||
{ 0, 0, 0, 0 }, // Land Mine
|
||||
{ 0, 0, 0, 0 }, // Ballhog
|
||||
{ 0, 0, 0, 1 }, // Self-Propelled Bomb
|
||||
{ 0, 0, 0, 0 }, // Grow
|
||||
{ 0, 0, 0, 0 }, // Shrink
|
||||
{ 0, 0, 0, 0 }, // Lightning Shield
|
||||
{ 0, 0, 0, 0 }, // Bubble Shield
|
||||
{ 0, 0, 0, 0 }, // Flame Shield
|
||||
{ 0, 0, 0, 0 }, // Hyudoro
|
||||
{ 0, 0, 0, 0 }, // Pogo Spring
|
||||
{ 0, 0, 0, 0 }, // Super Ring
|
||||
{ 0, 0, 0, 0 }, // Kitchen Sink
|
||||
{ 0, 0, 0, 0 }, // Drop Target
|
||||
{ 0, 0, 0, 0 }, // Garden Top
|
||||
{ 0, 0, 0, 0 }, // Gachabom
|
||||
{ 0, 1, 1, 0 }, // Sneaker x2
|
||||
{ 0, 0, 1, 1 }, // Sneaker x3
|
||||
{ 0, 0, 0, 0 }, // Banana x3
|
||||
{ 0, 1, 1, 0 }, // Orbinaut x3
|
||||
{ 0, 0, 1, 1 }, // Orbinaut x4
|
||||
{ 0, 0, 1, 1 }, // Jawz x2
|
||||
{ 0, 0, 0, 0 } // Gachabom x3
|
||||
};
|
||||
|
||||
static kartitems_t K_KartItemReelTimeAttack[] =
|
||||
{
|
||||
KITEM_SNEAKER,
|
||||
|
|
@ -326,7 +359,6 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (specialStage.active == true)
|
||||
{
|
||||
UINT32 ufoDis = K_GetSpecialUFODistance();
|
||||
|
|
@ -343,7 +375,6 @@ static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers
|
|||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
|
|
@ -475,6 +506,11 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
|
|||
I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table
|
||||
newOdds = K_KartItemOddsBattle[item-1][pos];
|
||||
}
|
||||
else if (specialStage.active == true)
|
||||
{
|
||||
I_Assert(pos < 4); // Ditto
|
||||
newOdds = K_KartItemOddsSpecial[item-1][pos];
|
||||
}
|
||||
else
|
||||
{
|
||||
I_Assert(pos < 8); // Ditto
|
||||
|
|
@ -537,29 +573,32 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done
|
||||
|| position == 1) // No SPB for 1st ever
|
||||
if (specialStage.active == false)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const UINT32 dist = max(0, ((signed)roulette->secondToFirst) - SPBSTARTDIST);
|
||||
const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST;
|
||||
const fixed_t maxOdds = 20 << FRACBITS;
|
||||
fixed_t multiplier = FixedDiv(dist, distRange);
|
||||
|
||||
if (multiplier < 0)
|
||||
if (roulette->firstDist < ENDDIST*2 // No SPB when 1st is almost done
|
||||
|| position == 1) // No SPB for 1st ever
|
||||
{
|
||||
multiplier = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (multiplier > FRACUNIT)
|
||||
else
|
||||
{
|
||||
multiplier = FRACUNIT;
|
||||
}
|
||||
const UINT32 dist = max(0, ((signed)roulette->secondToFirst) - SPBSTARTDIST);
|
||||
const UINT32 distRange = SPBFORCEDIST - SPBSTARTDIST;
|
||||
const fixed_t maxOdds = 20 << FRACBITS;
|
||||
fixed_t multiplier = FixedDiv(dist, distRange);
|
||||
|
||||
newOdds = FixedMul(maxOdds, multiplier);
|
||||
if (multiplier < 0)
|
||||
{
|
||||
multiplier = 0;
|
||||
}
|
||||
|
||||
if (multiplier > FRACUNIT)
|
||||
{
|
||||
multiplier = FRACUNIT;
|
||||
}
|
||||
|
||||
newOdds = FixedMul(maxOdds, multiplier);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -666,6 +705,11 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
|
|||
oddsValid[i] = false;
|
||||
continue;
|
||||
}
|
||||
else if (specialStage.active == true && i > 3)
|
||||
{
|
||||
oddsValid[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 1; j < NUMKARTRESULTS; j++)
|
||||
{
|
||||
|
|
@ -690,14 +734,24 @@ static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulett
|
|||
}
|
||||
else
|
||||
{
|
||||
SETUPDISTTABLE(0,1);
|
||||
SETUPDISTTABLE(1,1);
|
||||
SETUPDISTTABLE(2,1);
|
||||
SETUPDISTTABLE(3,2);
|
||||
SETUPDISTTABLE(4,2);
|
||||
SETUPDISTTABLE(5,3);
|
||||
SETUPDISTTABLE(6,3);
|
||||
SETUPDISTTABLE(7,1);
|
||||
if (specialStage.active == true) // Special Stages
|
||||
{
|
||||
SETUPDISTTABLE(0,2);
|
||||
SETUPDISTTABLE(1,2);
|
||||
SETUPDISTTABLE(2,3);
|
||||
SETUPDISTTABLE(3,1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SETUPDISTTABLE(0,1);
|
||||
SETUPDISTTABLE(1,1);
|
||||
SETUPDISTTABLE(2,1);
|
||||
SETUPDISTTABLE(3,2);
|
||||
SETUPDISTTABLE(4,2);
|
||||
SETUPDISTTABLE(5,3);
|
||||
SETUPDISTTABLE(6,3);
|
||||
SETUPDISTTABLE(7,1);
|
||||
}
|
||||
|
||||
for (i = 0; i < totalSize; i++)
|
||||
{
|
||||
|
|
@ -754,6 +808,11 @@ static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulett
|
|||
return false;
|
||||
}
|
||||
|
||||
if (specialStage.active == true)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player == NULL)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -845,19 +904,36 @@ static void K_InitRoulette(itemroulette_t *const roulette)
|
|||
roulette->exiting++;
|
||||
}
|
||||
|
||||
if (players[i].position == 1)
|
||||
if (specialStage.active == true)
|
||||
{
|
||||
roulette->firstDist = K_UndoMapScaling(players[i].distancetofinish);
|
||||
UINT32 dis = K_UndoMapScaling(players[i].distancetofinish);
|
||||
if (dis < roulette->secondDist)
|
||||
{
|
||||
roulette->secondDist = dis;
|
||||
}
|
||||
}
|
||||
|
||||
if (players[i].position == 2)
|
||||
else
|
||||
{
|
||||
roulette->secondDist = K_UndoMapScaling(players[i].distancetofinish);
|
||||
if (players[i].position == 1)
|
||||
{
|
||||
roulette->firstDist = K_UndoMapScaling(players[i].distancetofinish);
|
||||
}
|
||||
|
||||
if (players[i].position == 2)
|
||||
{
|
||||
roulette->secondDist = K_UndoMapScaling(players[i].distancetofinish);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (specialStage.active == true)
|
||||
{
|
||||
roulette->firstDist = K_UndoMapScaling(K_GetSpecialUFODistance());
|
||||
}
|
||||
|
||||
// Calculate 2nd's distance from 1st, for SPB
|
||||
if (roulette->firstDist != UINT32_MAX && roulette->secondDist != UINT32_MAX)
|
||||
if (roulette->firstDist != UINT32_MAX && roulette->secondDist != UINT32_MAX
|
||||
&& roulette->secondDist > roulette->firstDist)
|
||||
{
|
||||
roulette->secondToFirst = roulette->secondDist - roulette->firstDist;
|
||||
roulette->secondToFirst = K_ScaleItemDistance(roulette->secondToFirst, 16 - roulette->playing); // Reversed scaling
|
||||
|
|
@ -964,7 +1040,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
|
|||
fixed_t progress = 0;
|
||||
fixed_t total = 0;
|
||||
|
||||
if (modeattacking || roulette->playing <= 1)
|
||||
if (K_TimeAttackRules() == true)
|
||||
{
|
||||
// Time Attack rules; use a consistent speed.
|
||||
roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK;
|
||||
|
|
@ -1047,7 +1123,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
|
|||
|
||||
return;
|
||||
}
|
||||
else if (modeattacking || roulette->playing <= 1)
|
||||
else if (K_TimeAttackRules() == true)
|
||||
{
|
||||
switch (gametype)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "st_stuff.h"
|
||||
#include "z_zone.h"
|
||||
#include "k_waypoint.h"
|
||||
#include "k_objects.h"
|
||||
|
||||
struct specialStage specialStage;
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ void K_InitSpecialStage(void)
|
|||
INT32 i;
|
||||
|
||||
specialStage.beamDist = UINT32_MAX; // TODO: make proper value
|
||||
P_SetTarget(&specialStage.ufo, Obj_CreateSpecialUFO());
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ extern struct specialStage
|
|||
boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars
|
||||
|
||||
UINT32 beamDist; ///< Where the exit beam is.
|
||||
mobj_t *capsule; ///< The Chaos Emerald capsule.
|
||||
mobj_t *ufo; ///< The Chaos Emerald capsule.
|
||||
} specialStage;
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1070,6 +1070,45 @@ static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData)
|
|||
return isEnd;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_WaypointPathfindNextValid(void *data, void *setupData)
|
||||
|
||||
Returns if the current waypoint data has a next waypoint.
|
||||
|
||||
Input Arguments:-
|
||||
data - Should point to a pathfindnode_t to compare
|
||||
setupData - Should point to the pathfindsetup_t to compare
|
||||
|
||||
Return:-
|
||||
True if the waypoint has a next waypoint, false otherwise.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_WaypointPathfindNextValid(void *data, void *setupData)
|
||||
{
|
||||
boolean nextValid = false;
|
||||
|
||||
if (data == NULL || setupData == NULL)
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindNextValid received NULL data.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||
waypoint_t *wp = (waypoint_t *)node->nodedata;
|
||||
|
||||
if (setup->getconnectednodes == K_WaypointPathfindGetPrev)
|
||||
{
|
||||
nextValid = (wp->numprevwaypoints > 0U);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextValid = (wp->numnextwaypoints > 0U);
|
||||
}
|
||||
}
|
||||
|
||||
return nextValid;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
|
||||
|
||||
|
|
@ -1094,8 +1133,9 @@ static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData)
|
|||
{
|
||||
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||
boolean nextValid = K_WaypointPathfindNextValid(data, setupData);
|
||||
|
||||
scoreReached = (node->gscore >= setup->endgscore);
|
||||
scoreReached = (node->gscore >= setup->endgscore) || (nextValid == false);
|
||||
}
|
||||
|
||||
return scoreReached;
|
||||
|
|
@ -1127,8 +1167,9 @@ static boolean K_WaypointPathfindReachedGScoreSpawnable(void *data, void *setupD
|
|||
pathfindnode_t *node = (pathfindnode_t *)data;
|
||||
pathfindsetup_t *setup = (pathfindsetup_t *)setupData;
|
||||
waypoint_t *wp = (waypoint_t *)node->nodedata;
|
||||
boolean nextValid = K_WaypointPathfindNextValid(data, setupData);
|
||||
|
||||
scoreReached = (node->gscore >= setup->endgscore);
|
||||
scoreReached = (node->gscore >= setup->endgscore) || (nextValid == false);
|
||||
spawnable = K_GetWaypointIsSpawnpoint(wp);
|
||||
}
|
||||
|
||||
|
|
@ -1251,13 +1292,6 @@ boolean K_PathfindThruCircuit(
|
|||
"K_PathfindThruCircuit: sourcewaypoint with ID %d has no next waypoint\n",
|
||||
K_GetWaypointID(sourcewaypoint));
|
||||
}
|
||||
else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0))
|
||||
|| ((huntbackwards == true) && (finishline->numnextwaypoints == 0)))
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC,
|
||||
"K_PathfindThruCircuit: finishline with ID %d has no previous waypoint\n",
|
||||
K_GetWaypointID(finishline));
|
||||
}
|
||||
else
|
||||
{
|
||||
pathfindsetup_t pathfindsetup = {0};
|
||||
|
|
@ -1334,13 +1368,6 @@ boolean K_PathfindThruCircuitSpawnable(
|
|||
"K_PathfindThruCircuitSpawnable: sourcewaypoint with ID %d has no next waypoint\n",
|
||||
K_GetWaypointID(sourcewaypoint));
|
||||
}
|
||||
else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0))
|
||||
|| ((huntbackwards == true) && (finishline->numnextwaypoints == 0)))
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC,
|
||||
"K_PathfindThruCircuitSpawnable: finishline with ID %d has no previous waypoint\n",
|
||||
K_GetWaypointID(finishline));
|
||||
}
|
||||
else
|
||||
{
|
||||
pathfindsetup_t pathfindsetup = {0};
|
||||
|
|
|
|||
|
|
@ -834,7 +834,7 @@ int LUA_HookHurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 d
|
|||
return hook.status;
|
||||
}
|
||||
|
||||
void LUA_HookNetArchive(lua_CFunction archFunc)
|
||||
void LUA_HookNetArchive(lua_CFunction archFunc, savebuffer_t *save)
|
||||
{
|
||||
const hook_t * map = &hookIds[HOOK(NetVars)];
|
||||
Hook_State hook;
|
||||
|
|
@ -852,8 +852,9 @@ void LUA_HookNetArchive(lua_CFunction archFunc)
|
|||
|
||||
// tables becomes an upvalue of archFunc
|
||||
lua_pushvalue(gL, -1);
|
||||
lua_pushcclosure(gL, archFunc, 1);
|
||||
// stack: tables, archFunc
|
||||
lua_pushlightuserdata(gL, save);
|
||||
lua_pushcclosure(gL, archFunc, 2);
|
||||
// stack: tables, savebuffer_t, archFunc
|
||||
|
||||
init_hook_call(&hook, 0, res_none);
|
||||
call_mapped(&hook, map);
|
||||
|
|
|
|||
|
|
@ -1248,10 +1248,10 @@ static UINT8 ArchiveValue(UINT8 **p, int TABLESINDEX, int myindex)
|
|||
{
|
||||
polyobj_t *polyobj = *((polyobj_t **)lua_touserdata(gL, myindex));
|
||||
if (!polyobj)
|
||||
WRITEUINT8(save_p, ARCH_NULL);
|
||||
WRITEUINT8(*p, ARCH_NULL);
|
||||
else {
|
||||
WRITEUINT8(save_p, ARCH_POLYOBJ);
|
||||
WRITEUINT16(save_p, polyobj-PolyObjects);
|
||||
WRITEUINT8(*p, ARCH_POLYOBJ);
|
||||
WRITEUINT16(*p, polyobj-PolyObjects);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1353,9 +1353,10 @@ static void ArchiveExtVars(UINT8 **p, void *pointer, const char *ptype)
|
|||
static int NetArchive(lua_State *L)
|
||||
{
|
||||
int TABLESINDEX = lua_upvalueindex(1);
|
||||
savebuffer_t *save = lua_touserdata(L, lua_upvalueindex(2));
|
||||
int i, n = lua_gettop(L);
|
||||
for (i = 1; i <= n; i++)
|
||||
ArchiveValue(&save_p, TABLESINDEX, i);
|
||||
ArchiveValue(&save->p, TABLESINDEX, i);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
@ -1517,7 +1518,7 @@ static UINT8 UnArchiveValue(UINT8 **p, int TABLESINDEX)
|
|||
break;
|
||||
}
|
||||
case ARCH_POLYOBJ:
|
||||
LUA_PushUserdata(gL, &PolyObjects[READUINT16(save_p)], META_POLYOBJ);
|
||||
LUA_PushUserdata(gL, &PolyObjects[READUINT16(*p)], META_POLYOBJ);
|
||||
break;
|
||||
case ARCH_SLOPE:
|
||||
LUA_PushUserdata(gL, P_SlopeById(READUINT16(*p)), META_SLOPE);
|
||||
|
|
@ -1566,9 +1567,10 @@ static void UnArchiveExtVars(UINT8 **p, void *pointer)
|
|||
static int NetUnArchive(lua_State *L)
|
||||
{
|
||||
int TABLESINDEX = lua_upvalueindex(1);
|
||||
savebuffer_t *save = lua_touserdata(L, lua_upvalueindex(2));
|
||||
int i, n = lua_gettop(L);
|
||||
for (i = 1; i <= n; i++)
|
||||
UnArchiveValue(&save_p, TABLESINDEX);
|
||||
UnArchiveValue(&save->p, TABLESINDEX);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
@ -1602,7 +1604,7 @@ static void UnArchiveTables(UINT8 **p)
|
|||
lua_rawset(gL, -3);
|
||||
}
|
||||
|
||||
metatableid = READUINT16(save_p);
|
||||
metatableid = READUINT16(*p);
|
||||
if (metatableid)
|
||||
{
|
||||
// setmetatable(table, registry.metatables[metatableid])
|
||||
|
|
@ -1626,7 +1628,7 @@ void LUA_Step(void)
|
|||
lua_gc(gL, LUA_GCSTEP, 1);
|
||||
}
|
||||
|
||||
void LUA_Archive(UINT8 **p)
|
||||
void LUA_Archive(savebuffer_t *save, boolean network)
|
||||
{
|
||||
INT32 i;
|
||||
thinker_t *th;
|
||||
|
|
@ -1639,10 +1641,10 @@ void LUA_Archive(UINT8 **p)
|
|||
if (!playeringame[i] && i > 0) // NEVER skip player 0, this is for dedi servs.
|
||||
continue;
|
||||
// all players in game will be archived, even if they just add a 0.
|
||||
ArchiveExtVars(p, &players[i], "player");
|
||||
ArchiveExtVars(&save->p, &players[i], "player");
|
||||
}
|
||||
|
||||
if (p == &save_p)
|
||||
if (network == true)
|
||||
{
|
||||
if (gamestate == GS_LEVEL)
|
||||
{
|
||||
|
|
@ -1653,22 +1655,22 @@ void LUA_Archive(UINT8 **p)
|
|||
|
||||
// archive function will determine when to skip mobjs,
|
||||
// and write mobjnum in otherwise.
|
||||
ArchiveExtVars(p, th, "mobj");
|
||||
ArchiveExtVars(&save->p, th, "mobj");
|
||||
}
|
||||
}
|
||||
|
||||
WRITEUINT32(*p, UINT32_MAX); // end of mobjs marker, replaces mobjnum.
|
||||
WRITEUINT32(save->p, UINT32_MAX); // end of mobjs marker, replaces mobjnum.
|
||||
|
||||
LUA_HookNetArchive(NetArchive); // call the NetArchive hook in archive mode
|
||||
LUA_HookNetArchive(NetArchive, save); // call the NetArchive hook in archive mode
|
||||
}
|
||||
|
||||
ArchiveTables(p);
|
||||
ArchiveTables(&save->p);
|
||||
|
||||
if (gL)
|
||||
lua_pop(gL, 1); // pop tables
|
||||
}
|
||||
|
||||
void LUA_UnArchive(UINT8 **p)
|
||||
void LUA_UnArchive(savebuffer_t *save, boolean network)
|
||||
{
|
||||
UINT32 mobjnum;
|
||||
INT32 i;
|
||||
|
|
@ -1681,27 +1683,27 @@ void LUA_UnArchive(UINT8 **p)
|
|||
{
|
||||
if (!playeringame[i] && i > 0) // same here, this is to synch dediservs properly.
|
||||
continue;
|
||||
UnArchiveExtVars(p, &players[i]);
|
||||
UnArchiveExtVars(&save->p, &players[i]);
|
||||
}
|
||||
|
||||
if (p == &save_p)
|
||||
if (network == true)
|
||||
{
|
||||
do {
|
||||
mobjnum = READUINT32(*p); // read a mobjnum
|
||||
mobjnum = READUINT32(save->p); // read a mobjnum
|
||||
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
|
||||
{
|
||||
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
|
||||
continue;
|
||||
if (((mobj_t *)th)->mobjnum != mobjnum) // find matching mobj
|
||||
continue;
|
||||
UnArchiveExtVars(p, th); // apply variables
|
||||
UnArchiveExtVars(&save->p, th); // apply variables
|
||||
}
|
||||
} while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker.
|
||||
|
||||
LUA_HookNetArchive(NetUnArchive); // call the NetArchive hook in unarchive mode
|
||||
LUA_HookNetArchive(NetUnArchive, save); // call the NetArchive hook in unarchive mode
|
||||
}
|
||||
|
||||
UnArchiveTables(p);
|
||||
UnArchiveTables(&save->p);
|
||||
|
||||
if (gL)
|
||||
lua_pop(gL, 1); // pop tables
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ void LUA_DumpFile(const char *filename);
|
|||
#endif
|
||||
fixed_t LUA_EvalMath(const char *word);
|
||||
void LUA_Step(void);
|
||||
void LUA_Archive(UINT8 **p);
|
||||
void LUA_UnArchive(UINT8 **p);
|
||||
void LUA_Archive(savebuffer_t *save, boolean network);
|
||||
void LUA_UnArchive(savebuffer_t *save, boolean network);
|
||||
|
||||
int LUA_PushGlobals(lua_State *L, const char *word);
|
||||
int LUA_WriteGlobals(lua_State *L, const char *word);
|
||||
|
|
@ -63,7 +63,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c
|
|||
void LUA_CVarChanged(void *cvar); // lua_consolelib.c
|
||||
int Lua_optoption(lua_State *L, int narg,
|
||||
const char *def, const char *const lst[]);
|
||||
void LUA_HookNetArchive(lua_CFunction archFunc);
|
||||
void LUA_HookNetArchive(lua_CFunction archFunc, savebuffer_t *save);
|
||||
|
||||
void LUA_PushTaggableObjectArray
|
||||
( lua_State *L,
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
jawz.c
|
||||
duel-bomb.c
|
||||
broly.c
|
||||
ufo.c
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "../k_waypoint.h"
|
||||
#include "../k_respawn.h"
|
||||
#include "../k_collide.h"
|
||||
#include "../k_specialstage.h"
|
||||
|
||||
#define MAX_JAWZ_TURN (ANGLE_90 / 15) // We can turn a maximum of 6 degrees per frame at regular max speed
|
||||
|
||||
|
|
@ -140,17 +141,21 @@ static void JawzChase(mobj_t *th, boolean grounded)
|
|||
|
||||
if (jawz_chase(th) == NULL || P_MobjWasRemoved(jawz_chase(th)) == true)
|
||||
{
|
||||
mobj_t *newChase = NULL;
|
||||
player_t *owner = NULL;
|
||||
|
||||
th->angle = K_MomentumAngle(th);
|
||||
|
||||
if (jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false
|
||||
&& jawz_owner(th)->player != NULL)
|
||||
if ((jawz_owner(th) != NULL && P_MobjWasRemoved(jawz_owner(th)) == false)
|
||||
&& (jawz_owner(th)->player != NULL))
|
||||
{
|
||||
player_t *newPlayer = K_FindJawzTarget(th, jawz_owner(th)->player, ANGLE_90);
|
||||
owner = jawz_owner(th)->player;
|
||||
}
|
||||
|
||||
if (newPlayer != NULL)
|
||||
{
|
||||
P_SetTarget(&jawz_chase(th), newPlayer->mo);
|
||||
}
|
||||
newChase = K_FindJawzTarget(th, owner, ANGLE_90);
|
||||
if (newChase != NULL)
|
||||
{
|
||||
P_SetTarget(&jawz_chase(th), newChase);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +186,11 @@ static void JawzChase(mobj_t *th, boolean grounded)
|
|||
}
|
||||
}
|
||||
|
||||
static boolean JawzSteersBetter(void)
|
||||
{
|
||||
return !!!(gametyperules & GTR_CIRCUIT);
|
||||
}
|
||||
|
||||
void Obj_JawzThink(mobj_t *th)
|
||||
{
|
||||
mobj_t *ghost = P_SpawnGhostMobj(th);
|
||||
|
|
@ -211,7 +221,7 @@ void Obj_JawzThink(mobj_t *th)
|
|||
ghost->colorized = true;
|
||||
}
|
||||
|
||||
if (!(gametyperules & GTR_CIRCUIT))
|
||||
if (JawzSteersBetter() == true)
|
||||
{
|
||||
th->friction = max(0, 3 * th->friction / 4);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "../z_zone.h"
|
||||
#include "../k_waypoint.h"
|
||||
#include "../k_respawn.h"
|
||||
#include "../k_specialstage.h"
|
||||
|
||||
#define SPB_SLIPTIDEDELTA (ANG1 * 3)
|
||||
#define SPB_STEERDELTA (ANGLE_90 - ANG10)
|
||||
|
|
@ -292,7 +293,7 @@ static boolean SPBSeekSoundPlaying(mobj_t *spb)
|
|||
|| S_SoundPlaying(spb, sfx_spbskc));
|
||||
}
|
||||
|
||||
static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
||||
static void SPBSeek(mobj_t *spb, mobj_t *bestMobj)
|
||||
{
|
||||
const fixed_t desiredSpeed = SPB_DEFAULTSPEED;
|
||||
|
||||
|
|
@ -321,16 +322,15 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
|||
|
||||
spb_lastplayer(spb) = -1; // Just make sure this is reset
|
||||
|
||||
if (bestPlayer == NULL
|
||||
|| bestPlayer->mo == NULL
|
||||
|| P_MobjWasRemoved(bestPlayer->mo) == true
|
||||
|| bestPlayer->mo->health <= 0
|
||||
|| (bestPlayer->respawn.state != RESPAWNST_NONE))
|
||||
if (bestMobj == NULL
|
||||
|| P_MobjWasRemoved(bestMobj) == true
|
||||
|| bestMobj->health <= 0
|
||||
|| (bestMobj->player != NULL && bestMobj->player->respawn.state != RESPAWNST_NONE))
|
||||
{
|
||||
// No one there? Completely STOP.
|
||||
spb->momx = spb->momy = spb->momz = 0;
|
||||
|
||||
if (bestPlayer == NULL)
|
||||
if (bestMobj == NULL)
|
||||
{
|
||||
spbplace = -1;
|
||||
}
|
||||
|
|
@ -339,8 +339,16 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
|||
}
|
||||
|
||||
// Found someone, now get close enough to initiate the slaughter...
|
||||
P_SetTarget(&spb_chase(spb), bestPlayer->mo);
|
||||
spbplace = bestPlayer->position;
|
||||
P_SetTarget(&spb_chase(spb), bestMobj);
|
||||
|
||||
if (bestMobj->player != NULL)
|
||||
{
|
||||
spbplace = bestMobj->player->position;
|
||||
}
|
||||
else
|
||||
{
|
||||
spbplace = 1;
|
||||
}
|
||||
|
||||
dist = SPBDist(spb, spb_chase(spb));
|
||||
activeDist = FixedMul(SPB_ACTIVEDIST, spb_chase(spb)->scale);
|
||||
|
|
@ -400,7 +408,18 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
|||
curWaypoint = K_GetWaypointFromIndex( (size_t)spb_curwaypoint(spb) );
|
||||
}
|
||||
|
||||
destWaypoint = bestPlayer->nextwaypoint;
|
||||
if (bestMobj->player != NULL)
|
||||
{
|
||||
destWaypoint = bestMobj->player->nextwaypoint;
|
||||
}
|
||||
else if (bestMobj->type == MT_SPECIAL_UFO)
|
||||
{
|
||||
destWaypoint = K_GetSpecialUFOWaypoint(bestMobj);
|
||||
}
|
||||
else
|
||||
{
|
||||
destWaypoint = K_GetBestWaypointForMobj(bestMobj);
|
||||
}
|
||||
|
||||
if (curWaypoint != NULL)
|
||||
{
|
||||
|
|
@ -433,7 +452,8 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
|||
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
if (cv_spbtest.value) {
|
||||
if (cv_spbtest.value)
|
||||
{
|
||||
if (pathtoplayer.numnodes > 1)
|
||||
{
|
||||
// Go to the next waypoint.
|
||||
|
|
@ -529,45 +549,48 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
|||
|
||||
SetSPBSpeed(spb, xySpeed, zSpeed);
|
||||
|
||||
// see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh!
|
||||
steerDist = 1536 * mapobjectscale;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (specialStage.active == false)
|
||||
{
|
||||
fixed_t ourDist = INT32_MAX;
|
||||
INT32 ourDelta = INT32_MAX;
|
||||
// see if a player is near us, if they are, try to hit them by slightly thrusting towards them, otherwise, bleh!
|
||||
steerDist = 1536 * mapobjectscale;
|
||||
|
||||
if (playeringame[i] == false || players[i].spectator == true)
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
// Not in-game
|
||||
continue;
|
||||
fixed_t ourDist = INT32_MAX;
|
||||
INT32 ourDelta = INT32_MAX;
|
||||
|
||||
if (playeringame[i] == false || players[i].spectator == true)
|
||||
{
|
||||
// Not in-game
|
||||
continue;
|
||||
}
|
||||
|
||||
if (players[i].mo == NULL || P_MobjWasRemoved(players[i].mo) == true)
|
||||
{
|
||||
// Invalid mobj
|
||||
continue;
|
||||
}
|
||||
|
||||
ourDelta = AngleDelta(spb->angle, R_PointToAngle2(spb->x, spb->y, players[i].mo->x, players[i].mo->y));
|
||||
if (ourDelta > SPB_STEERDELTA)
|
||||
{
|
||||
// Check if the angle wouldn't make us LOSE speed.
|
||||
continue;
|
||||
}
|
||||
|
||||
ourDist = R_PointToDist2(spb->x, spb->y, players[i].mo->x, players[i].mo->y);
|
||||
if (ourDist < steerDist)
|
||||
{
|
||||
steerDist = ourDist;
|
||||
steerMobj = players[i].mo; // it doesn't matter if we override this guy now.
|
||||
}
|
||||
}
|
||||
|
||||
if (players[i].mo == NULL || P_MobjWasRemoved(players[i].mo) == true)
|
||||
// different player from our main target, try and ram into em~!
|
||||
if (steerMobj != NULL && steerMobj != spb_chase(spb))
|
||||
{
|
||||
// Invalid mobj
|
||||
continue;
|
||||
P_Thrust(spb, R_PointToAngle2(spb->x, spb->y, steerMobj->x, steerMobj->y), spb_speed(spb) / 4);
|
||||
}
|
||||
|
||||
ourDelta = AngleDelta(spb->angle, R_PointToAngle2(spb->x, spb->y, players[i].mo->x, players[i].mo->y));
|
||||
if (ourDelta > SPB_STEERDELTA)
|
||||
{
|
||||
// Check if the angle wouldn't make us LOSE speed.
|
||||
continue;
|
||||
}
|
||||
|
||||
ourDist = R_PointToDist2(spb->x, spb->y, players[i].mo->x, players[i].mo->y);
|
||||
if (ourDist < steerDist)
|
||||
{
|
||||
steerDist = ourDist;
|
||||
steerMobj = players[i].mo; // it doesn't matter if we override this guy now.
|
||||
}
|
||||
}
|
||||
|
||||
// different player from our main target, try and ram into em~!
|
||||
if (steerMobj != NULL && steerMobj != spb_chase(spb))
|
||||
{
|
||||
P_Thrust(spb, R_PointToAngle2(spb->x, spb->y, steerMobj->x, steerMobj->y), spb_speed(spb) / 4);
|
||||
}
|
||||
|
||||
if (sliptide != 0)
|
||||
|
|
@ -593,7 +616,7 @@ static void SPBSeek(mobj_t *spb, player_t *bestPlayer)
|
|||
}
|
||||
}
|
||||
|
||||
static void SPBChase(mobj_t *spb, player_t *bestPlayer)
|
||||
static void SPBChase(mobj_t *spb, mobj_t *bestMobj)
|
||||
{
|
||||
fixed_t baseSpeed = 0;
|
||||
fixed_t maxSpeed = 0;
|
||||
|
|
@ -642,8 +665,8 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
|
|||
S_StartSound(spb, spb->info->activesound);
|
||||
}
|
||||
|
||||
// Maybe we want SPB to target an object later? IDK lol
|
||||
chasePlayer = chase->player;
|
||||
|
||||
if (chasePlayer != NULL)
|
||||
{
|
||||
UINT8 fracmax = 32;
|
||||
|
|
@ -679,7 +702,8 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
|
|||
cy = chasePlayer->cmomy;
|
||||
|
||||
// Switch targets if you're no longer 1st for long enough
|
||||
if (bestPlayer != NULL && chasePlayer->position <= bestPlayer->position)
|
||||
if (bestMobj != NULL
|
||||
&& (bestMobj->player == NULL || chasePlayer->position <= bestMobj->player->position))
|
||||
{
|
||||
spb_modetimer(spb) = SPB_HOTPOTATO;
|
||||
}
|
||||
|
|
@ -697,6 +721,12 @@ static void SPBChase(mobj_t *spb, player_t *bestPlayer)
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
spb_lastplayer(spb) = -1;
|
||||
spbplace = 1;
|
||||
spb_modetimer(spb) = SPB_HOTPOTATO;
|
||||
}
|
||||
|
||||
dist = P_AproxDistance(P_AproxDistance(spb->x - chase->x, spb->y - chase->y), spb->z - chase->z);
|
||||
|
||||
|
|
@ -807,7 +837,7 @@ static void SPBWait(mobj_t *spb)
|
|||
void Obj_SPBThink(mobj_t *spb)
|
||||
{
|
||||
mobj_t *ghost = NULL;
|
||||
player_t *bestPlayer = NULL;
|
||||
mobj_t *bestMobj = NULL;
|
||||
UINT8 bestRank = UINT8_MAX;
|
||||
size_t i;
|
||||
|
||||
|
|
@ -844,6 +874,15 @@ void Obj_SPBThink(mobj_t *spb)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (specialStage.active == true)
|
||||
{
|
||||
if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false)
|
||||
{
|
||||
bestRank = 1;
|
||||
bestMobj = specialStage.ufo;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the player with the best rank
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
|
|
@ -886,7 +925,7 @@ void Obj_SPBThink(mobj_t *spb)
|
|||
if (player->position < bestRank)
|
||||
{
|
||||
bestRank = player->position;
|
||||
bestPlayer = player;
|
||||
bestMobj = player->mo;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -894,11 +933,11 @@ void Obj_SPBThink(mobj_t *spb)
|
|||
{
|
||||
case SPB_MODE_SEEK:
|
||||
default:
|
||||
SPBSeek(spb, bestPlayer);
|
||||
SPBSeek(spb, bestMobj);
|
||||
break;
|
||||
|
||||
case SPB_MODE_CHASE:
|
||||
SPBChase(spb, bestPlayer);
|
||||
SPBChase(spb, bestMobj);
|
||||
break;
|
||||
|
||||
case SPB_MODE_WAIT:
|
||||
|
|
@ -970,14 +1009,18 @@ void Obj_SPBExplode(mobj_t *spb)
|
|||
|
||||
void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher)
|
||||
{
|
||||
player_t *player = toucher->player;
|
||||
player_t *const player = toucher->player;
|
||||
mobj_t *owner = NULL;
|
||||
mobj_t *chase = NULL;
|
||||
|
||||
if (spb_intangible(spb) > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((spb_owner(spb) == toucher || spb_owner(spb) == toucher->target)
|
||||
owner = spb_owner(spb);
|
||||
|
||||
if ((owner == toucher || owner == toucher->target)
|
||||
&& (spb_nothink(spb) > 0))
|
||||
{
|
||||
return;
|
||||
|
|
@ -988,30 +1031,34 @@ void Obj_SPBTouch(mobj_t *spb, mobj_t *toucher)
|
|||
return;
|
||||
}
|
||||
|
||||
if (player->spectator == true)
|
||||
if (player != NULL)
|
||||
{
|
||||
return;
|
||||
if (player->spectator == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->bubbleblowup > 0)
|
||||
{
|
||||
// Stun the SPB, and remove the shield.
|
||||
K_DropHnextList(player, false);
|
||||
spb_mode(spb) = SPB_MODE_WAIT;
|
||||
spb_modetimer(spb) = 55; // Slightly over the respawn timer length
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->bubbleblowup > 0)
|
||||
{
|
||||
// Stun the SPB, and remove the shield.
|
||||
K_DropHnextList(player, false);
|
||||
spb_mode(spb) = SPB_MODE_WAIT;
|
||||
spb_modetimer(spb) = 55; // Slightly over the respawn timer length
|
||||
return;
|
||||
}
|
||||
|
||||
if (spb_chase(spb) != NULL && P_MobjWasRemoved(spb_chase(spb)) == false
|
||||
&& toucher == spb_chase(spb))
|
||||
chase = spb_chase(spb);
|
||||
if (chase != NULL && P_MobjWasRemoved(chase) == false
|
||||
&& toucher == chase)
|
||||
{
|
||||
// Cause the explosion.
|
||||
Obj_SPBExplode(spb);
|
||||
return;
|
||||
}
|
||||
else
|
||||
else if (toucher->flags & MF_SHOOTABLE)
|
||||
{
|
||||
// Regular spinout, please.
|
||||
P_DamageMobj(toucher, spb, spb_owner(spb), 1, DMG_NORMAL);
|
||||
P_DamageMobj(toucher, spb, owner, 1, DMG_NORMAL);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
832
src/objects/ufo.c
Normal file
832
src/objects/ufo.c
Normal file
|
|
@ -0,0 +1,832 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour
|
||||
// Copyright (C) 2022 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file ufo.c
|
||||
/// \brief Special Stage UFO + Emerald handler
|
||||
|
||||
#include "../doomdef.h"
|
||||
#include "../doomstat.h"
|
||||
#include "../info.h"
|
||||
#include "../k_kart.h"
|
||||
#include "../k_objects.h"
|
||||
#include "../m_random.h"
|
||||
#include "../p_local.h"
|
||||
#include "../r_main.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../g_game.h"
|
||||
#include "../z_zone.h"
|
||||
#include "../k_waypoint.h"
|
||||
#include "../k_specialstage.h"
|
||||
|
||||
#define UFO_BASE_SPEED (42 * FRACUNIT) // UFO's slowest speed.
|
||||
#define UFO_SPEEDUP (FRACUNIT >> 1) // Acceleration
|
||||
#define UFO_SLOWDOWN (FRACUNIT >> 1) // Deceleration
|
||||
#define UFO_SPACING (768 * FRACUNIT) // How far the UFO wants to stay in front
|
||||
#define UFO_DEADZONE (2048 * FRACUNIT) // Deadzone where it won't update it's speed as much.
|
||||
#define UFO_SPEEDFACTOR (FRACUNIT * 3 / 4) // Factor of player's best speed, to make it more fair.
|
||||
#define UFO_DAMAGED_SPEED (UFO_BASE_SPEED >> 1) // Speed to add when UFO takes damage.
|
||||
#define UFO_START_SPEED (UFO_BASE_SPEED << 1) // Speed when the map starts.
|
||||
|
||||
#define UFO_NUMARMS (3)
|
||||
#define UFO_ARMDELTA (ANGLE_MAX / UFO_NUMARMS)
|
||||
|
||||
#define ufo_waypoint(o) ((o)->extravalue1)
|
||||
#define ufo_distancetofinish(o) ((o)->extravalue2)
|
||||
#define ufo_speed(o) ((o)->watertop)
|
||||
#define ufo_collectdelay(o) ((o)->threshold)
|
||||
|
||||
#define ufo_pieces(o) ((o)->hnext)
|
||||
|
||||
#define ufo_piece_type(o) ((o)->extravalue1)
|
||||
|
||||
#define ufo_piece_owner(o) ((o)->target)
|
||||
#define ufo_piece_next(o) ((o)->hnext)
|
||||
#define ufo_piece_prev(o) ((o)->hprev)
|
||||
|
||||
enum
|
||||
{
|
||||
UFO_PIECE_TYPE_POD,
|
||||
UFO_PIECE_TYPE_ARM,
|
||||
UFO_PIECE_TYPE_STEM,
|
||||
};
|
||||
|
||||
static void UFOMoveTo(mobj_t *ufo, fixed_t destx, fixed_t desty, fixed_t destz)
|
||||
{
|
||||
ufo->momx = destx - ufo->x;
|
||||
ufo->momy = desty - ufo->y;
|
||||
ufo->momz = destz - ufo->z;
|
||||
}
|
||||
|
||||
static fixed_t GenericDistance(
|
||||
fixed_t curx, fixed_t cury, fixed_t curz,
|
||||
fixed_t destx, fixed_t desty, fixed_t destz)
|
||||
{
|
||||
return P_AproxDistance(P_AproxDistance(destx - curx, desty - cury), destz - curz);
|
||||
}
|
||||
|
||||
static boolean UFOEmeraldChase(mobj_t *ufo)
|
||||
{
|
||||
return (ufo->health <= 1);
|
||||
}
|
||||
|
||||
static boolean UFOPieceValid(mobj_t *piece)
|
||||
{
|
||||
return (piece != NULL && P_MobjWasRemoved(piece) == false && piece->health > 0);
|
||||
}
|
||||
|
||||
static void UFOUpdateDistanceToFinish(mobj_t *ufo)
|
||||
{
|
||||
waypoint_t *finishLine = K_GetFinishLineWaypoint();
|
||||
waypoint_t *nextWaypoint = K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo));
|
||||
|
||||
if (nextWaypoint != NULL && finishLine != NULL)
|
||||
{
|
||||
const boolean useshortcuts = false;
|
||||
const boolean huntbackwards = false;
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {0};
|
||||
|
||||
pathfindsuccess =
|
||||
K_PathfindToWaypoint(nextWaypoint, finishLine, &pathtofinish, useshortcuts, huntbackwards);
|
||||
|
||||
// Update the UFO's distance to the finish line if a path was found.
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
// Add euclidean distance to the next waypoint to the distancetofinish
|
||||
UINT32 adddist;
|
||||
fixed_t disttowaypoint =
|
||||
P_AproxDistance(
|
||||
(ufo->x >> FRACBITS) - (nextWaypoint->mobj->x >> FRACBITS),
|
||||
(ufo->y >> FRACBITS) - (nextWaypoint->mobj->y >> FRACBITS));
|
||||
disttowaypoint = P_AproxDistance(disttowaypoint, (ufo->z >> FRACBITS) - (nextWaypoint->mobj->z >> FRACBITS));
|
||||
|
||||
adddist = (UINT32)disttowaypoint;
|
||||
|
||||
ufo_distancetofinish(ufo) = pathtofinish.totaldist + adddist;
|
||||
Z_Free(pathtofinish.array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UFOUpdateSpeed(mobj_t *ufo)
|
||||
{
|
||||
const fixed_t baseSpeed = FixedMul(UFO_BASE_SPEED, K_GetKartGameSpeedScalar(gamespeed));
|
||||
const UINT32 spacing = FixedMul(FixedMul(UFO_SPACING, mapobjectscale), K_GetKartGameSpeedScalar(gamespeed)) >> FRACBITS;
|
||||
const UINT32 deadzone = FixedMul(FixedMul(UFO_DEADZONE, mapobjectscale), K_GetKartGameSpeedScalar(gamespeed)) >> FRACBITS;
|
||||
|
||||
// Best values of all of the players.
|
||||
UINT32 bestDist = UINT32_MAX;
|
||||
fixed_t bestSpeed = 0;
|
||||
|
||||
// Desired values for the UFO itself.
|
||||
UINT32 wantedDist = UINT32_MAX;
|
||||
fixed_t wantedSpeed = ufo_speed(ufo);
|
||||
fixed_t speedDelta = 0;
|
||||
|
||||
UINT8 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *player = NULL;
|
||||
|
||||
if (playeringame[i] == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player = &players[i];
|
||||
if (player->spectator == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->distancetofinish < bestDist)
|
||||
{
|
||||
bestDist = player->distancetofinish;
|
||||
|
||||
// Doesn't matter if a splitscreen player behind is moving faster behind the one most caught up.
|
||||
bestSpeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy);
|
||||
bestSpeed = min(bestSpeed, K_GetKartSpeed(player, false, false)); // Don't become unfair with Sneakers.
|
||||
bestSpeed = FixedDiv(bestSpeed, mapobjectscale); // Unscale from mapobjectscale to FRACUNIT
|
||||
bestSpeed = FixedMul(bestSpeed, UFO_SPEEDFACTOR); // Make it a bit more lenient
|
||||
}
|
||||
}
|
||||
|
||||
if (bestDist == UINT32_MAX)
|
||||
{
|
||||
// Invalid, lets go back to base speed.
|
||||
wantedSpeed = baseSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
INT32 distDelta = 0;
|
||||
|
||||
if (bestDist > spacing)
|
||||
{
|
||||
wantedDist = bestDist - spacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
wantedDist = 0;
|
||||
}
|
||||
|
||||
distDelta = ufo_distancetofinish(ufo) - wantedDist;
|
||||
|
||||
if (distDelta > 0)
|
||||
{
|
||||
// Too far behind! Start speeding up!
|
||||
wantedSpeed = max(bestSpeed, baseSpeed << 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (abs(distDelta) <= deadzone)
|
||||
{
|
||||
// We're in a good spot, try to match the player.
|
||||
wantedSpeed = max(bestSpeed >> 1, baseSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too far ahead! Start slowing down!
|
||||
wantedSpeed = baseSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slowly accelerate or decelerate to
|
||||
// get to our desired speed.
|
||||
speedDelta = wantedSpeed - ufo_speed(ufo);
|
||||
if (speedDelta > 0)
|
||||
{
|
||||
if (abs(speedDelta) <= UFO_SPEEDUP)
|
||||
{
|
||||
ufo_speed(ufo) = wantedSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
ufo_speed(ufo) += UFO_SPEEDUP;
|
||||
}
|
||||
}
|
||||
else if (speedDelta < 0)
|
||||
{
|
||||
if (abs(speedDelta) <= UFO_SLOWDOWN)
|
||||
{
|
||||
ufo_speed(ufo) = wantedSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
ufo_speed(ufo) -= UFO_SLOWDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UFOUpdateAngle(mobj_t *ufo)
|
||||
{
|
||||
angle_t dest = K_MomentumAngle(ufo);
|
||||
INT32 delta = AngleDeltaSigned(ufo->angle, dest);
|
||||
ufo->angle += delta >> 2;
|
||||
}
|
||||
|
||||
waypoint_t *K_GetSpecialUFOWaypoint(mobj_t *ufo)
|
||||
{
|
||||
if ((ufo == NULL) && (specialStage.active == true))
|
||||
{
|
||||
ufo = specialStage.ufo;
|
||||
}
|
||||
|
||||
if (ufo != NULL && P_MobjWasRemoved(ufo) == false
|
||||
&& ufo->type == MT_SPECIAL_UFO)
|
||||
{
|
||||
if (ufo_waypoint(ufo) >= 0)
|
||||
{
|
||||
return K_GetWaypointFromIndex((size_t)ufo_waypoint(ufo));
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void UFOMove(mobj_t *ufo)
|
||||
{
|
||||
waypoint_t *curWaypoint = NULL;
|
||||
waypoint_t *destWaypoint = NULL;
|
||||
|
||||
fixed_t distLeft = INT32_MAX;
|
||||
fixed_t newX = ufo->x;
|
||||
fixed_t newY = ufo->y;
|
||||
fixed_t newZ = ufo->z;
|
||||
const fixed_t floatHeight = 24 * ufo->scale;
|
||||
|
||||
const boolean useshortcuts = false;
|
||||
const boolean huntbackwards = false;
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {0};
|
||||
size_t pathIndex = 0;
|
||||
|
||||
boolean reachedEnd = false;
|
||||
|
||||
curWaypoint = K_GetSpecialUFOWaypoint(ufo);
|
||||
destWaypoint = K_GetFinishLineWaypoint();
|
||||
|
||||
if (curWaypoint == NULL || destWaypoint == NULL)
|
||||
{
|
||||
// Waypoints aren't valid.
|
||||
// Just stand still.
|
||||
ufo->momx = 0;
|
||||
ufo->momy = 0;
|
||||
ufo->momz = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
distLeft = FixedMul(ufo_speed(ufo), mapobjectscale);
|
||||
|
||||
while (distLeft > 0)
|
||||
{
|
||||
fixed_t wpX = curWaypoint->mobj->x;
|
||||
fixed_t wpY = curWaypoint->mobj->y;
|
||||
fixed_t wpZ = curWaypoint->mobj->z + floatHeight;
|
||||
|
||||
fixed_t distToNext = GenericDistance(
|
||||
newX, newY, newZ,
|
||||
wpX, wpY, wpZ
|
||||
);
|
||||
|
||||
if (distToNext > distLeft)
|
||||
{
|
||||
// Only made it partially there.
|
||||
newX += FixedMul(FixedDiv(wpX - newX, distToNext), distLeft);
|
||||
newY += FixedMul(FixedDiv(wpY - newY, distToNext), distLeft);
|
||||
newZ += FixedMul(FixedDiv(wpZ - newZ, distToNext), distLeft);
|
||||
|
||||
distLeft = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close enough to the next waypoint,
|
||||
// move there and remove the distance.
|
||||
newX = wpX;
|
||||
newY = wpY;
|
||||
newZ = wpZ;
|
||||
|
||||
distLeft -= distToNext;
|
||||
|
||||
if (curWaypoint == destWaypoint)
|
||||
{
|
||||
// Reached the end.
|
||||
reachedEnd = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create waypoint path to our destination.
|
||||
// Crazy over-engineered, just to catch when
|
||||
// waypoints are insanely close to each other :P
|
||||
if (pathfindsuccess == false)
|
||||
{
|
||||
pathfindsuccess = K_PathfindToWaypoint(
|
||||
curWaypoint, destWaypoint,
|
||||
&pathtofinish,
|
||||
useshortcuts, huntbackwards
|
||||
);
|
||||
|
||||
if (pathfindsuccess == false)
|
||||
{
|
||||
// Path isn't valid.
|
||||
// Just keep going.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pathIndex++;
|
||||
|
||||
if (pathIndex >= pathtofinish.numnodes)
|
||||
{
|
||||
// Successfully reached the end of the path.
|
||||
reachedEnd = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Now moving to the next waypoint.
|
||||
curWaypoint = (waypoint_t *)pathtofinish.array[pathIndex].nodedata;
|
||||
ufo_waypoint(ufo) = (INT32)K_GetWaypointHeapIndex(curWaypoint);
|
||||
}
|
||||
}
|
||||
|
||||
UFOMoveTo(ufo, newX, newY, newZ);
|
||||
|
||||
if (reachedEnd == true)
|
||||
{
|
||||
CONS_Printf("You lost...\n");
|
||||
ufo_waypoint(ufo) = -1; // Invalidate
|
||||
}
|
||||
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
Z_Free(pathtofinish.array);
|
||||
}
|
||||
}
|
||||
|
||||
static void UFOEmeraldVFX(mobj_t *ufo)
|
||||
{
|
||||
const INT32 bobS = 32;
|
||||
const angle_t bobA = (leveltime & (bobS - 1)) * (ANGLE_MAX / bobS);
|
||||
const fixed_t bobH = 16 * ufo->scale;
|
||||
|
||||
ufo->sprzoff = FixedMul(bobH, FINESINE(bobA >> ANGLETOFINESHIFT));
|
||||
|
||||
if (leveltime % 3 == 0)
|
||||
{
|
||||
mobj_t *sparkle = P_SpawnMobjFromMobj(
|
||||
ufo,
|
||||
P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT,
|
||||
P_RandomRange(PR_SPARKLE, -48, 48) * FRACUNIT,
|
||||
(P_RandomRange(PR_SPARKLE, 0, 64) * FRACUNIT) + FixedDiv(ufo->sprzoff, ufo->scale),
|
||||
MT_EMERALDSPARK
|
||||
);
|
||||
|
||||
sparkle->color = ufo->color;
|
||||
sparkle->momz += 8 * ufo->scale * P_MobjFlip(ufo);
|
||||
}
|
||||
}
|
||||
|
||||
void Obj_SpecialUFOThinker(mobj_t *ufo)
|
||||
{
|
||||
UFOMove(ufo);
|
||||
UFOUpdateAngle(ufo);
|
||||
UFOUpdateDistanceToFinish(ufo);
|
||||
UFOUpdateSpeed(ufo);
|
||||
|
||||
if (UFOEmeraldChase(ufo) == true)
|
||||
{
|
||||
// Spawn emerald sparkles
|
||||
UFOEmeraldVFX(ufo);
|
||||
ufo_collectdelay(ufo)--;
|
||||
}
|
||||
else
|
||||
{
|
||||
ufo_collectdelay(ufo) = TICRATE;
|
||||
}
|
||||
}
|
||||
|
||||
static void UFOCopyHitlagToPieces(mobj_t *ufo)
|
||||
{
|
||||
mobj_t *piece = NULL;
|
||||
|
||||
piece = ufo_pieces(ufo);
|
||||
while (UFOPieceValid(piece) == true)
|
||||
{
|
||||
piece->hitlag = ufo->hitlag;
|
||||
piece->eflags = (piece->eflags & ~MFE_DAMAGEHITLAG) | (ufo->eflags & MFE_DAMAGEHITLAG);
|
||||
piece = ufo_piece_next(piece);
|
||||
}
|
||||
}
|
||||
|
||||
static void UFOKillPiece(mobj_t *piece)
|
||||
{
|
||||
angle_t dir = ANGLE_MAX;
|
||||
fixed_t thrust = 0;
|
||||
|
||||
if (UFOPieceValid(piece) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
piece->health = 0;
|
||||
piece->tics = TICRATE;
|
||||
piece->flags &= ~MF_NOGRAVITY;
|
||||
|
||||
switch (ufo_piece_type(piece))
|
||||
{
|
||||
case UFO_PIECE_TYPE_STEM:
|
||||
{
|
||||
piece->tics = 1;
|
||||
return;
|
||||
}
|
||||
case UFO_PIECE_TYPE_ARM:
|
||||
{
|
||||
dir = piece->angle;
|
||||
thrust = 12 * piece->scale;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
dir = FixedAngle(P_RandomRange(PR_DECORATION, 0, 359) << FRACBITS);
|
||||
thrust = 4 * piece->scale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
P_Thrust(piece, dir, -thrust);
|
||||
P_SetObjectMomZ(piece, 12*FRACUNIT, true);
|
||||
}
|
||||
|
||||
static void UFOKillPieces(mobj_t *ufo)
|
||||
{
|
||||
mobj_t *piece = NULL;
|
||||
|
||||
piece = ufo_pieces(ufo);
|
||||
while (UFOPieceValid(piece) == true)
|
||||
{
|
||||
UFOKillPiece(piece);
|
||||
piece = ufo_piece_next(piece);
|
||||
}
|
||||
}
|
||||
|
||||
static UINT8 GetUFODamage(mobj_t *inflictor, UINT8 damageType)
|
||||
{
|
||||
if (inflictor != NULL && P_MobjWasRemoved(inflictor) == false)
|
||||
{
|
||||
switch (inflictor->type)
|
||||
{
|
||||
case MT_JAWZ_SHIELD:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
{
|
||||
// Shields deal chip damage.
|
||||
return 10;
|
||||
}
|
||||
case MT_JAWZ:
|
||||
{
|
||||
// Thrown Jawz deal a bit extra.
|
||||
return 15;
|
||||
}
|
||||
case MT_ORBINAUT:
|
||||
{
|
||||
// Thrown orbinauts deal double damage.
|
||||
return 20;
|
||||
}
|
||||
case MT_SPB:
|
||||
{
|
||||
// SPB deals triple damage.
|
||||
return 30;
|
||||
}
|
||||
case MT_BANANA:
|
||||
{
|
||||
// Banana snipes deal triple damage,
|
||||
// laid down bananas deal regular damage.
|
||||
if (inflictor->health > 1)
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
|
||||
return 10;
|
||||
}
|
||||
case MT_PLAYER:
|
||||
{
|
||||
// Players deal damage relative to how many sneakers they used.
|
||||
return 15 * max(1, inflictor->player->numsneakers);
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Guess from damage type.
|
||||
switch (damageType & DMG_TYPEMASK)
|
||||
{
|
||||
case DMG_NORMAL:
|
||||
case DMG_STING:
|
||||
default:
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
case DMG_WIPEOUT:
|
||||
{
|
||||
return 20;
|
||||
}
|
||||
case DMG_EXPLODE:
|
||||
case DMG_TUMBLE:
|
||||
{
|
||||
return 30;
|
||||
}
|
||||
case DMG_VOLTAGE:
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UINT8 damageType)
|
||||
{
|
||||
const fixed_t addSpeed = FixedMul(UFO_DAMAGED_SPEED, K_GetKartGameSpeedScalar(gamespeed));
|
||||
UINT8 damage = 1;
|
||||
|
||||
(void)source;
|
||||
|
||||
if (UFOEmeraldChase(ufo) == true)
|
||||
{
|
||||
// Damaged fully already, no need for any more.
|
||||
return false;
|
||||
}
|
||||
|
||||
damage = GetUFODamage(inflictor, damageType);
|
||||
|
||||
if (damage <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Speed up on damage!
|
||||
ufo_speed(ufo) += addSpeed;
|
||||
|
||||
K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true);
|
||||
UFOCopyHitlagToPieces(ufo);
|
||||
|
||||
if (damage >= ufo->health - 1)
|
||||
{
|
||||
// Destroy the UFO parts, and make the emerald collectible!
|
||||
UFOKillPieces(ufo);
|
||||
|
||||
ufo->health = 1;
|
||||
ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW);
|
||||
ufo->shadowscale = FRACUNIT/3;
|
||||
|
||||
ufo_speed(ufo) += addSpeed; // Even more speed!
|
||||
return true;
|
||||
}
|
||||
|
||||
ufo->health -= damage;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Obj_PlayerUFOCollide(mobj_t *ufo, mobj_t *other)
|
||||
{
|
||||
if (other->player == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((other->player->sneakertimer > 0)
|
||||
&& !P_PlayerInPain(other->player)
|
||||
&& (other->player->flashing == 0))
|
||||
{
|
||||
// Bump and deal damage.
|
||||
Obj_SpecialUFODamage(ufo, other, other, DMG_STEAL);
|
||||
K_KartBouncing(other, ufo);
|
||||
other->player->sneakertimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Obj_UFOPieceThink(mobj_t *piece)
|
||||
{
|
||||
mobj_t *ufo = ufo_piece_owner(piece);
|
||||
|
||||
if (ufo == NULL || P_MobjWasRemoved(ufo) == true)
|
||||
{
|
||||
P_KillMobj(piece, NULL, NULL, DMG_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
piece->destscale = 3 * ufo->destscale / 2;
|
||||
piece->scalespeed = ufo->scalespeed;
|
||||
|
||||
switch (ufo_piece_type(piece))
|
||||
{
|
||||
case UFO_PIECE_TYPE_POD:
|
||||
{
|
||||
UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (132 * piece->scale));
|
||||
break;
|
||||
}
|
||||
case UFO_PIECE_TYPE_ARM:
|
||||
{
|
||||
fixed_t dis = (88 * piece->scale);
|
||||
|
||||
fixed_t x = ufo->x - FixedMul(dis, FINECOSINE(piece->angle >> ANGLETOFINESHIFT));
|
||||
fixed_t y = ufo->y - FixedMul(dis, FINESINE(piece->angle >> ANGLETOFINESHIFT));
|
||||
|
||||
UFOMoveTo(piece, x, y, ufo->z + (24 * piece->scale));
|
||||
|
||||
piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED));
|
||||
break;
|
||||
}
|
||||
case UFO_PIECE_TYPE_STEM:
|
||||
{
|
||||
fixed_t stemZ = ufo->z + (294 * piece->scale);
|
||||
fixed_t sc = FixedDiv(FixedDiv(ufo->ceilingz - stemZ, piece->scale), 15 * FRACUNIT);
|
||||
|
||||
UFOMoveTo(piece, ufo->x, ufo->y, stemZ);
|
||||
piece->spriteyscale = sc;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
P_RemoveMobj(piece);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Obj_UFOPieceDead(mobj_t *piece)
|
||||
{
|
||||
piece->renderflags ^= RF_DONTDRAW;
|
||||
}
|
||||
|
||||
void Obj_UFOPieceRemoved(mobj_t *piece)
|
||||
{
|
||||
// Repair piece list.
|
||||
mobj_t *ufo = ufo_piece_owner(piece);
|
||||
mobj_t *next = ufo_piece_next(piece);
|
||||
mobj_t *prev = ufo_piece_prev(piece);
|
||||
|
||||
if (prev != NULL && P_MobjWasRemoved(prev) == false)
|
||||
{
|
||||
P_SetTarget(
|
||||
&ufo_piece_next(prev),
|
||||
(next != NULL && P_MobjWasRemoved(next) == false) ? next : NULL
|
||||
);
|
||||
}
|
||||
|
||||
if (next != NULL && P_MobjWasRemoved(next) == false)
|
||||
{
|
||||
P_SetTarget(
|
||||
&ufo_piece_prev(next),
|
||||
(prev != NULL && P_MobjWasRemoved(prev) == false) ? prev : NULL
|
||||
);
|
||||
}
|
||||
|
||||
if (ufo != NULL && P_MobjWasRemoved(ufo) == false)
|
||||
{
|
||||
if (piece == ufo_pieces(ufo))
|
||||
{
|
||||
P_SetTarget(
|
||||
&ufo_pieces(ufo),
|
||||
(next != NULL && P_MobjWasRemoved(next) == false) ? next : NULL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
P_SetTarget(&ufo_piece_next(piece), NULL);
|
||||
P_SetTarget(&ufo_piece_prev(piece), NULL);
|
||||
}
|
||||
|
||||
static mobj_t *InitSpecialUFO(waypoint_t *start)
|
||||
{
|
||||
mobj_t *ufo = NULL;
|
||||
mobj_t *overlay = NULL;
|
||||
mobj_t *piece = NULL;
|
||||
mobj_t *prevPiece = NULL;
|
||||
size_t i;
|
||||
|
||||
if (start == NULL)
|
||||
{
|
||||
// Simply create at the origin with default values.
|
||||
ufo = P_SpawnMobj(0, 0, 0, MT_SPECIAL_UFO);
|
||||
ufo_waypoint(ufo) = -1; // Invalidate
|
||||
ufo_distancetofinish(ufo) = INT32_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create with a proper waypoint track!
|
||||
ufo = P_SpawnMobj(start->mobj->x, start->mobj->y, start->mobj->z, MT_SPECIAL_UFO);
|
||||
ufo_waypoint(ufo) = (INT32)K_GetWaypointHeapIndex(start);
|
||||
UFOUpdateDistanceToFinish(ufo);
|
||||
}
|
||||
|
||||
ufo_speed(ufo) = FixedMul(UFO_START_SPEED, K_GetKartGameSpeedScalar(gamespeed));
|
||||
|
||||
// TODO: Adjustable Special Stage emerald color
|
||||
ufo->color = SKINCOLOR_CHAOSEMERALD1;
|
||||
|
||||
overlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY);
|
||||
P_SetTarget(&overlay->target, ufo);
|
||||
overlay->color = ufo->color;
|
||||
|
||||
// TODO: Super Emeralds / Chaos Rings
|
||||
P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER);
|
||||
|
||||
// Create UFO pieces.
|
||||
// First: UFO center.
|
||||
piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE);
|
||||
P_SetTarget(&ufo_piece_owner(piece), ufo);
|
||||
|
||||
P_SetMobjState(piece, S_SPECIAL_UFO_POD);
|
||||
ufo_piece_type(piece) = UFO_PIECE_TYPE_POD;
|
||||
|
||||
overlay = P_SpawnMobjFromMobj(piece, 0, 0, 0, MT_OVERLAY);
|
||||
P_SetTarget(&overlay->target, piece);
|
||||
P_SetMobjState(overlay, S_SPECIAL_UFO_OVERLAY);
|
||||
|
||||
P_SetTarget(&ufo_pieces(ufo), piece);
|
||||
prevPiece = piece;
|
||||
|
||||
// Add the catcher arms.
|
||||
for (i = 0; i < UFO_NUMARMS; i++)
|
||||
{
|
||||
piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE);
|
||||
P_SetTarget(&ufo_piece_owner(piece), ufo);
|
||||
|
||||
P_SetMobjState(piece, S_SPECIAL_UFO_ARM);
|
||||
ufo_piece_type(piece) = UFO_PIECE_TYPE_ARM;
|
||||
|
||||
piece->angle = UFO_ARMDELTA * i;
|
||||
|
||||
P_SetTarget(&ufo_piece_next(prevPiece), piece);
|
||||
P_SetTarget(&ufo_piece_prev(piece), prevPiece);
|
||||
prevPiece = piece;
|
||||
}
|
||||
|
||||
// Add the stem.
|
||||
piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE);
|
||||
P_SetTarget(&ufo_piece_owner(piece), ufo);
|
||||
|
||||
P_SetMobjState(piece, S_SPECIAL_UFO_STEM);
|
||||
ufo_piece_type(piece) = UFO_PIECE_TYPE_STEM;
|
||||
|
||||
P_SetTarget(&ufo_piece_next(prevPiece), piece);
|
||||
P_SetTarget(&ufo_piece_prev(piece), prevPiece);
|
||||
prevPiece = piece;
|
||||
|
||||
return ufo;
|
||||
}
|
||||
|
||||
mobj_t *Obj_CreateSpecialUFO(void)
|
||||
{
|
||||
waypoint_t *finishWaypoint = K_GetFinishLineWaypoint();
|
||||
waypoint_t *startWaypoint = NULL;
|
||||
|
||||
if (finishWaypoint != NULL)
|
||||
{
|
||||
const boolean huntbackwards = true;
|
||||
const boolean useshortcuts = false;
|
||||
const UINT32 traveldist = INT32_MAX; // Go as far back as possible. Not UINT32_MAX to avoid possible overflow.
|
||||
boolean pathfindsuccess = false;
|
||||
path_t pathtofinish = {0};
|
||||
|
||||
pathfindsuccess = K_PathfindThruCircuit(
|
||||
finishWaypoint, traveldist,
|
||||
&pathtofinish,
|
||||
useshortcuts, huntbackwards
|
||||
);
|
||||
|
||||
if (pathfindsuccess == true)
|
||||
{
|
||||
startWaypoint = (waypoint_t *)pathtofinish.array[ pathtofinish.numnodes - 1 ].nodedata;
|
||||
Z_Free(pathtofinish.array);
|
||||
}
|
||||
}
|
||||
|
||||
return InitSpecialUFO(startWaypoint);
|
||||
}
|
||||
|
||||
UINT32 K_GetSpecialUFODistance(void)
|
||||
{
|
||||
if (specialStage.active == true)
|
||||
{
|
||||
if (specialStage.ufo != NULL && P_MobjWasRemoved(specialStage.ufo) == false)
|
||||
{
|
||||
return (UINT32)ufo_distancetofinish(specialStage.ufo);
|
||||
}
|
||||
}
|
||||
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
|
@ -386,11 +386,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
special->target->player->karmadelay = comebacktime;
|
||||
}
|
||||
return;
|
||||
case MT_SPB:
|
||||
{
|
||||
Obj_SPBTouch(special, toucher);
|
||||
return;
|
||||
}
|
||||
case MT_DUELBOMB:
|
||||
{
|
||||
Obj_DuelBombTouch(special, toucher);
|
||||
|
|
@ -409,6 +404,18 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
player->emeralds |= special->extravalue1;
|
||||
K_CheckEmeralds(player);
|
||||
break;
|
||||
case MT_SPECIAL_UFO:
|
||||
if (!P_CanPickupItem(player, 0))
|
||||
return;
|
||||
|
||||
if (special->threshold > 0)
|
||||
return;
|
||||
|
||||
if (toucher->hitlag > 0)
|
||||
return;
|
||||
|
||||
CONS_Printf("You win!\n");
|
||||
break;
|
||||
/*
|
||||
case MT_EERIEFOG:
|
||||
special->frame &= ~FF_TRANS80;
|
||||
|
|
@ -1402,7 +1409,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
INT16 spacing = (target->radius >> 1) / target->scale;
|
||||
|
||||
// set respawn fuse
|
||||
if (modeattacking) // no respawns
|
||||
if (K_TimeAttackRules() == true) // no respawns
|
||||
;
|
||||
else if (target->threshold == KITEM_SUPERRING)
|
||||
target->fuse = 20*TICRATE;
|
||||
|
|
@ -2289,7 +2296,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
|
||||
K_PlayPainSound(target, source);
|
||||
|
||||
if ((hardhit == true) || (cv_kartdebughuddrop.value && !modeattacking))
|
||||
if ((hardhit == true) || cv_kartdebughuddrop.value)
|
||||
{
|
||||
K_DropItems(player);
|
||||
}
|
||||
|
|
@ -2311,6 +2318,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
}
|
||||
else
|
||||
{
|
||||
if (target->type == MT_SPECIAL_UFO)
|
||||
{
|
||||
return Obj_SpecialUFODamage(target, inflictor, source, damagetype);
|
||||
}
|
||||
|
||||
if (damagetype & DMG_STEAL)
|
||||
{
|
||||
// Not a player, steal damage is intended to not do anything
|
||||
|
|
|
|||
55
src/p_map.c
55
src/p_map.c
|
|
@ -757,6 +757,53 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|
||||
// SRB2kart 011617 - Colission[sic] code for kart items //{
|
||||
|
||||
if (thing->type == MT_SPB)
|
||||
{
|
||||
if (tm.thing->type != MT_PLAYER
|
||||
&& thing->tracer != tm.thing)
|
||||
{
|
||||
// Needs to be a player or the
|
||||
// thing that we're chasing.
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
if (tm.thing->z > thing->z + thing->height)
|
||||
{
|
||||
return BMIT_CONTINUE; // overhead
|
||||
}
|
||||
|
||||
if (tm.thing->z + tm.thing->height < thing->z)
|
||||
{
|
||||
return BMIT_CONTINUE; // underneath
|
||||
}
|
||||
|
||||
Obj_SPBTouch(thing, tm.thing);
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
else if (tm.thing->type == MT_SPB)
|
||||
{
|
||||
if (thing->type != MT_PLAYER
|
||||
&& tm.thing->tracer != thing)
|
||||
{
|
||||
// Needs to be a player or the
|
||||
// thing that we're chasing.
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
if (tm.thing->z > thing->z + thing->height)
|
||||
{
|
||||
return BMIT_CONTINUE; // overhead
|
||||
}
|
||||
|
||||
if (tm.thing->z + tm.thing->height < thing->z)
|
||||
{
|
||||
return BMIT_CONTINUE; // underneath
|
||||
}
|
||||
|
||||
Obj_SPBTouch(tm.thing, thing);
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
if (thing->type == MT_SHRINK_GUN || thing->type == MT_SHRINK_PARTICLE)
|
||||
{
|
||||
if (tm.thing->type != MT_PLAYER)
|
||||
|
|
@ -1379,6 +1426,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
else if (thing->type == MT_SPECIAL_UFO)
|
||||
{
|
||||
if (!(thing->flags & MF_SPECIAL))
|
||||
{
|
||||
Obj_PlayerUFOCollide(thing, tm.thing);
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
}
|
||||
else if (thing->type == MT_BLUEROBRA_HEAD || thing->type == MT_BLUEROBRA_JOINT)
|
||||
{
|
||||
// see if it went over / under
|
||||
|
|
|
|||
48
src/p_mobj.c
48
src/p_mobj.c
|
|
@ -46,6 +46,7 @@
|
|||
#include "k_terrain.h"
|
||||
#include "k_collide.h"
|
||||
#include "k_objects.h"
|
||||
#include "k_grandprix.h"
|
||||
|
||||
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
|
||||
consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
|
||||
|
|
@ -5306,10 +5307,24 @@ void P_RunOverlays(void)
|
|||
mo->pitch = mo->target->pitch;
|
||||
mo->roll = mo->target->roll;
|
||||
|
||||
#if 0
|
||||
mo->spritexoffset = mo->target->spritexoffset;
|
||||
mo->spriteyoffset = mo->target->spriteyoffset;
|
||||
mo->spritexscale = mo->target->spritexscale;
|
||||
mo->spriteyscale = mo->target->spriteyscale;
|
||||
|
||||
mo->sprxoff = mo->target->sprxoff;
|
||||
mo->spryoff = mo->target->spryoff;
|
||||
mo->sprzoff = mo->target->sprzoff;
|
||||
#endif
|
||||
|
||||
mo->hitlag = mo->target->hitlag;
|
||||
mo->eflags = (mo->eflags & ~MFE_DAMAGEHITLAG) | (mo->target->eflags & MFE_DAMAGEHITLAG);
|
||||
|
||||
if ((mo->flags & MF_DONTENCOREMAP) != (mo->target->flags & MF_DONTENCOREMAP))
|
||||
mo->flags ^= MF_DONTENCOREMAP;
|
||||
|
||||
mo->dispoffset = mo->target->dispoffset + mo->info->dispoffset;
|
||||
mo->dispoffset = mo->target->dispoffset;
|
||||
|
||||
if (!(mo->state->frame & FF_ANIMATE))
|
||||
{
|
||||
|
|
@ -5329,6 +5344,7 @@ void P_RunOverlays(void)
|
|||
// if you're using FF_ANIMATE on an overlay,
|
||||
// then you're on your own.
|
||||
zoffs = 0;
|
||||
mo->dispoffset++;
|
||||
}
|
||||
|
||||
P_UnsetThingPosition(mo);
|
||||
|
|
@ -6755,6 +6771,11 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
|
|||
S_StartSound(dust, sfx_s3k3d);
|
||||
}
|
||||
break;
|
||||
case MT_SPECIAL_UFO_PIECE:
|
||||
{
|
||||
Obj_UFOPieceDead(mobj);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -7366,6 +7387,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
Obj_DuelBombThink(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_SPECIAL_UFO:
|
||||
{
|
||||
Obj_SpecialUFOThinker(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_SPECIAL_UFO_PIECE:
|
||||
{
|
||||
Obj_UFOPieceThink(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_EMERALD:
|
||||
{
|
||||
if (battleovertime.enabled >= 10*TICRATE)
|
||||
|
|
@ -10121,6 +10152,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
|
|||
case MT_PLAYER:
|
||||
case MT_KART_LEFTOVER:
|
||||
case MT_BATTLECAPSULE:
|
||||
case MT_SPECIAL_UFO:
|
||||
thing->shadowscale = FRACUNIT;
|
||||
break;
|
||||
case MT_SMALLMACE:
|
||||
|
|
@ -10978,6 +11010,11 @@ void P_RemoveMobj(mobj_t *mobj)
|
|||
Obj_ShrinkGunRemoved(mobj);
|
||||
}
|
||||
|
||||
if (mobj->type == MT_SPECIAL_UFO_PIECE)
|
||||
{
|
||||
Obj_UFOPieceRemoved(mobj);
|
||||
}
|
||||
|
||||
mobj->health = 0; // Just because
|
||||
|
||||
// unlink from sector and block lists
|
||||
|
|
@ -11541,13 +11578,12 @@ void P_SpawnPlayer(INT32 playernum)
|
|||
}
|
||||
else if (p->bot)
|
||||
{
|
||||
/*
|
||||
if (bonusgame || specialstage || boss)
|
||||
if (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)
|
||||
{
|
||||
// Bots should avoid
|
||||
// Bots aren't supposed to be here.
|
||||
p->spectator = true;
|
||||
}
|
||||
*/
|
||||
else
|
||||
{
|
||||
// No point in a spectating bot!
|
||||
p->spectator = false;
|
||||
|
|
@ -12018,7 +12054,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
|
|||
|
||||
// in record attack, only spawn ring capsules
|
||||
// (behavior can be inverted with the Extra flag, i.e. item capsule spawns and ring capsule does not)
|
||||
if (modeattacking
|
||||
if (K_TimeAttackRules() == true
|
||||
&& (!(mthing->args[2] & TMICF_INVERTTIMEATTACK) == !isRingCapsule))
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
3501
src/p_saveg.c
3501
src/p_saveg.c
File diff suppressed because it is too large
Load diff
|
|
@ -18,13 +18,19 @@
|
|||
#pragma interface
|
||||
#endif
|
||||
|
||||
// 1024 bytes is plenty for a savegame
|
||||
#define SAVEGAMESIZE (1024)
|
||||
|
||||
// For netgames
|
||||
#define NETSAVEGAMESIZE (768*1024)
|
||||
|
||||
// Persistent storage/archiving.
|
||||
// These are the load / save game routines.
|
||||
|
||||
void P_SaveGame(INT16 mapnum);
|
||||
void P_SaveNetGame(boolean resending);
|
||||
boolean P_LoadGame(INT16 mapoverride);
|
||||
boolean P_LoadNetGame(boolean reloading);
|
||||
void P_SaveGame(savebuffer_t *save, INT16 mapnum);
|
||||
void P_SaveNetGame(savebuffer_t *save, boolean resending);
|
||||
boolean P_LoadGame(savebuffer_t *save, INT16 mapoverride);
|
||||
boolean P_LoadNetGame(savebuffer_t *save, boolean reloading);
|
||||
|
||||
mobj_t *P_FindNewPosition(UINT32 oldposition);
|
||||
|
||||
|
|
@ -38,6 +44,13 @@ struct savedata_t
|
|||
};
|
||||
|
||||
extern savedata_t savedata;
|
||||
extern UINT8 *save_p;
|
||||
|
||||
struct savebuffer_t
|
||||
{
|
||||
UINT8 *buffer;
|
||||
UINT8 *p;
|
||||
UINT8 *end;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -723,37 +723,40 @@ void P_WriteThings(void)
|
|||
const char * filename;
|
||||
size_t i, length;
|
||||
mapthing_t *mt;
|
||||
UINT8 *savebuffer, *savebuf_p;
|
||||
savebuffer_t save;
|
||||
INT16 temp;
|
||||
|
||||
savebuf_p = savebuffer = (UINT8 *)malloc(nummapthings * sizeof (mapthing_t));
|
||||
save.size = nummapthings * sizeof (mapthing_t);
|
||||
save.p = save.buffer = (UINT8 *)malloc(nummapthings * sizeof (mapthing_t));
|
||||
|
||||
if (!savebuf_p)
|
||||
if (!save.p)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for thing writing!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
save.end = save.buffer + save.size;
|
||||
|
||||
mt = mapthings;
|
||||
for (i = 0; i < nummapthings; i++, mt++)
|
||||
{
|
||||
WRITEINT16(savebuf_p, mt->x);
|
||||
WRITEINT16(savebuf_p, mt->y);
|
||||
WRITEINT16(save.p, mt->x);
|
||||
WRITEINT16(save.p, mt->y);
|
||||
|
||||
WRITEINT16(savebuf_p, mt->angle);
|
||||
WRITEINT16(save.p, mt->angle);
|
||||
|
||||
temp = (INT16)(mt->type + ((INT16)mt->extrainfo << 12));
|
||||
WRITEINT16(savebuf_p, temp);
|
||||
WRITEUINT16(savebuf_p, mt->options);
|
||||
WRITEINT16(save.p, temp);
|
||||
WRITEUINT16(save.p, mt->options);
|
||||
}
|
||||
|
||||
length = savebuf_p - savebuffer;
|
||||
length = save.p - save.buffer;
|
||||
|
||||
filename = va("newthings-%s.lmp", G_BuildMapName(gamemap));
|
||||
|
||||
FIL_WriteFile(filename, savebuffer, length);
|
||||
free(savebuffer);
|
||||
savebuf_p = NULL;
|
||||
FIL_WriteFile(filename, save.buffer, length);
|
||||
free(save.buffer);
|
||||
save.p = NULL;
|
||||
|
||||
CONS_Printf(M_GetText("%s saved.\n"), filename);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,6 +258,7 @@ TYPEDEF (polyfadedata_t);
|
|||
|
||||
// p_saveg.h
|
||||
TYPEDEF (savedata_t);
|
||||
TYPEDEF (savebuffer_t);
|
||||
|
||||
// p_setup.h
|
||||
TYPEDEF (levelflat_t);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue