mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge remote-tracking branch 'origin/master' into ringbox
This commit is contained in:
commit
c71bb7092e
62 changed files with 1874 additions and 719 deletions
|
|
@ -41,6 +41,7 @@
|
|||
#include "../r_skins.h"
|
||||
#include "../k_battle.h"
|
||||
#include "../k_podium.h"
|
||||
#include "../k_bot.h"
|
||||
#include "../z_zone.h"
|
||||
|
||||
#include "call-funcs.hpp"
|
||||
|
|
@ -1710,6 +1711,52 @@ bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wor
|
|||
return false;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
|
||||
|
||||
Inserts a bot, if there's room for them.
|
||||
--------------------------------------------------*/
|
||||
bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
|
||||
{
|
||||
ACSVM::MapScope *map = NULL;
|
||||
|
||||
ACSVM::String *skinStr = nullptr;
|
||||
INT32 skin = -1;
|
||||
|
||||
UINT8 difficulty = 0;
|
||||
botStyle_e style = BOT_STYLE_NORMAL;
|
||||
|
||||
UINT8 newplayernum = 0;
|
||||
bool success = false;
|
||||
|
||||
(void)argC;
|
||||
|
||||
map = thread->scopeMap;
|
||||
|
||||
skinStr = map->getString(argV[0]);
|
||||
if (skinStr->len != 0)
|
||||
{
|
||||
skin = R_SkinAvailable(skinStr->str);
|
||||
}
|
||||
|
||||
if (skin == -1)
|
||||
{
|
||||
skin = R_BotDefaultSkin();
|
||||
}
|
||||
|
||||
difficulty = std::clamp(static_cast<int>(argV[1]), 1, MAXBOTDIFFICULTY);
|
||||
|
||||
style = static_cast<botStyle_e>(argV[2]);
|
||||
if (style < BOT_STYLE_NORMAL || style >= BOT_STYLE__MAX)
|
||||
{
|
||||
style = BOT_STYLE_NORMAL;
|
||||
}
|
||||
|
||||
success = K_AddBot(skin, difficulty, style, &newplayernum);
|
||||
thread->dataStk.push(success ? newplayernum : -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM
|
|||
bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
|
||||
|
||||
bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
|
||||
bool CallFunc_AddBot(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
|
||||
|
||||
bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
|
||||
bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC);
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ Environment::Environment()
|
|||
addFuncDataACS0( 502, addCallFunc(CallFunc_PodiumFinish));
|
||||
addFuncDataACS0( 503, addCallFunc(CallFunc_SetLineRenderStyle));
|
||||
addFuncDataACS0( 504, addCallFunc(CallFunc_MapWarp));
|
||||
addFuncDataACS0( 505, addCallFunc(CallFunc_AddBot));
|
||||
}
|
||||
|
||||
ACSVM::Thread *Environment::allocThread()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include "thread.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "../doomtype.h"
|
||||
#include "../doomdef.h"
|
||||
#include "../doomstat.h"
|
||||
|
|
@ -26,7 +25,6 @@ extern "C" {
|
|||
#include "../r_defs.h"
|
||||
#include "../r_state.h"
|
||||
#include "../p_polyobj.h"
|
||||
}
|
||||
|
||||
using namespace srb2::acs;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "acsvm.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "../doomtype.h"
|
||||
#include "../doomdef.h"
|
||||
#include "../doomstat.h"
|
||||
|
|
@ -23,7 +24,7 @@
|
|||
#include "../r_defs.h"
|
||||
#include "../r_state.h"
|
||||
#include "../p_spec.h"
|
||||
|
||||
}
|
||||
|
||||
namespace srb2::acs {
|
||||
|
||||
|
|
|
|||
103
src/d_clisrv.c
103
src/d_clisrv.c
|
|
@ -653,7 +653,6 @@ typedef enum
|
|||
static void GetPackets(void);
|
||||
|
||||
static cl_mode_t cl_mode = CL_SEARCHING;
|
||||
static cl_mode_t cl_requestmode = CL_ABORTED;
|
||||
|
||||
#ifdef HAVE_CURL
|
||||
char http_source[MAX_MIRROR_LENGTH];
|
||||
|
|
@ -1671,43 +1670,37 @@ void CL_UpdateServerList (void)
|
|||
SendAskInfo(BROADCASTADDR);
|
||||
}
|
||||
|
||||
static boolean M_ConfirmConnect(void)
|
||||
static void M_ConfirmConnect(INT32 choice)
|
||||
{
|
||||
if (G_PlayerInputDown(0, gc_b, 1) || G_PlayerInputDown(0, gc_x, 1) || G_GetDeviceGameKeyDownArray(0)[KEY_ESCAPE])
|
||||
{
|
||||
cl_requestmode = CL_ABORTED;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (G_PlayerInputDown(0, gc_a, 1) || G_GetDeviceGameKeyDownArray(0)[KEY_ENTER])
|
||||
if (choice == MA_YES)
|
||||
{
|
||||
if (totalfilesrequestednum > 0)
|
||||
{
|
||||
#ifdef HAVE_CURL
|
||||
#ifdef HAVE_CURL
|
||||
if (http_source[0] == '\0' || curl_failedwebdownload)
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
if (CL_SendFileRequest())
|
||||
{
|
||||
cl_requestmode = CL_DOWNLOADFILES;
|
||||
cl_mode = CL_DOWNLOADFILES;
|
||||
}
|
||||
else
|
||||
{
|
||||
cl_requestmode = CL_DOWNLOADFAILED;
|
||||
cl_mode = CL_DOWNLOADFAILED;
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_CURL
|
||||
#ifdef HAVE_CURL
|
||||
else
|
||||
cl_requestmode = CL_PREPAREHTTPFILES;
|
||||
#endif
|
||||
cl_mode = CL_PREPAREHTTPFILES;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
cl_requestmode = CL_LOADFILES;
|
||||
cl_mode = CL_LOADFILES;
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
return false;
|
||||
cl_mode = CL_ABORTED;
|
||||
}
|
||||
|
||||
static boolean CL_FinishedFileList(void)
|
||||
|
|
@ -1759,7 +1752,7 @@ static boolean CL_FinishedFileList(void)
|
|||
"This server is full!\n"
|
||||
"\n"
|
||||
"You may load server addons (if any), and wait for a slot.\n"
|
||||
), NULL, MM_NOTHING, "Continue", "Back to Menu");
|
||||
), &M_ConfirmConnect, MM_YESNO, "Continue", "Back to Menu");
|
||||
cl_mode = CL_CONFIRMCONNECT;
|
||||
}
|
||||
else
|
||||
|
|
@ -1822,13 +1815,13 @@ static boolean CL_FinishedFileList(void)
|
|||
"\n"
|
||||
"You may download, load server addons,\n"
|
||||
"and wait for a slot.\n"
|
||||
), downloadsize), NULL, MM_NOTHING, "Continue", "Back to Menu");
|
||||
), downloadsize), &M_ConfirmConnect, MM_YESNO, "Continue", "Back to Menu");
|
||||
else
|
||||
M_StartMessage("Server Connection",
|
||||
va(M_GetText(
|
||||
"Download of %s additional content\n"
|
||||
"is required to join.\n"
|
||||
), downloadsize), NULL, MM_NOTHING, "Continue", "Back to Menu");
|
||||
), downloadsize), &M_ConfirmConnect, MM_YESNO, "Continue", "Back to Menu");
|
||||
|
||||
Z_Free(downloadsize);
|
||||
cl_mode = CL_CONFIRMCONNECT;
|
||||
|
|
@ -1969,6 +1962,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
|
|||
{
|
||||
boolean waitmore;
|
||||
INT32 i;
|
||||
const UINT8 pid = 0;
|
||||
|
||||
switch (cl_mode)
|
||||
{
|
||||
|
|
@ -2168,26 +2162,27 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
|
|||
G_MapEventsToControls(&events[eventtail]);
|
||||
}
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&k_menu_mutex);
|
||||
#endif
|
||||
M_UpdateMenuCMD(0, true);
|
||||
|
||||
if (cl_mode == CL_CONFIRMCONNECT)
|
||||
{
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&k_menu_mutex);
|
||||
#endif
|
||||
if (M_MenuMessageTick() && M_ConfirmConnect())
|
||||
M_StopMessage(0);
|
||||
else if (menumessage.active == false)
|
||||
cl_mode = cl_requestmode;
|
||||
#ifdef HAVE_THREADS
|
||||
I_unlock_mutex(k_menu_mutex);
|
||||
#endif
|
||||
if (menumessage.active)
|
||||
M_HandleMenuMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (G_PlayerInputDown(0, gc_b, 1)
|
||||
|| G_PlayerInputDown(0, gc_x, 1)
|
||||
|| G_GetDeviceGameKeyDownArray(0)[KEY_ESCAPE])
|
||||
if (M_MenuBackPressed(pid))
|
||||
cl_mode = CL_ABORTED;
|
||||
}
|
||||
|
||||
M_ScreenshotTicker();
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
I_unlock_mutex(k_menu_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cl_mode == CL_ABORTED)
|
||||
|
|
@ -2265,7 +2260,6 @@ static void CL_ConnectToServer(void)
|
|||
lastfilenum = -1;
|
||||
|
||||
cl_mode = CL_SEARCHING;
|
||||
cl_requestmode = CL_ABORTED; // sane default
|
||||
|
||||
// Don't get a corrupt savegame error because tmpsave already exists
|
||||
if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
|
||||
|
|
@ -4010,6 +4004,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
INT16 newplayernum;
|
||||
UINT8 skinnum = 0;
|
||||
UINT8 difficulty = DIFFICULTBOT;
|
||||
botStyle_e style = BOT_STYLE_NORMAL;
|
||||
|
||||
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
|
||||
{
|
||||
|
|
@ -4025,41 +4020,9 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
|
|||
newplayernum = READUINT8(*p);
|
||||
skinnum = READUINT8(*p);
|
||||
difficulty = READUINT8(*p);
|
||||
style = READUINT8(*p);
|
||||
|
||||
CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum);
|
||||
|
||||
// Clear player before joining, lest some things get set incorrectly
|
||||
CL_ClearPlayer(newplayernum);
|
||||
G_DestroyParty(newplayernum);
|
||||
|
||||
playeringame[newplayernum] = true;
|
||||
G_AddPlayer(newplayernum);
|
||||
if (newplayernum+1 > doomcom->numslots)
|
||||
doomcom->numslots = (INT16)(newplayernum+1);
|
||||
|
||||
playernode[newplayernum] = servernode;
|
||||
|
||||
// this will permit unlocks
|
||||
memcpy(&players[newplayernum].availabilities, R_GetSkinAvailabilities(false, true), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
players[newplayernum].splitscreenindex = 0;
|
||||
players[newplayernum].bot = true;
|
||||
players[newplayernum].botvars.difficulty = difficulty;
|
||||
players[newplayernum].lives = 9;
|
||||
|
||||
players[newplayernum].skincolor = skins[skinnum].prefcolor;
|
||||
sprintf(player_names[newplayernum], "%s", skins[skinnum].realname);
|
||||
SetPlayerSkinByNum(newplayernum, skinnum);
|
||||
|
||||
playerconsole[newplayernum] = newplayernum;
|
||||
G_BuildLocalSplitscreenParty(newplayernum);
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false);
|
||||
}
|
||||
|
||||
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
|
||||
K_SetBot(newplayernum, skinnum, difficulty, style);
|
||||
}
|
||||
|
||||
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities,
|
||||
|
|
|
|||
11
src/d_main.c
11
src/d_main.c
|
|
@ -140,7 +140,7 @@ UINT16 numskincolors;
|
|||
menucolor_t *menucolorhead, *menucolortail;
|
||||
|
||||
char savegamename[256];
|
||||
char liveeventbackup[256];
|
||||
char gpbackup[256];
|
||||
|
||||
char srb2home[256] = ".";
|
||||
char srb2path[256] = ".";
|
||||
|
|
@ -295,7 +295,7 @@ void D_ProcessEvents(void)
|
|||
// Update menu CMD
|
||||
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
||||
{
|
||||
M_UpdateMenuCMD(i);
|
||||
M_UpdateMenuCMD(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -982,7 +982,6 @@ void D_ClearState(void)
|
|||
cht_debug = 0;
|
||||
emeralds = 0;
|
||||
memset(&luabanks, 0, sizeof(luabanks));
|
||||
lastmaploaded = 0;
|
||||
|
||||
// In case someone exits out at the same time they start a time attack run,
|
||||
// reset modeattacking
|
||||
|
|
@ -1327,7 +1326,7 @@ void D_SRB2Main(void)
|
|||
|
||||
// default savegame
|
||||
strcpy(savegamename, SAVEGAMENAME"%u.ssg");
|
||||
strcpy(liveeventbackup, "live"SAVEGAMENAME".bkp"); // intentionally not ending with .ssg
|
||||
strcpy(gpbackup, "gp"SAVEGAMENAME".bkp"); // intentionally not ending with .ssg
|
||||
|
||||
// Init the joined IP table for quick rejoining of past games.
|
||||
M_InitJoinedIPArray();
|
||||
|
|
@ -1358,7 +1357,7 @@ void D_SRB2Main(void)
|
|||
|
||||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, srb2home, PATHSEP);
|
||||
strcatbf(liveeventbackup, srb2home, PATHSEP);
|
||||
strcatbf(gpbackup, srb2home, PATHSEP);
|
||||
|
||||
snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", srb2home);
|
||||
#else // DEFAULTDIR
|
||||
|
|
@ -1370,7 +1369,7 @@ void D_SRB2Main(void)
|
|||
|
||||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, userhome, PATHSEP);
|
||||
strcatbf(liveeventbackup, userhome, PATHSEP);
|
||||
strcatbf(gpbackup, userhome, PATHSEP);
|
||||
|
||||
snprintf(luafiledir, sizeof luafiledir, "%s" PATHSEP "luafiles", userhome);
|
||||
#endif // DEFAULTDIR
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ consvar_t cv_items[NUMKARTRESULTS-1] = {
|
|||
CVAR_INIT ("rocketsneaker", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("invincibility", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("banana", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("eggmanmonitor", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("eggmark", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("orbinaut", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("jawz", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
CVAR_INIT ("mine", "On", CV_NETVAR, CV_OnOff, NULL),
|
||||
|
|
|
|||
|
|
@ -324,9 +324,20 @@ struct respawnvars_t
|
|||
boolean init;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BOT_STYLE_NORMAL,
|
||||
BOT_STYLE_STAY,
|
||||
//BOT_STYLE_CHASE,
|
||||
//BOT_STYLE_ESCAPE,
|
||||
BOT_STYLE__MAX
|
||||
} botStyle_e;
|
||||
|
||||
// player_t struct for all bot variables
|
||||
struct botvars_t
|
||||
{
|
||||
botStyle_e style; // Training mode-style CPU mode
|
||||
|
||||
UINT8 difficulty; // Bot's difficulty setting
|
||||
UINT8 diffincrease; // In GP: bot difficulty will increase this much next round
|
||||
boolean rival; // If true, they're the GP rival
|
||||
|
|
@ -756,16 +767,22 @@ struct player_t
|
|||
mobj_t *stumbleIndicator;
|
||||
mobj_t *sliptideZipIndicator;
|
||||
mobj_t *whip;
|
||||
mobj_t *hand;
|
||||
|
||||
UINT8 instaShieldCooldown;
|
||||
UINT8 guardCooldown;
|
||||
|
||||
UINT8 handtimer;
|
||||
angle_t besthanddirection;
|
||||
|
||||
INT16 incontrol; // -1 to -175 when spinning out or tumbling, 1 to 175 when not. Use to check for combo hits or emergency inputs.
|
||||
|
||||
boolean markedfordeath;
|
||||
|
||||
UINT8 ringboxdelay; // Delay until Ring Box auto-activates
|
||||
UINT8 ringboxaward; // Where did we stop?
|
||||
|
||||
fixed_t outrun; // Milky Way road effect
|
||||
|
||||
uint8_t public_key[PUBKEYLENGTH];
|
||||
|
||||
|
|
|
|||
|
|
@ -1278,6 +1278,10 @@ void readlevelheader(MYFILE *f, char * name)
|
|||
{
|
||||
mapheaderinfo[num]->light_contrast = (UINT8)i;
|
||||
}
|
||||
else if (fastcmp(word, "SPRITEBACKLIGHT"))
|
||||
{
|
||||
mapheaderinfo[num]->sprite_backlight = (SINT8)i;
|
||||
}
|
||||
else if (fastcmp(word, "LIGHTANGLE"))
|
||||
{
|
||||
if (fastcmp(word2, "EVEN"))
|
||||
|
|
@ -2993,8 +2997,8 @@ void readmaincfg(MYFILE *f, boolean mainfile)
|
|||
// can't use sprintf since there is %u in savegamename
|
||||
strcatbf(savegamename, srb2home, PATHSEP);
|
||||
|
||||
strcpy(liveeventbackup, va("live%s.bkp", timeattackfolder));
|
||||
strcatbf(liveeventbackup, srb2home, PATHSEP);
|
||||
strcpy(gpbackup, va("gp%s.bkp", timeattackfolder));
|
||||
strcatbf(gpbackup, srb2home, PATHSEP);
|
||||
|
||||
refreshdirmenu |= REFRESHDIR_GAMEDATA;
|
||||
gamedataadded = true;
|
||||
|
|
|
|||
|
|
@ -3299,9 +3299,15 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
"S_SLIPTIDEZIP",
|
||||
|
||||
"S_INSTAWHIP",
|
||||
"S_INSTAWHIP_RECHARGE1",
|
||||
"S_INSTAWHIP_RECHARGE2",
|
||||
"S_INSTAWHIP_RECHARGE3",
|
||||
"S_INSTAWHIP_RECHARGE4",
|
||||
"S_BLOCKRING",
|
||||
"S_BLOCKBODY",
|
||||
|
||||
"S_SERVANTHAND",
|
||||
|
||||
// Signpost sparkles
|
||||
"S_SIGNSPARK1",
|
||||
"S_SIGNSPARK2",
|
||||
|
|
@ -5345,9 +5351,12 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
"MT_SLIPTIDEZIP",
|
||||
|
||||
"MT_INSTAWHIP",
|
||||
"MT_INSTAWHIP_RECHARGE",
|
||||
"MT_BLOCKRING",
|
||||
"MT_BLOCKBODY",
|
||||
|
||||
"MT_SERVANTHAND",
|
||||
|
||||
"MT_SIGNSPARKLE",
|
||||
|
||||
"MT_FASTLINE",
|
||||
|
|
|
|||
|
|
@ -517,9 +517,9 @@ void CONS_Debug(UINT32 debugflags, const char *fmt, ...) FUNCDEBUG;
|
|||
#include "m_swap.h"
|
||||
|
||||
// Things that used to be in dstrings.h
|
||||
#define SAVEGAMENAME "srb2sav"
|
||||
#define SAVEGAMENAME "ringsav"
|
||||
extern char savegamename[256];
|
||||
extern char liveeventbackup[256];
|
||||
extern char gpbackup[256];
|
||||
|
||||
// m_misc.h
|
||||
#ifdef GETTEXT
|
||||
|
|
|
|||
|
|
@ -51,8 +51,6 @@ extern UINT8 mapmusrng;
|
|||
extern UINT32 maptol;
|
||||
|
||||
extern INT32 cursaveslot;
|
||||
//extern INT16 lastmapsaved;
|
||||
extern INT16 lastmaploaded;
|
||||
extern UINT8 gamecomplete;
|
||||
|
||||
// Extra abilities/settings for skins (combinable stuff)
|
||||
|
|
@ -503,6 +501,7 @@ struct mapheader_t
|
|||
UINT16 palette; ///< PAL lump to use on this map
|
||||
UINT16 encorepal; ///< PAL for encore mode
|
||||
UINT8 light_contrast; ///< Range of wall lighting. 0 is no lighting.
|
||||
SINT8 sprite_backlight; ///< Subtract from wall lighting for sprites only.
|
||||
boolean use_light_angle; ///< When false, wall lighting is evenly distributed. When true, wall lighting is directional.
|
||||
angle_t light_angle; ///< Angle of directional wall lighting.
|
||||
|
||||
|
|
@ -762,6 +761,7 @@ extern fixed_t mapobjectscale;
|
|||
extern struct maplighting
|
||||
{
|
||||
UINT8 contrast;
|
||||
SINT8 backlight;
|
||||
boolean directional;
|
||||
angle_t angle;
|
||||
} maplighting;
|
||||
|
|
|
|||
|
|
@ -2131,8 +2131,10 @@ void F_TitleScreenTicker(boolean run)
|
|||
// Now start the music
|
||||
S_ChangeMusicInternal("_title", looptitle);
|
||||
}
|
||||
else if (menumessage.fadetimer < 9)
|
||||
menumessage.fadetimer++;
|
||||
else if (menumessage.active)
|
||||
{
|
||||
M_MenuMessageTick();
|
||||
}
|
||||
|
||||
finalecount++;
|
||||
}
|
||||
|
|
|
|||
325
src/g_game.c
325
src/g_game.c
|
|
@ -123,8 +123,6 @@ precipprops_t precipprops[MAXPRECIP] =
|
|||
preciptype_t precip_freeslot = PRECIP_FIRSTFREESLOT;
|
||||
|
||||
INT32 cursaveslot = 0; // Auto-save 1p savegame slot
|
||||
//INT16 lastmapsaved = 0; // Last map we auto-saved at
|
||||
INT16 lastmaploaded = 0; // Last map the game loaded
|
||||
UINT8 gamecomplete = 0;
|
||||
|
||||
marathonmode_t marathonmode = 0;
|
||||
|
|
@ -2689,6 +2687,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
P_SetTarget(&players[player].awayview.mobj, NULL);
|
||||
P_SetTarget(&players[player].stumbleIndicator, NULL);
|
||||
P_SetTarget(&players[player].whip, NULL);
|
||||
P_SetTarget(&players[player].hand, NULL);
|
||||
P_SetTarget(&players[player].ringShooter, NULL);
|
||||
P_SetTarget(&players[player].followmobj, NULL);
|
||||
|
||||
|
|
@ -3350,6 +3349,7 @@ void G_ExitLevel(void)
|
|||
else
|
||||
{
|
||||
// Back to the menu with you.
|
||||
G_HandleSaveLevel(true);
|
||||
D_QuitNetGame();
|
||||
CL_Reset();
|
||||
D_ClearState();
|
||||
|
|
@ -4017,39 +4017,29 @@ void G_UpdateVisited(void)
|
|||
G_SaveGameData();
|
||||
}
|
||||
|
||||
static boolean CanSaveLevel(INT32 mapnum)
|
||||
void G_HandleSaveLevel(boolean removecondition)
|
||||
{
|
||||
// SRB2Kart: No save files yet
|
||||
(void)mapnum;
|
||||
return false;
|
||||
}
|
||||
if (!grandprixinfo.gp || !grandprixinfo.cup
|
||||
|| splitscreen || netgame)
|
||||
return;
|
||||
|
||||
static void G_HandleSaveLevel(void)
|
||||
{
|
||||
// do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
|
||||
if (nextmap >= NEXTMAP_SPECIAL)
|
||||
{
|
||||
if (!gamecomplete)
|
||||
gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
|
||||
if (cursaveslot > 0)
|
||||
{
|
||||
if (marathonmode)
|
||||
{
|
||||
// don't keep a backup around when the run is done!
|
||||
if (FIL_FileExists(liveeventbackup))
|
||||
remove(liveeventbackup);
|
||||
cursaveslot = 0;
|
||||
}
|
||||
else if (!usedCheats && !(netgame || multiplayer || ultimatemode || demo.recording || metalrecording || modeattacking))
|
||||
G_SaveGame((UINT32)cursaveslot, 0); // TODO when we readd a campaign one day
|
||||
}
|
||||
}
|
||||
// and doing THIS here means you don't lose your progress if you close the game mid-intermission
|
||||
else if (!(ultimatemode || demo.playback || demo.recording || metalrecording || modeattacking)
|
||||
&& cursaveslot > 0 && CanSaveLevel(lastmap+1))
|
||||
{
|
||||
G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages
|
||||
}
|
||||
if (removecondition)
|
||||
goto doremove;
|
||||
|
||||
if (gamestate != GS_LEVEL
|
||||
|| roundqueue.size == 0)
|
||||
return;
|
||||
|
||||
if (roundqueue.position == 1
|
||||
|| players[consoleplayer].lives <= 1) // because a life is lost on reload
|
||||
goto doremove;
|
||||
|
||||
G_SaveGame();
|
||||
return;
|
||||
|
||||
doremove:
|
||||
if (FIL_FileExists(gpbackup))
|
||||
remove(gpbackup);
|
||||
}
|
||||
|
||||
// Next map apparatus
|
||||
|
|
@ -4156,7 +4146,7 @@ void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencor
|
|||
}
|
||||
}
|
||||
|
||||
static void G_GetNextMap(void)
|
||||
void G_GetNextMap(void)
|
||||
{
|
||||
INT32 i;
|
||||
boolean setalready = false;
|
||||
|
|
@ -4539,7 +4529,6 @@ void G_AfterIntermission(void)
|
|||
if (gamestate != GS_VOTING)
|
||||
{
|
||||
G_GetNextMap();
|
||||
G_HandleSaveLevel();
|
||||
}
|
||||
|
||||
if ((grandprixinfo.gp == true) && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene.
|
||||
|
|
@ -4644,9 +4633,6 @@ static void G_DoContinued(void)
|
|||
// Reset score
|
||||
pl->score = 0;
|
||||
|
||||
if (!(netgame || multiplayer || demo.playback || demo.recording || metalrecording || modeattacking) && !usedCheats && cursaveslot > 0)
|
||||
G_SaveGameOver((UINT32)cursaveslot, true);
|
||||
|
||||
// Reset # of lives
|
||||
pl->lives = 3;
|
||||
|
||||
|
|
@ -5672,24 +5658,23 @@ void G_SaveGameData(void)
|
|||
// G_InitFromSavegame
|
||||
// Can be called by the startup code or the menu task.
|
||||
//
|
||||
void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
||||
|
||||
#define SAV_VERSIONMINOR 2
|
||||
|
||||
void G_LoadGame(void)
|
||||
{
|
||||
char vcheck[VERSIONSIZE];
|
||||
char vcheck[VERSIONSIZE+1];
|
||||
char savename[255];
|
||||
UINT8 versionMinor;
|
||||
savebuffer_t save = {0};
|
||||
|
||||
// memset savedata to all 0, fixes calling perfectly valid saves corrupt because of bots
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
|
||||
#ifdef SAVEGAME_OTHERVERSIONS
|
||||
//Oh christ. The force load response needs access to mapoverride too...
|
||||
startonmapnum = mapoverride;
|
||||
#endif
|
||||
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
//if (makelivebackup)
|
||||
strcpy(savename, gpbackup);
|
||||
//else
|
||||
//sprintf(savename, savegamename, cursaveslot);
|
||||
|
||||
if (P_SaveBufferFromFile(&save, savename) == false)
|
||||
{
|
||||
|
|
@ -5697,23 +5682,16 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
return;
|
||||
}
|
||||
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
if (strcmp((const char *)save.p, (const char *)vcheck))
|
||||
{
|
||||
#ifdef SAVEGAME_OTHERVERSIONS
|
||||
M_StartMessage("Savegame Load", 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"),
|
||||
M_ForceLoadGameResponse, MM_YESNO, NULL, NULL);
|
||||
//Freeing done by the callback function of the above message
|
||||
#else
|
||||
M_ClearMenus(true); // so ESC backs out to title
|
||||
M_StartMessage("Savegame Load", M_GetText("Save game from different version\n\n"), NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Command_ExitGame_f();
|
||||
P_SaveBufferFree(&save);
|
||||
versionMinor = READUINT8(save.p);
|
||||
|
||||
// no cheating!
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
#endif
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, "version %d", VERSION);
|
||||
|
||||
if (versionMinor != SAV_VERSIONMINOR
|
||||
|| memcmp(save.p, vcheck, VERSIONSIZE))
|
||||
{
|
||||
M_StartMessage("Savegame Load", va(M_GetText("Savegame %s is from\na different version."), savename), NULL, MM_NOTHING, NULL, NULL);
|
||||
P_SaveBufferFree(&save);
|
||||
return; // bad version
|
||||
}
|
||||
save.p += VERSIONSIZE;
|
||||
|
|
@ -5725,62 +5703,91 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
|
|||
// automapactive = false;
|
||||
|
||||
// dearchive all the modifications
|
||||
if (!P_LoadGame(&save, mapoverride))
|
||||
if (!P_LoadGame(&save))
|
||||
{
|
||||
M_ClearMenus(true); // so ESC backs out to title
|
||||
M_StartMessage("Savegame Load", M_GetText("Savegame file corrupted\n"), NULL, MM_NOTHING, NULL, "Return to Menu");
|
||||
Command_ExitGame_f();
|
||||
M_StartMessage("Savegame Load", va(M_GetText("Savegame %s could not be loaded.\n"
|
||||
"Check the console log for more info.\n"), savename), NULL, MM_NOTHING, NULL, NULL);
|
||||
Z_Free(save.buffer);
|
||||
save.p = save.buffer = NULL;
|
||||
|
||||
// no cheating!
|
||||
memset(&savedata, 0, sizeof(savedata));
|
||||
return;
|
||||
}
|
||||
if (marathonmode)
|
||||
{
|
||||
marathontime = READUINT32(save.p);
|
||||
marathonmode |= READUINT8(save.p);
|
||||
}
|
||||
|
||||
// done
|
||||
P_SaveBufferFree(&save);
|
||||
|
||||
// gameaction = ga_nothing;
|
||||
// G_SetGamestate(GS_LEVEL);
|
||||
displayplayers[0] = consoleplayer;
|
||||
multiplayer = false;
|
||||
splitscreen = 0;
|
||||
SplitScreen_OnChange(); // not needed?
|
||||
|
||||
// G_DeferedInitNew(sk_medium, G_BuildMapName(1), 0, 0, 1);
|
||||
if (setsizeneeded)
|
||||
R_ExecuteSetViewSize();
|
||||
|
||||
M_ClearMenus(true);
|
||||
CON_ToggleOff();
|
||||
}
|
||||
|
||||
void G_GetBackupCupData(boolean actuallygetdata)
|
||||
{
|
||||
if (actuallygetdata == false)
|
||||
{
|
||||
cupsavedata.cup = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
char vcheck[VERSIONSIZE+1];
|
||||
char savename[255];
|
||||
UINT8 versionMinor;
|
||||
savebuffer_t save = {0};
|
||||
|
||||
//if (makelivebackup)
|
||||
strcpy(savename, gpbackup);
|
||||
//else
|
||||
//sprintf(savename, savegamename, cursaveslot);
|
||||
|
||||
if (P_SaveBufferFromFile(&save, savename) == false)
|
||||
{
|
||||
cupsavedata.cup = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
versionMinor = READUINT8(save.p);
|
||||
|
||||
memset(vcheck, 0, sizeof (vcheck));
|
||||
sprintf(vcheck, "version %d", VERSION);
|
||||
|
||||
if (versionMinor != SAV_VERSIONMINOR
|
||||
|| memcmp(save.p, vcheck, VERSIONSIZE))
|
||||
{
|
||||
cupsavedata.cup = NULL;
|
||||
P_SaveBufferFree(&save);
|
||||
return; // bad version
|
||||
}
|
||||
save.p += VERSIONSIZE;
|
||||
|
||||
P_GetBackupCupData(&save);
|
||||
|
||||
if (cv_dummygpdifficulty.value != cupsavedata.difficulty
|
||||
|| !!cv_dummygpencore.value != cupsavedata.encore)
|
||||
{
|
||||
// Still not compatible.
|
||||
cupsavedata.cup = NULL;
|
||||
}
|
||||
|
||||
// done
|
||||
P_SaveBufferFree(&save);
|
||||
}
|
||||
|
||||
//
|
||||
// G_SaveGame
|
||||
// Saves your game.
|
||||
//
|
||||
void G_SaveGame(UINT32 slot, INT16 mapnum)
|
||||
void G_SaveGame(void)
|
||||
{
|
||||
boolean saved;
|
||||
char savename[256] = "";
|
||||
const char *backup;
|
||||
savebuffer_t save = {0};
|
||||
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
backup = va("%s",savename);
|
||||
//if (makelivebackup)
|
||||
strcpy(savename, gpbackup);
|
||||
//else
|
||||
//sprintf(savename, savegamename, cursaveslot);
|
||||
|
||||
gameaction = ga_nothing;
|
||||
{
|
||||
char name[VERSIONSIZE];
|
||||
char name[VERSIONSIZE+1];
|
||||
size_t length;
|
||||
|
||||
if (P_SaveBufferAlloc(&save, SAVEGAMESIZE) == false)
|
||||
|
|
@ -5789,139 +5796,27 @@ void G_SaveGame(UINT32 slot, INT16 mapnum)
|
|||
return;
|
||||
}
|
||||
|
||||
WRITEUINT8(save.p, SAV_VERSIONMINOR);
|
||||
|
||||
memset(name, 0, sizeof (name));
|
||||
sprintf(name, (marathonmode ? "back-up %d" : "version %d"), VERSION);
|
||||
sprintf(name, "version %d", VERSION);
|
||||
WRITEMEM(save.p, name, VERSIONSIZE);
|
||||
|
||||
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));
|
||||
}
|
||||
P_SaveGame(&save);
|
||||
|
||||
length = save.p - save.buffer;
|
||||
saved = FIL_WriteFile(backup, save.buffer, length);
|
||||
saved = FIL_WriteFile(savename, save.buffer, length);
|
||||
P_SaveBufferFree(&save);
|
||||
}
|
||||
|
||||
gameaction = ga_nothing;
|
||||
|
||||
if (cht_debug && saved)
|
||||
CONS_Printf(M_GetText("Game saved.\n"));
|
||||
CONS_Printf(M_GetText("%s saved.\n"), savename);
|
||||
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));
|
||||
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s\n"), savename);
|
||||
}
|
||||
|
||||
#define BADSAVE goto cleanup;
|
||||
#define CHECKPOS if (save.p >= save.end) BADSAVE
|
||||
void G_SaveGameOver(UINT32 slot, boolean modifylives)
|
||||
{
|
||||
boolean saved = false;
|
||||
size_t length;
|
||||
char vcheck[VERSIONSIZE];
|
||||
char savename[255];
|
||||
const char *backup;
|
||||
savebuffer_t save = {0};
|
||||
|
||||
if (marathonmode)
|
||||
strcpy(savename, liveeventbackup);
|
||||
else
|
||||
sprintf(savename, savegamename, slot);
|
||||
backup = va("%s",savename);
|
||||
|
||||
if (P_SaveBufferFromFile(&save, savename) == false)
|
||||
{
|
||||
CONS_Printf(M_GetText("Couldn't read file %s\n"), savename);
|
||||
return;
|
||||
}
|
||||
|
||||
length = save.size;
|
||||
|
||||
{
|
||||
char temp[sizeof(timeattackfolder)];
|
||||
UINT8 *lives_p;
|
||||
SINT8 pllives;
|
||||
|
||||
// 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;
|
||||
|
||||
// P_UnArchiveMisc()
|
||||
(void)READINT16(save.p);
|
||||
CHECKPOS
|
||||
(void)READUINT16(save.p); // emeralds
|
||||
CHECKPOS
|
||||
READSTRINGN(save.p, temp, sizeof(temp)); // mod it belongs to
|
||||
if (strcmp(temp, timeattackfolder)) BADSAVE
|
||||
|
||||
// P_UnArchivePlayer()
|
||||
CHECKPOS
|
||||
(void)READUINT16(save.p);
|
||||
CHECKPOS
|
||||
|
||||
WRITEUINT8(save.p, numgameovers);
|
||||
CHECKPOS
|
||||
|
||||
lives_p = save.p;
|
||||
pllives = READSINT8(save.p); // lives
|
||||
CHECKPOS
|
||||
if (modifylives && pllives < startinglivesbalance[numgameovers])
|
||||
{
|
||||
pllives = startinglivesbalance[numgameovers];
|
||||
WRITESINT8(lives_p, pllives);
|
||||
}
|
||||
|
||||
(void)READINT32(save.p); // Score
|
||||
CHECKPOS
|
||||
(void)READINT32(save.p); // continues
|
||||
|
||||
// File end marker check
|
||||
CHECKPOS
|
||||
switch (READUINT8(save.p))
|
||||
{
|
||||
case 0xb7:
|
||||
{
|
||||
UINT8 i, banksinuse;
|
||||
CHECKPOS
|
||||
banksinuse = READUINT8(save.p);
|
||||
CHECKPOS
|
||||
if (banksinuse > NUM_LUABANKS)
|
||||
BADSAVE
|
||||
for (i = 0; i < banksinuse; i++)
|
||||
{
|
||||
(void)READINT32(save.p);
|
||||
CHECKPOS
|
||||
}
|
||||
if (READUINT8(save.p) != 0x1d)
|
||||
BADSAVE
|
||||
}
|
||||
case 0x1d:
|
||||
break;
|
||||
default:
|
||||
BADSAVE
|
||||
}
|
||||
|
||||
// done
|
||||
saved = FIL_WriteFile(backup, save.buffer, length);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (cht_debug && saved)
|
||||
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));
|
||||
|
||||
P_SaveBufferFree(&save);
|
||||
}
|
||||
#undef CHECKPOS
|
||||
#undef BADSAVE
|
||||
|
||||
//
|
||||
// G_DeferedInitNew
|
||||
// Can be called by the startup code or the menu task,
|
||||
|
|
|
|||
11
src/g_game.h
11
src/g_game.h
|
|
@ -187,16 +187,14 @@ void G_StartTitleCard(void);
|
|||
void G_PreLevelTitleCard(void);
|
||||
boolean G_IsTitleCardAvailable(void);
|
||||
|
||||
// Can be called by the startup code or M_Responder, calls P_SetupLevel.
|
||||
void G_LoadGame(UINT32 slot, INT16 mapoverride);
|
||||
void G_HandleSaveLevel(boolean removecondition);
|
||||
void G_SaveGame(void);
|
||||
void G_LoadGame(void);
|
||||
void G_GetBackupCupData(boolean actuallygetdata);
|
||||
|
||||
void G_SaveGameData(void);
|
||||
void G_DirtyGameData(void);
|
||||
|
||||
void G_SaveGame(UINT32 slot, INT16 mapnum);
|
||||
|
||||
void G_SaveGameOver(UINT32 slot, boolean modifylives);
|
||||
|
||||
void G_SetGametype(INT16 gametype);
|
||||
char *G_PrepareGametypeConstant(const char *newgtconst);
|
||||
void G_AddTOL(UINT32 newtol, const char *tolname);
|
||||
|
|
@ -209,6 +207,7 @@ boolean G_GametypeHasSpectators(void);
|
|||
INT16 G_SometimesGetDifferentEncore(void);
|
||||
void G_ExitLevel(void);
|
||||
void G_NextLevel(void);
|
||||
void G_GetNextMap(void);
|
||||
void G_Continue(void);
|
||||
void G_UseContinue(void);
|
||||
void G_AfterIntermission(void);
|
||||
|
|
|
|||
63
src/info.c
63
src/info.c
|
|
@ -557,9 +557,12 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"SLPT", // Sliptide zip indicator
|
||||
|
||||
"IWHP", // Instawhip
|
||||
"WPRE", // Instawhip Recharge
|
||||
"GRNG", // Guard ring
|
||||
"GBDY", // Guard body
|
||||
|
||||
"DHND", // Servant Hand
|
||||
|
||||
"WIPD", // Wipeout dust trail
|
||||
"DRIF", // Drift Sparks
|
||||
"BDRF", // Brake drift sparks
|
||||
|
|
@ -3965,9 +3968,15 @@ state_t states[NUMSTATES] =
|
|||
{SPR_SLPT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SLIPTIDEZIP
|
||||
|
||||
{SPR_IWHP, FF_FLOORSPRITE|FF_ANIMATE|0, -1, {NULL}, 6, 2, S_NULL}, // S_INSTAWHIP
|
||||
{SPR_NULL, 0, 1, {NULL}, 0, 0, S_INSTAWHIP_RECHARGE2}, // S_INSTAWHIP_RECHARGE1
|
||||
{SPR_NULL, 0, 0, {A_PlaySound}, sfx_s3ka0, 2, S_INSTAWHIP_RECHARGE3}, // S_INSTAWHIP_RECHARGE2
|
||||
{SPR_WPRE, FF_FULLBRIGHT|FF_FLOORSPRITE|FF_ANIMATE|0, 36, {NULL}, 17, 2, S_INSTAWHIP_RECHARGE4}, // S_INSTAWHIP_RECHARGE3
|
||||
{SPR_NULL, 0, 0, {A_PlaySound}, sfx_s3k7c, 2, S_NULL}, // S_INSTAWHIP_RECHARGE4
|
||||
{SPR_GRNG, FF_FULLBRIGHT|FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_BLOCKRING
|
||||
{SPR_GBDY, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_BLOCKBODY
|
||||
|
||||
{SPR_DHND, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SERVANTHAND
|
||||
|
||||
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_SIGNSPARK2}, // S_SIGNSPARK1
|
||||
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_SIGNSPARK3}, // S_SIGNSPARK2
|
||||
{SPR_SGNS, FF_ADD|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_SIGNSPARK4}, // S_SIGNSPARK3
|
||||
|
|
@ -22719,6 +22728,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_INSTAWHIP_RECHARGE
|
||||
-1, // doomednum
|
||||
S_INSTAWHIP_RECHARGE1, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
90*FRACUNIT, // radius
|
||||
90*FRACUNIT, // height
|
||||
0, // display offset
|
||||
100, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY|MF_NOCLIPTHING|MF_DONTENCOREMAP|MF_NOSQUISH, // flags
|
||||
S_INSTAWHIP_RECHARGE3 // raisestate
|
||||
},
|
||||
|
||||
{ // MT_BLOCKRING
|
||||
-1, // doomednum
|
||||
S_BLOCKRING, // spawnstate
|
||||
|
|
@ -22772,6 +22808,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_SERVANTHAND
|
||||
-1, // doomednum
|
||||
S_SERVANTHAND, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
40*FRACUNIT, // radius
|
||||
40*FRACUNIT, // height
|
||||
0, // display offset
|
||||
100, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_SIGNSPARKLE
|
||||
-1, // doomednum
|
||||
|
|
|
|||
12
src/info.h
12
src/info.h
|
|
@ -1108,9 +1108,12 @@ typedef enum sprite
|
|||
SPR_SLPT, // Sliptide zip indicator
|
||||
|
||||
SPR_IWHP, // Instawhip
|
||||
SPR_WPRE, // Instawhip Recharge
|
||||
SPR_GRNG, // Guard ring
|
||||
SPR_GBDY, // Guard body
|
||||
|
||||
SPR_DHND, // Servant Hand
|
||||
|
||||
SPR_WIPD, // Wipeout dust trail
|
||||
SPR_DRIF, // Drift Sparks
|
||||
SPR_BDRF, // Brake drift sparks
|
||||
|
|
@ -4375,9 +4378,15 @@ typedef enum state
|
|||
S_SLIPTIDEZIP,
|
||||
|
||||
S_INSTAWHIP,
|
||||
S_INSTAWHIP_RECHARGE1,
|
||||
S_INSTAWHIP_RECHARGE2,
|
||||
S_INSTAWHIP_RECHARGE3,
|
||||
S_INSTAWHIP_RECHARGE4,
|
||||
S_BLOCKRING,
|
||||
S_BLOCKBODY,
|
||||
|
||||
S_SERVANTHAND,
|
||||
|
||||
// Signpost sparkles
|
||||
S_SIGNSPARK1,
|
||||
S_SIGNSPARK2,
|
||||
|
|
@ -6456,9 +6465,12 @@ typedef enum mobj_type
|
|||
MT_SLIPTIDEZIP,
|
||||
|
||||
MT_INSTAWHIP,
|
||||
MT_INSTAWHIP_RECHARGE,
|
||||
MT_BLOCKRING,
|
||||
MT_BLOCKBODY,
|
||||
|
||||
MT_SERVANTHAND,
|
||||
|
||||
MT_SIGNSPARKLE,
|
||||
|
||||
MT_FASTLINE,
|
||||
|
|
|
|||
268
src/k_bot.c
268
src/k_bot.c
|
|
@ -31,20 +31,102 @@
|
|||
#include "k_podium.h"
|
||||
#include "k_respawn.h"
|
||||
#include "m_easing.h"
|
||||
#include "d_clisrv.h"
|
||||
#include "g_party.h"
|
||||
#include "k_grandprix.h" // K_CanChangeRules
|
||||
#include "hu_stuff.h" // HU_AddChatText
|
||||
#include "discord.h" // DRPC_UpdatePresence
|
||||
#include "i_net.h" // doomcom
|
||||
|
||||
#ifdef DEVELOP
|
||||
consvar_t cv_botcontrol = CVAR_INIT ("botcontrol", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL);
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p)
|
||||
void K_SetBot(UINT8 playerNum, UINT8 skinnum, UINT8 difficulty, botStyle_e style)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p)
|
||||
void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e style)
|
||||
{
|
||||
CONS_Debug(DBG_NETPLAY, "addbot: %d\n", newplayernum);
|
||||
|
||||
// Clear player before joining, lest some things get set incorrectly
|
||||
CL_ClearPlayer(newplayernum);
|
||||
G_DestroyParty(newplayernum);
|
||||
|
||||
playeringame[newplayernum] = true;
|
||||
G_AddPlayer(newplayernum);
|
||||
if (newplayernum+1 > doomcom->numslots)
|
||||
doomcom->numslots = (INT16)(newplayernum+1);
|
||||
|
||||
playernode[newplayernum] = servernode;
|
||||
|
||||
// this will permit unlocks
|
||||
memcpy(&players[newplayernum].availabilities, R_GetSkinAvailabilities(false, true), MAXAVAILABILITY*sizeof(UINT8));
|
||||
|
||||
players[newplayernum].splitscreenindex = 0;
|
||||
players[newplayernum].bot = true;
|
||||
players[newplayernum].botvars.difficulty = difficulty;
|
||||
players[newplayernum].botvars.style = style;
|
||||
players[newplayernum].lives = 9;
|
||||
|
||||
players[newplayernum].skincolor = skins[skinnum].prefcolor;
|
||||
sprintf(player_names[newplayernum], "%s", skins[skinnum].realname);
|
||||
SetPlayerSkinByNum(newplayernum, skinnum);
|
||||
|
||||
playerconsole[newplayernum] = newplayernum;
|
||||
G_BuildLocalSplitscreenParty(newplayernum);
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false);
|
||||
}
|
||||
|
||||
LUA_HookInt(newplayernum, HOOK(PlayerJoin));
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
||||
{
|
||||
UINT8 newplayernum = *p;
|
||||
|
||||
for (; newplayernum < MAXPLAYERS; newplayernum++)
|
||||
{
|
||||
if (playeringame[newplayernum] == false)
|
||||
{
|
||||
// free player slot
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newplayernum >= MAXPLAYERS)
|
||||
{
|
||||
// nothing is free
|
||||
*p = MAXPLAYERS;
|
||||
return false;
|
||||
}
|
||||
|
||||
K_SetBot(newplayernum, skin, difficulty, style);
|
||||
DEBFILE(va("Everyone added bot %d\n", newplayernum));
|
||||
|
||||
// use the next free slot
|
||||
*p = newplayernum+1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p)
|
||||
{
|
||||
UINT8 buf[3];
|
||||
UINT8 *buf_p = buf;
|
||||
UINT8 newplayernum = *p;
|
||||
|
||||
// search for a free playernum
|
||||
|
|
@ -54,56 +136,66 @@ boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *p)
|
|||
UINT8 n;
|
||||
|
||||
for (n = 0; n < MAXNETNODES; n++)
|
||||
{
|
||||
if (nodetoplayer[n] == newplayernum
|
||||
|| nodetoplayer2[n] == newplayernum
|
||||
|| nodetoplayer3[n] == newplayernum
|
||||
|| nodetoplayer4[n] == newplayernum)
|
||||
break;
|
||||
}
|
||||
|
||||
if (n == MAXNETNODES)
|
||||
break;
|
||||
}
|
||||
|
||||
while (playeringame[newplayernum]
|
||||
&& players[newplayernum].bot
|
||||
&& newplayernum < MAXPLAYERS)
|
||||
for (; newplayernum < MAXPLAYERS; newplayernum++)
|
||||
{
|
||||
newplayernum++;
|
||||
if (playeringame[newplayernum] == false)
|
||||
{
|
||||
// free player slot
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newplayernum >= MAXPLAYERS)
|
||||
{
|
||||
*p = newplayernum;
|
||||
// nothing is free
|
||||
*p = MAXPLAYERS;
|
||||
return false;
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, newplayernum);
|
||||
|
||||
if (skin > numskins)
|
||||
if (server)
|
||||
{
|
||||
skin = numskins;
|
||||
UINT8 buf[4];
|
||||
UINT8 *buf_p = buf;
|
||||
|
||||
WRITEUINT8(buf_p, newplayernum);
|
||||
|
||||
if (skin > numskins)
|
||||
{
|
||||
skin = numskins;
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, skin);
|
||||
|
||||
if (difficulty < 1)
|
||||
{
|
||||
difficulty = 1;
|
||||
}
|
||||
else if (difficulty > MAXBOTDIFFICULTY)
|
||||
{
|
||||
difficulty = MAXBOTDIFFICULTY;
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, difficulty);
|
||||
WRITEUINT8(buf_p, style);
|
||||
|
||||
SendNetXCmd(XD_ADDBOT, buf, buf_p - buf);
|
||||
DEBFILE(va("Server added bot %d\n", newplayernum));
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, skin);
|
||||
|
||||
if (difficulty < 1)
|
||||
{
|
||||
difficulty = 1;
|
||||
}
|
||||
else if (difficulty > MAXBOTDIFFICULTY)
|
||||
{
|
||||
difficulty = MAXBOTDIFFICULTY;
|
||||
}
|
||||
|
||||
WRITEUINT8(buf_p, difficulty);
|
||||
|
||||
SendNetXCmd(XD_ADDBOT, buf, buf_p - buf);
|
||||
|
||||
DEBFILE(va("Server added bot %d\n", newplayernum));
|
||||
// use the next free slot (we can't put playeringame[newplayernum] = true here)
|
||||
newplayernum++;
|
||||
|
||||
*p = newplayernum;
|
||||
*p = newplayernum+1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -125,11 +217,6 @@ void K_UpdateMatchRaceBots(void)
|
|||
UINT8 grabskins[MAXSKINS+1];
|
||||
UINT8 i;
|
||||
|
||||
if (!server)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Init usable bot skins list
|
||||
for (i = 0; i < numskins; i++)
|
||||
{
|
||||
|
|
@ -156,6 +243,9 @@ void K_UpdateMatchRaceBots(void)
|
|||
|
||||
// While we're here, we should update bot difficulty to the proper value.
|
||||
players[i].botvars.difficulty = difficulty;
|
||||
|
||||
// Enforce normal style for Match Race
|
||||
players[i].botvars.style = BOT_STYLE_NORMAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -169,12 +259,16 @@ void K_UpdateMatchRaceBots(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (difficulty == 0 || !(gametyperules & GTR_BOTS))
|
||||
if (K_CanChangeRules(true) == false
|
||||
|| (gametyperules & GTR_BOTS) == 0
|
||||
|| difficulty == 0)
|
||||
{
|
||||
// Remove bots if there are any.
|
||||
wantedbots = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add bots to fill up MAXPLAYERS
|
||||
wantedbots = pmax - numplayers - numwaiting;
|
||||
|
||||
if (wantedbots < 0)
|
||||
|
|
@ -197,11 +291,15 @@ void K_UpdateMatchRaceBots(void)
|
|||
for (i = 0; i < usableskins; i++)
|
||||
{
|
||||
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||
{
|
||||
usableskins--;
|
||||
}
|
||||
|
||||
grabskins[i] = grabskins[usableskins];
|
||||
grabskins[usableskins] = MAXSKINS;
|
||||
}
|
||||
|
|
@ -212,12 +310,12 @@ void K_UpdateMatchRaceBots(void)
|
|||
|
||||
if (usableskins > 0)
|
||||
{
|
||||
UINT8 index = M_RandomKey(usableskins);
|
||||
UINT8 index = P_RandomKey(PR_BOTS, usableskins);
|
||||
skinnum = grabskins[index];
|
||||
grabskins[index] = grabskins[--usableskins];
|
||||
}
|
||||
|
||||
if (!K_AddBot(skinnum, difficulty, &newplayernum))
|
||||
if (!K_AddBot(skinnum, difficulty, BOT_STYLE_NORMAL, &newplayernum))
|
||||
{
|
||||
// Not enough player slots to add the bot, break the loop.
|
||||
break;
|
||||
|
|
@ -228,26 +326,25 @@ void K_UpdateMatchRaceBots(void)
|
|||
}
|
||||
else if (numbots > wantedbots)
|
||||
{
|
||||
UINT8 buf[2];
|
||||
|
||||
i = MAXPLAYERS;
|
||||
|
||||
while (numbots > wantedbots && i > 0)
|
||||
{
|
||||
i--;
|
||||
|
||||
if (playeringame[i] && players[i].bot)
|
||||
{
|
||||
buf[0] = i;
|
||||
buf[1] = KR_LEAVE;
|
||||
SendNetXCmd(XD_REMOVEPLAYER, &buf, 2);
|
||||
|
||||
CL_RemovePlayer(i, KR_LEAVE);
|
||||
numbots--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We should have enough bots now :)
|
||||
|
||||
#ifdef HAVE_DISCORDRPC
|
||||
// Player count change was possible, so update presence
|
||||
DRPC_UpdatePresence();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
@ -1473,11 +1570,11 @@ static void K_BuildBotPodiumTiccmd(player_t *player, ticcmd_t *cmd)
|
|||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||
|
||||
See header file for description.
|
||||
Build ticcmd for bots with a style of BOT_STYLE_NORMAL
|
||||
--------------------------------------------------*/
|
||||
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
|
||||
{
|
||||
precise_t t = 0;
|
||||
botprediction_t *predict = NULL;
|
||||
|
|
@ -1487,29 +1584,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
|||
INT32 turnamt = 0;
|
||||
const line_t *botController = player->botvars.controller != UINT16_MAX ? &lines[player->botvars.controller] : NULL;
|
||||
|
||||
// Remove any existing controls
|
||||
memset(cmd, 0, sizeof(ticcmd_t));
|
||||
|
||||
if (player->mo == NULL
|
||||
|| player->spectator == true
|
||||
|| G_GamestateUsesLevel() == false)
|
||||
{
|
||||
// Not in the level.
|
||||
return;
|
||||
}
|
||||
|
||||
// Complete override of all ticcmd functionality
|
||||
if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (K_PodiumSequence() == true)
|
||||
{
|
||||
K_BuildBotPodiumTiccmd(player, cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(gametyperules & GTR_BOTS) // No bot behaviors
|
||||
|| K_GetNumWaypoints() == 0 // No waypoints
|
||||
|| leveltime <= introtime // During intro camera
|
||||
|
|
@ -1762,6 +1836,54 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
|||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
{
|
||||
// Remove any existing controls
|
||||
memset(cmd, 0, sizeof(ticcmd_t));
|
||||
|
||||
if (player->mo == NULL
|
||||
|| player->spectator == true
|
||||
|| G_GamestateUsesLevel() == false)
|
||||
{
|
||||
// Not in the level.
|
||||
return;
|
||||
}
|
||||
|
||||
// Complete override of all ticcmd functionality.
|
||||
// May add more hooks to individual pieces of bot ticcmd,
|
||||
// but this should always be here so anyone can roll
|
||||
// their own :)
|
||||
if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)) == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (K_PodiumSequence() == true)
|
||||
{
|
||||
K_BuildBotPodiumTiccmd(player, cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (player->botvars.style)
|
||||
{
|
||||
case BOT_STYLE_STAY:
|
||||
{
|
||||
// Hey, this one's pretty easy :P
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
K_BuildBotTiccmdNormal(player, cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_UpdateBotGameplayVars(player_t *player);
|
||||
|
||||
|
|
|
|||
56
src/k_bot.h
56
src/k_bot.h
|
|
@ -138,25 +138,67 @@ fixed_t K_UpdateRubberband(player_t *player);
|
|||
fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy);
|
||||
|
||||
|
||||
// NOT AVAILABLE FOR LUA
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum);
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p);
|
||||
|
||||
Returns the waypoint actually being used as the finish line.
|
||||
Adds a new bot, using code intended to run on all clients.
|
||||
|
||||
Input Arguments:-
|
||||
skin - Skin number that the bot will use.
|
||||
difficulty - Difficulty level this bot will use.
|
||||
style - Bot style to spawn this bot with, see botStyle_e.
|
||||
newplayernum - Pointer to the last valid player slot number.
|
||||
Is a pointer so that this function can be called multiple times to add more than one bot.
|
||||
|
||||
Return:-
|
||||
true if a bot packet can be sent, otherwise false.
|
||||
true if a bot was added, otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, UINT8 *newplayernum);
|
||||
boolean K_AddBot(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p);
|
||||
|
||||
|
||||
// NOT AVAILABLE FOR LUA
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e style);
|
||||
|
||||
Sets a player ID to be a new bot directly. Invoked directly
|
||||
by K_AddBot, and indirectly by K_AddBotFromServer by sending
|
||||
a packet.
|
||||
|
||||
Input Arguments:-
|
||||
newplayernum - Player slot number to set as a bot.
|
||||
skin - Skin number that the bot will use.
|
||||
difficulty - Difficulty level this bot will use.
|
||||
style - Bot style to spawn this bot with, see botStyle_e.
|
||||
|
||||
Return:-
|
||||
None
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e style);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *newplayernum);
|
||||
|
||||
Adds a new bot, using a server-sided packet sent to all clients.
|
||||
Using regular K_AddBot wherever possible is better, but this is kept
|
||||
as a back-up measure if this is the only option.
|
||||
|
||||
Input Arguments:-
|
||||
skin - Skin number that the bot will use.
|
||||
difficulty - Difficulty level this bot will use.
|
||||
style - Bot style to spawn this bot with, see botStyle_e.
|
||||
newplayernum - Pointer to the last valid player slot number.
|
||||
Is a pointer so that this function can be called multiple times to add more than one bot.
|
||||
|
||||
Return:-
|
||||
true if a bot can be added via a packet later, otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_AddBotFromServer(UINT8 skin, UINT8 difficulty, botStyle_e style, UINT8 *p);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -186,6 +186,13 @@ void K_InitGrandPrixBots(void)
|
|||
{
|
||||
if (playeringame[i])
|
||||
{
|
||||
if (players[i].bot == true)
|
||||
{
|
||||
// Remove existing bots.
|
||||
CL_RemovePlayer(i, KR_LEAVE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (numplayers < MAXSPLITSCREENPLAYERS && !players[i].spectator)
|
||||
{
|
||||
competitors[numplayers] = i;
|
||||
|
|
@ -227,11 +234,15 @@ void K_InitGrandPrixBots(void)
|
|||
for (i = 0; i < usableskins; i++)
|
||||
{
|
||||
if (!(grabskins[i] == MAXSKINS || !R_SkinUsable(-1, grabskins[i], true)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
while (usableskins > i && (grabskins[usableskins] == MAXSKINS || !R_SkinUsable(-1, grabskins[usableskins], true)))
|
||||
{
|
||||
usableskins--;
|
||||
}
|
||||
|
||||
grabskins[i] = grabskins[usableskins];
|
||||
grabskins[usableskins] = MAXSKINS;
|
||||
}
|
||||
|
|
@ -245,7 +256,7 @@ void K_InitGrandPrixBots(void)
|
|||
|
||||
if (usableskins > 0)
|
||||
{
|
||||
UINT8 index = M_RandomKey(usableskins);
|
||||
UINT8 index = P_RandomKey(PR_BOTS, usableskins);
|
||||
skinnum = grabskins[index];
|
||||
grabskins[index] = grabskins[--usableskins];
|
||||
}
|
||||
|
|
@ -256,13 +267,51 @@ void K_InitGrandPrixBots(void)
|
|||
|
||||
for (i = 0; i < wantedbots; i++)
|
||||
{
|
||||
if (!K_AddBot(botskinlist[i], difficultylevels[i], &newplayernum))
|
||||
if (!K_AddBot(botskinlist[i], difficultylevels[i], BOT_STYLE_NORMAL, &newplayernum))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_LoadGrandPrixSaveGame(void)
|
||||
|
||||
See header file for description.
|
||||
---------------------------------------------------*/
|
||||
|
||||
void K_LoadGrandPrixSaveGame(void)
|
||||
{
|
||||
if (splitscreen)
|
||||
{
|
||||
// You're not doing splitscreen runs at GDQ.
|
||||
// We are literally 14 days from code freeze
|
||||
// and I am not accomodating weird setup this
|
||||
// second in my last minute QoL feature.
|
||||
// I will *actually* fight you
|
||||
return;
|
||||
}
|
||||
|
||||
players[consoleplayer].lives = savedata.lives;
|
||||
players[consoleplayer].score = savedata.score;
|
||||
players[consoleplayer].totalring = savedata.totalring;
|
||||
|
||||
UINT8 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (savedata.bots[i].valid == false)
|
||||
continue;
|
||||
|
||||
K_SetBot(i, savedata.bots[i].skin, savedata.bots[i].difficulty, BOT_STYLE_NORMAL);
|
||||
|
||||
players[i].botvars.rival = savedata.bots[i].rival;
|
||||
players[i].score = savedata.bots[i].score;
|
||||
|
||||
players[i].spectator = !(gametyperules & GTR_BOTS) || (grandprixinfo.eventmode != GPEVENT_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static INT16 K_RivalScore(player_t *bot)
|
||||
|
||||
|
|
@ -327,8 +376,10 @@ void K_UpdateGrandPrixBots(void)
|
|||
UINT16 newrivalscore = 0;
|
||||
UINT8 i;
|
||||
|
||||
if (K_PodiumSequence())
|
||||
if (K_PodiumSequence() == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ typedef enum
|
|||
GPEVENT_SPECIAL,
|
||||
} gpEvent_e;
|
||||
|
||||
// Please also see P_ArchiveMisc
|
||||
extern struct grandprixinfo
|
||||
{
|
||||
boolean gp; ///< If true, then we are in a Grand Prix.
|
||||
|
|
@ -100,6 +101,15 @@ UINT8 K_GetGPPlayerCount(UINT8 humans);
|
|||
void K_InitGrandPrixBots(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_LoadGrandPrixSaveGame(void)
|
||||
|
||||
Handles loading savedata_t info for Grand Prix context.
|
||||
---------------------------------------------------*/
|
||||
|
||||
void K_LoadGrandPrixSaveGame(void);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_UpdateGrandPrixBots(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -5156,7 +5156,7 @@ static void K_DrawWaypointDebugger(void)
|
|||
}
|
||||
|
||||
V_DrawString(8, 156, 0, va("Current Waypoint ID: %d", K_GetWaypointID(stplyr->currentwaypoint)));
|
||||
V_DrawString(8, 166, 0, va("Next Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint)));
|
||||
V_DrawString(8, 166, 0, va("Next Waypoint ID: %d%s", K_GetWaypointID(stplyr->nextwaypoint), ((stplyr->pflags & PF_WRONGWAY) ? " (WRONG WAY)" : "")));
|
||||
V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish));
|
||||
|
||||
if (numstarposts > 0)
|
||||
|
|
@ -5525,11 +5525,6 @@ void K_drawKartHUD(void)
|
|||
// Draw FREE PLAY.
|
||||
K_drawKartFreePlay();
|
||||
|
||||
if (r_splitscreen == 0 && (stplyr->pflags & PF_WRONGWAY) && ((leveltime / 8) & 1))
|
||||
{
|
||||
V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY");
|
||||
}
|
||||
|
||||
if ((netgame || cv_mindelay.value) && r_splitscreen && Playing())
|
||||
{
|
||||
K_drawMiniPing();
|
||||
|
|
|
|||
62
src/k_kart.c
62
src/k_kart.c
|
|
@ -3302,6 +3302,7 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed)
|
|||
fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberband)
|
||||
{
|
||||
const boolean mobjValid = (player->mo != NULL && P_MobjWasRemoved(player->mo) == false);
|
||||
const fixed_t physicsScale = mobjValid ? K_GrowShrinkSpeedMul(player) : FRACUNIT;
|
||||
fixed_t finalspeed = 0;
|
||||
|
||||
if (K_PodiumSequence() == true)
|
||||
|
|
@ -3338,22 +3339,26 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberb
|
|||
|
||||
finalspeed = FixedMul(finalspeed, mapobjectscale);
|
||||
|
||||
if (doboostpower == true)
|
||||
{
|
||||
if (mobjValid == true)
|
||||
{
|
||||
// Scale with the player.
|
||||
finalspeed = FixedMul(finalspeed, K_GrowShrinkSpeedMul(player));
|
||||
}
|
||||
|
||||
finalspeed = FixedMul(finalspeed, player->boostpower + player->speedboost);
|
||||
}
|
||||
|
||||
if (dorubberband == true && K_PlayerUsesBotMovement(player) == true)
|
||||
{
|
||||
finalspeed = FixedMul(finalspeed, player->botvars.rubberband);
|
||||
}
|
||||
|
||||
if (doboostpower == true)
|
||||
{
|
||||
// Scale with the player.
|
||||
finalspeed = FixedMul(finalspeed, physicsScale);
|
||||
|
||||
// Add speed boosts.
|
||||
finalspeed = FixedMul(finalspeed, player->boostpower + player->speedboost);
|
||||
}
|
||||
|
||||
if (player->outrun != 0)
|
||||
{
|
||||
// Milky Way's roads
|
||||
finalspeed += FixedMul(player->outrun, physicsScale);
|
||||
}
|
||||
|
||||
return finalspeed;
|
||||
}
|
||||
|
||||
|
|
@ -3370,18 +3375,22 @@ fixed_t K_GetKartAccel(player_t *player)
|
|||
|
||||
k_accel += 17 * stat; // 121 - 257
|
||||
|
||||
if (K_PodiumSequence() == true)
|
||||
{
|
||||
return FixedMul(k_accel, FRACUNIT / 4);
|
||||
}
|
||||
|
||||
// Marble Garden Top gets 1200% accel
|
||||
if (player->curshield == KSHIELD_TOP)
|
||||
{
|
||||
k_accel *= 12;
|
||||
}
|
||||
|
||||
return FixedMul(k_accel, (FRACUNIT + player->accelboost) / 4);
|
||||
if (K_PodiumSequence() == true)
|
||||
{
|
||||
k_accel = FixedMul(k_accel, FRACUNIT / 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
k_accel = FixedMul(k_accel, (FRACUNIT + player->accelboost) / 4);
|
||||
}
|
||||
|
||||
return k_accel;
|
||||
}
|
||||
|
||||
UINT16 K_GetKartFlashing(player_t *player)
|
||||
|
|
@ -8276,9 +8285,14 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->tripwireState = TRIPSTATE_NONE;
|
||||
}
|
||||
|
||||
if (player->hand && P_MobjWasRemoved(player->hand))
|
||||
P_SetTarget(&player->hand, NULL);
|
||||
|
||||
if (player->spectator == false)
|
||||
{
|
||||
K_KartEbrakeVisuals(player);
|
||||
|
||||
Obj_ServantHandHandling(player);
|
||||
}
|
||||
|
||||
if (K_GetKartButtons(player) & BT_BRAKE &&
|
||||
|
|
@ -8597,6 +8611,7 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player)
|
|||
{
|
||||
angle_t nextbestdelta = ANGLE_90;
|
||||
angle_t nextbestmomdelta = ANGLE_90;
|
||||
angle_t nextbestanydelta = ANGLE_MAX;
|
||||
size_t i = 0U;
|
||||
|
||||
if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U))
|
||||
|
|
@ -8638,8 +8653,14 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player)
|
|||
momdelta = InvAngle(momdelta);
|
||||
}
|
||||
|
||||
if (angledelta < nextbestdelta || momdelta < nextbestmomdelta)
|
||||
if (angledelta < nextbestanydelta || momdelta < nextbestanydelta)
|
||||
{
|
||||
nextbestanydelta = min(angledelta, momdelta);
|
||||
player->besthanddirection = angletowaypoint;
|
||||
|
||||
if (nextbestanydelta >= ANGLE_90)
|
||||
continue;
|
||||
|
||||
// Wanted to use a next waypoint, so remove WRONG WAY flag.
|
||||
// Done here instead of when set, because of finish line
|
||||
// hacks meaning we might not actually use this one, but
|
||||
|
|
@ -10780,6 +10801,11 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
whip->fuse = 12; // Changing instawhip animation duration? Look here
|
||||
player->flashing = max(player->flashing, 12);
|
||||
player->mo->momz += 4*mapobjectscale;
|
||||
|
||||
// Spawn in triangle formation
|
||||
Obj_SpawnInstaWhipRecharge(player, 0);
|
||||
Obj_SpawnInstaWhipRecharge(player, ANGLE_120);
|
||||
Obj_SpawnInstaWhipRecharge(player, ANGLE_240);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "command.h"
|
||||
#include "doomstat.h" // MAXSPLITSCREENPLAYERS
|
||||
#include "g_demo.h" //menudemo_t
|
||||
#include "p_saveg.h" // savedata_cup_t
|
||||
#include "k_profiles.h" // profile data & functions
|
||||
#include "g_input.h" // gc_
|
||||
#include "i_threads.h"
|
||||
|
|
@ -517,10 +518,11 @@ typedef enum
|
|||
} manswer_e;
|
||||
|
||||
#define MAXMENUMESSAGE 256
|
||||
#define MENUMESSAGECLOSE 2
|
||||
extern struct menumessage_s
|
||||
{
|
||||
boolean active;
|
||||
boolean closing;
|
||||
UINT8 closing;
|
||||
|
||||
INT32 flags; // MM_
|
||||
const char *header;
|
||||
|
|
@ -533,6 +535,7 @@ extern struct menumessage_s
|
|||
|
||||
void (*routine)(INT32 choice); // Normal routine
|
||||
//void (*eroutine)(event_t *ev); // Event routine (MM_EVENTHANDLER)
|
||||
INT32 answer;
|
||||
|
||||
const char *defaultstr;
|
||||
const char *confirmstr;
|
||||
|
|
@ -599,7 +602,7 @@ void M_SetMenuDelay(UINT8 i);
|
|||
|
||||
void M_SortServerList(void);
|
||||
|
||||
void M_UpdateMenuCMD(UINT8 i);
|
||||
void M_UpdateMenuCMD(UINT8 i, boolean bailrequired);
|
||||
boolean M_Responder(event_t *ev);
|
||||
boolean M_MenuButtonPressed(UINT8 pid, UINT32 bt);
|
||||
boolean M_MenuButtonHeld(UINT8 pid, UINT32 bt);
|
||||
|
|
|
|||
|
|
@ -606,15 +606,15 @@ static void M_DrawMenuTyping(void)
|
|||
// Draw the message popup submenu
|
||||
void M_DrawMenuMessage(void)
|
||||
{
|
||||
if (!menumessage.active)
|
||||
return;
|
||||
|
||||
INT32 x = (BASEVIDWIDTH - menumessage.x)/2;
|
||||
INT32 y = (BASEVIDHEIGHT - menumessage.y)/2 + floor(pow(2, (double)(9 - menumessage.fadetimer)));
|
||||
size_t i, start = 0;
|
||||
char string[MAXMENUMESSAGE];
|
||||
const char *msg = menumessage.message;
|
||||
|
||||
if (!menumessage.active)
|
||||
return;
|
||||
|
||||
V_DrawFadeScreen(31, menumessage.fadetimer);
|
||||
|
||||
V_DrawFill(0, y, BASEVIDWIDTH, menumessage.y, 159);
|
||||
|
|
@ -629,25 +629,64 @@ void M_DrawMenuMessage(void)
|
|||
INT32 workx = x + menumessage.x;
|
||||
INT32 worky = y + menumessage.y;
|
||||
|
||||
boolean push;
|
||||
|
||||
if (menumessage.closing)
|
||||
push = (menumessage.answer != MA_YES);
|
||||
else
|
||||
{
|
||||
const UINT8 anim_duration = 16;
|
||||
push = ((menumessage.timer % (anim_duration * 2)) < anim_duration);
|
||||
}
|
||||
|
||||
workx -= V_ThinStringWidth(menumessage.defaultstr, V_6WIDTHSPACE|V_ALLOWLOWERCASE);
|
||||
V_DrawThinString(workx, worky + 1, V_6WIDTHSPACE|V_ALLOWLOWERCASE, menumessage.defaultstr);
|
||||
V_DrawThinString(
|
||||
workx, worky + 1,
|
||||
V_6WIDTHSPACE|V_ALLOWLOWERCASE
|
||||
| ((push && (menumessage.closing & MENUMESSAGECLOSE)) ? highlightflags : 0),
|
||||
menumessage.defaultstr
|
||||
);
|
||||
|
||||
workx -= 2;
|
||||
|
||||
workx -= SHORT(kp_button_x[1][0]->width);
|
||||
K_drawButtonAnim(workx, worky, 0, kp_button_x[1], menumessage.timer);
|
||||
K_drawButton(
|
||||
workx * FRACUNIT, worky * FRACUNIT,
|
||||
0, kp_button_x[1],
|
||||
push
|
||||
);
|
||||
|
||||
workx -= SHORT(kp_button_b[1][0]->width);
|
||||
K_drawButtonAnim(workx, worky, 0, kp_button_b[1], menumessage.timer);
|
||||
K_drawButton(
|
||||
workx * FRACUNIT, worky * FRACUNIT,
|
||||
0, kp_button_b[1],
|
||||
push
|
||||
);
|
||||
|
||||
if (menumessage.confirmstr)
|
||||
{
|
||||
workx -= 12;
|
||||
|
||||
if (menumessage.closing)
|
||||
push = !push;
|
||||
|
||||
workx -= V_ThinStringWidth(menumessage.confirmstr, V_6WIDTHSPACE|V_ALLOWLOWERCASE);
|
||||
V_DrawThinString(workx, worky + 1, V_6WIDTHSPACE|V_ALLOWLOWERCASE, menumessage.confirmstr);
|
||||
V_DrawThinString(
|
||||
workx, worky + 1,
|
||||
V_6WIDTHSPACE|V_ALLOWLOWERCASE
|
||||
| ((push && (menumessage.closing & MENUMESSAGECLOSE)) ? highlightflags : 0),
|
||||
menumessage.confirmstr
|
||||
);
|
||||
|
||||
workx -= 2;
|
||||
}
|
||||
|
||||
workx -= SHORT(kp_button_a[1][0]->width);
|
||||
K_drawButtonAnim(workx, worky, 0, kp_button_a[1], menumessage.timer);
|
||||
K_drawButton(
|
||||
workx * FRACUNIT, worky * FRACUNIT,
|
||||
0, kp_button_a[1],
|
||||
push
|
||||
);
|
||||
}
|
||||
|
||||
x -= 4;
|
||||
|
|
@ -2329,6 +2368,7 @@ static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch)
|
|||
void M_DrawCupSelect(void)
|
||||
{
|
||||
UINT8 i, j, temp = 0;
|
||||
INT16 x, y;
|
||||
UINT8 *colormap = NULL;
|
||||
cupwindata_t *windata = NULL;
|
||||
levelsearch_t templevelsearch = levellist.levelsearch; // full copy
|
||||
|
|
@ -2339,7 +2379,6 @@ void M_DrawCupSelect(void)
|
|||
{
|
||||
size_t id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS));
|
||||
patch_t *patch = NULL;
|
||||
INT16 x, y;
|
||||
INT16 icony = 7;
|
||||
char status = 'A';
|
||||
char monitor;
|
||||
|
|
@ -2424,6 +2463,13 @@ void M_DrawCupSelect(void)
|
|||
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(templevelsearch.cup->icon, PU_CACHE));
|
||||
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE));
|
||||
|
||||
if (cupgrid.grandprix == true
|
||||
&& templevelsearch.cup == cupsavedata.cup
|
||||
&& id != CUPMENU_CURSORID)
|
||||
{
|
||||
V_DrawScaledPatch(x + 32, y + 32, 0, W_CachePatchName("CUPBKUP1", PU_CACHE));
|
||||
}
|
||||
|
||||
if (!windata)
|
||||
;
|
||||
else if (windata->best_placement != 0)
|
||||
|
|
@ -2523,17 +2569,38 @@ void M_DrawCupSelect(void)
|
|||
}
|
||||
}
|
||||
|
||||
V_DrawScaledPatch(14 + (cupgrid.x*42) - 4,
|
||||
20 + (cupgrid.y*44) - 1 - (24*menutransition.tics),
|
||||
0, W_CachePatchName("CUPCURS", PU_CACHE)
|
||||
);
|
||||
x = 14 + (cupgrid.x*42);
|
||||
y = 20 + (cupgrid.y*44) - (30*menutransition.tics);
|
||||
|
||||
V_DrawScaledPatch(x - 4, y - 1, 0, W_CachePatchName("CUPCURS", PU_CACHE));
|
||||
|
||||
templevelsearch.cup = cupgrid.builtgrid[CUPMENU_CURSORID];
|
||||
|
||||
if (cupgrid.grandprix == true
|
||||
&& templevelsearch.cup != NULL
|
||||
&& templevelsearch.cup == cupsavedata.cup)
|
||||
{
|
||||
V_DrawScaledPatch(x + 32, y + 32, 0, W_CachePatchName("CUPBKUP2", PU_CACHE));
|
||||
}
|
||||
|
||||
V_DrawFill(0, 146 + (24*menutransition.tics), BASEVIDWIDTH, 54, 31);
|
||||
M_DrawCupPreview(146 + (24*menutransition.tics), &templevelsearch);
|
||||
|
||||
M_DrawCupTitle(120 - (24*menutransition.tics), &templevelsearch);
|
||||
|
||||
if (cupgrid.numpages > 1)
|
||||
{
|
||||
x = 3 - (skullAnimCounter/5);
|
||||
y = 20 + (44 - 1) - (30*menutransition.tics);
|
||||
|
||||
patch_t *cuparrow = W_CachePatchName("CUPARROW", PU_CACHE);
|
||||
|
||||
if (cupgrid.pageno != 0)
|
||||
V_DrawScaledPatch(x, y, 0, cuparrow);
|
||||
|
||||
if (cupgrid.pageno != cupgrid.numpages-1)
|
||||
V_DrawScaledPatch(BASEVIDWIDTH-x, y, V_FLIP, cuparrow);
|
||||
}
|
||||
}
|
||||
|
||||
static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map)
|
||||
|
|
|
|||
|
|
@ -539,6 +539,21 @@ void M_StartControlPanel(void)
|
|||
// (We can change this timer later when extra animation is added.)
|
||||
if (finalecount < 1)
|
||||
return;
|
||||
|
||||
if (menumessage.active)
|
||||
{
|
||||
if (!menumessage.closing && menumessage.fadetimer == 9)
|
||||
{
|
||||
// The following doesn't work with MM_YESNO.
|
||||
// However, because there's no guarantee a profile
|
||||
// is selected or controls set up to our liking,
|
||||
// we can't call M_HandleMenuMessage.
|
||||
|
||||
M_StopMessage(MA_NONE);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
menuactive = true;
|
||||
|
|
@ -549,8 +564,6 @@ void M_StartControlPanel(void)
|
|||
}
|
||||
else if (!Playing())
|
||||
{
|
||||
M_StopMessage(0); // Doesn't work with MM_YESNO or MM_EVENTHANDLER... but good enough to get the game as it is currently functional again
|
||||
|
||||
if (gamestate != GS_MENU)
|
||||
{
|
||||
G_SetGamestate(GS_MENU);
|
||||
|
|
@ -754,7 +767,7 @@ void M_SetMenuDelay(UINT8 i)
|
|||
}
|
||||
}
|
||||
|
||||
void M_UpdateMenuCMD(UINT8 i)
|
||||
void M_UpdateMenuCMD(UINT8 i, boolean bailrequired)
|
||||
{
|
||||
UINT8 mp = max(1, setup_numplayers);
|
||||
|
||||
|
|
@ -792,6 +805,11 @@ void M_UpdateMenuCMD(UINT8 i)
|
|||
|
||||
if (G_PlayerInputDown(i, gc_start, mp)) { menucmd[i].buttons |= MBT_START; }
|
||||
|
||||
if (bailrequired && i == 0)
|
||||
{
|
||||
if (G_GetDeviceGameKeyDownArray(0)[KEY_ESCAPE]) { menucmd[i].buttons |= MBT_B; }
|
||||
}
|
||||
|
||||
if (menucmd[i].dpad_ud == 0 && menucmd[i].dpad_lr == 0 && menucmd[i].buttons == 0)
|
||||
{
|
||||
// Reset delay count with no buttons.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#ifndef k_objects_H
|
||||
#define k_objects_H
|
||||
|
||||
#include "tables.h" // angle_t
|
||||
#include "taglist.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -109,6 +110,8 @@ boolean Obj_DropTargetMorphThink(mobj_t *morph);
|
|||
|
||||
/* Instawhip */
|
||||
void Obj_InstaWhipThink(mobj_t *whip);
|
||||
void Obj_SpawnInstaWhipRecharge(player_t *player, angle_t angleOffset);
|
||||
void Obj_InstaWhipRechargeThink(mobj_t *mobj);
|
||||
|
||||
/* Block VFX */
|
||||
void Obj_BlockRingThink(mobj_t *ring);
|
||||
|
|
@ -138,6 +141,9 @@ void Obj_RandomItemSpawn(mobj_t *mobj);
|
|||
void Obj_GachaBomReboundThink(mobj_t *mobj);
|
||||
void Obj_SpawnGachaBomRebound(mobj_t *source, mobj_t *target);
|
||||
|
||||
/* Servant Hand */
|
||||
void Obj_ServantHandHandling(player_t *player);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "y_inter.h"
|
||||
#include "m_cond.h"
|
||||
#include "p_local.h"
|
||||
#include "p_saveg.h"
|
||||
#include "p_setup.h"
|
||||
#include "st_stuff.h" // hud hiding
|
||||
#include "fastcmp.h"
|
||||
|
|
@ -252,6 +253,12 @@ boolean K_StartCeremony(void)
|
|||
maptol = mapheaderinfo[gamemap-1]->typeoflevel;
|
||||
globalweather = mapheaderinfo[gamemap-1]->weather;
|
||||
|
||||
if (savedata.lives > 0)
|
||||
{
|
||||
K_LoadGrandPrixSaveGame();
|
||||
savedata.lives = 0;
|
||||
}
|
||||
|
||||
// Make sure all of the GAME OVER'd players can spawn
|
||||
// and be present for the podium
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Please also see P_ArchiveMisc
|
||||
struct gpRank_t
|
||||
{
|
||||
UINT8 players;
|
||||
|
|
|
|||
|
|
@ -455,6 +455,9 @@ void K_ProcessTerrainEffect(mobj_t *mo)
|
|||
return;
|
||||
}
|
||||
|
||||
// Milky Way road effect
|
||||
player->outrun = terrain->outrun;
|
||||
|
||||
// Damage effects
|
||||
if (terrain->damageType > 0)
|
||||
{
|
||||
|
|
@ -1688,6 +1691,10 @@ static void K_ParseTerrainParameter(size_t i, char *param, char *val)
|
|||
{
|
||||
terrain->springStarColor = get_number(val);
|
||||
}
|
||||
else if (stricmp(param, "outrun") == 0 || stricmp(param, "speedIncrease") == 0)
|
||||
{
|
||||
terrain->outrun = FLOAT_TO_FIXED(atof(val));
|
||||
}
|
||||
else if (stricmp(param, "floorClip") == 0)
|
||||
{
|
||||
terrain->floorClip = FLOAT_TO_FIXED(atof(val));
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ struct terrain_t
|
|||
angle_t speedPadAngle; // Speed pad angle
|
||||
fixed_t springStrength; // Spring strength
|
||||
UINT16 springStarColor; // Spring star color
|
||||
fixed_t outrun; // Raise top speed by this amount, for super fast road.
|
||||
fixed_t floorClip; // Offset for sprites on this ground
|
||||
UINT32 flags; // Flag values (see: terrain_flags_t)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -80,31 +80,45 @@ static UINT8 cheatf_warp(void)
|
|||
}
|
||||
}
|
||||
|
||||
M_ClearMenus(true);
|
||||
|
||||
const char *text;
|
||||
|
||||
if (success)
|
||||
{
|
||||
G_SetUsedCheats();
|
||||
M_ClearMenus(true);
|
||||
S_StartSound(0, sfx_kc42);
|
||||
|
||||
M_StartMessage("Tournament Mode",
|
||||
M_GetText(
|
||||
text = M_GetText(
|
||||
"All challenges temporarily unlocked.\n"
|
||||
"Saving is disabled - the game will\n"
|
||||
"return to normal on next launch.\n"
|
||||
), NULL, MM_NOTHING, NULL, NULL);
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
S_StartSound(0, sfx_s3k7b);
|
||||
|
||||
M_StartMessage("Tournament Mode",
|
||||
M_GetText(
|
||||
"This is the correct password, but\n"
|
||||
"you already have every challenge\n"
|
||||
"unlocked, so saving is still allowed!\n"
|
||||
), NULL, MM_NOTHING, NULL, NULL);
|
||||
if (usedCheats)
|
||||
{
|
||||
text = M_GetText(
|
||||
"This is the correct password, but\n"
|
||||
"you already have every challenge\n"
|
||||
"unlocked, so nothing has changed.\n"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = M_GetText(
|
||||
"This is the correct password, but\n"
|
||||
"you already have every challenge\n"
|
||||
"unlocked, so saving is still allowed!\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
M_StartMessage("Tournament Mode", text, NULL, MM_NOTHING, NULL, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ typedef enum
|
|||
// However each instance of RNG being used for
|
||||
// gameplay should be split up as much as possible.
|
||||
|
||||
// Place new ones at the end for demo compatibility.
|
||||
|
||||
PR_EXECUTOR, // Linedef executor
|
||||
PR_ACS, // ACS scripts
|
||||
|
||||
|
|
@ -72,6 +74,8 @@ typedef enum
|
|||
|
||||
PR_MOVINGTARGET, // Randomised moving targets
|
||||
|
||||
PR_BOTS, // Bot spawning
|
||||
|
||||
PRNUMCLASS
|
||||
} pr_class_t;
|
||||
|
||||
|
|
|
|||
|
|
@ -101,11 +101,12 @@ static boolean prevmajormods = false;
|
|||
|
||||
static void M_AddonsClearName(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
||||
if (!majormods || prevmajormods)
|
||||
{
|
||||
CLEARNAME;
|
||||
}
|
||||
M_StopMessage(choice);
|
||||
}
|
||||
|
||||
// Handles messages for addon errors.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ menuitem_t OPTIONS_GameplayItems[] =
|
|||
|
||||
{IT_KEYHANDLER | IT_NOTHING, NULL, "Banana", NULL, {.routine = M_HandleItemToggles}, KITEM_BANANA, 0},
|
||||
{IT_KEYHANDLER | IT_NOTHING, NULL, "Banana x3", NULL, {.routine = M_HandleItemToggles}, KRITEM_TRIPLEBANANA, 0},
|
||||
{IT_KEYHANDLER | IT_NOTHING, NULL, "Eggman Mark", NULL, {.routine = M_HandleItemToggles}, KITEM_EGGMAN, 0},
|
||||
{IT_KEYHANDLER | IT_NOTHING, NULL, "Eggmark", NULL, {.routine = M_HandleItemToggles}, KITEM_EGGMAN, 0},
|
||||
{IT_KEYHANDLER | IT_NOTHING, NULL, "Gachabom", NULL, {.routine = M_HandleItemToggles}, KITEM_GACHABOM, 0},
|
||||
|
||||
{IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinaut", NULL, {.routine = M_HandleItemToggles}, KITEM_ORBINAUT, 0},
|
||||
|
|
|
|||
|
|
@ -484,6 +484,7 @@ void M_CharacterSelectInit(void)
|
|||
setup_page = 0;
|
||||
}
|
||||
|
||||
|
||||
void M_CharacterSelect(INT32 choice)
|
||||
{
|
||||
(void)choice;
|
||||
|
|
|
|||
|
|
@ -414,7 +414,6 @@ static void M_WriteGuestReplay(INT32 ch)
|
|||
|
||||
if (FIL_FileExists(rguest))
|
||||
{
|
||||
//M_StopMessage(0);
|
||||
remove(rguest);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#include "../../v_video.h"
|
||||
#include "../../k_grandprix.h"
|
||||
#include "../../r_local.h" // SplitScreen_OnChange
|
||||
#include "../../k_podium.h" // K_StartCeremony
|
||||
#include "../../m_misc.h" // FIL_FileExists
|
||||
#include "../../d_main.h" // D_ClearState
|
||||
|
||||
menuitem_t PLAY_CupSelect[] =
|
||||
{
|
||||
|
|
@ -32,6 +35,150 @@ menu_t PLAY_CupSelectDef = {
|
|||
|
||||
struct cupgrid_s cupgrid;
|
||||
|
||||
static void M_StartCup(UINT8 entry)
|
||||
{
|
||||
UINT8 ssplayers = cv_splitplayers.value-1;
|
||||
|
||||
if (ssplayers > 0)
|
||||
{
|
||||
// Splitscreen is not accomodated with this recovery feature.
|
||||
entry = 0;
|
||||
}
|
||||
|
||||
S_StartSound(NULL, sfx_s3k63);
|
||||
|
||||
paused = false;
|
||||
|
||||
S_StopMusicCredit();
|
||||
|
||||
// Early fadeout to let the sound finish playing
|
||||
F_WipeStartScreen();
|
||||
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
|
||||
F_WipeEndScreen();
|
||||
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
|
||||
|
||||
if (cv_maxconnections.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxconnections, ssplayers+1);
|
||||
|
||||
if (splitscreen != ssplayers)
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
|
||||
if (entry == 0)
|
||||
{
|
||||
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
|
||||
|
||||
// read our dummy cvars
|
||||
|
||||
grandprixinfo.gamespeed = min(KARTSPEED_HARD, cv_dummygpdifficulty.value);
|
||||
grandprixinfo.masterbots = (cv_dummygpdifficulty.value == 3);
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.initalize = true;
|
||||
grandprixinfo.cup = levellist.levelsearch.cup;
|
||||
|
||||
// Populate the roundqueue
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
G_GPCupIntoRoundQueue(levellist.levelsearch.cup, levellist.newgametype, (boolean)cv_dummygpencore.value);
|
||||
roundqueue.position = roundqueue.roundnum = 1;
|
||||
roundqueue.netcommunicate = true; // relevant for future Online GP
|
||||
}
|
||||
else
|
||||
{
|
||||
// Silently change player setup
|
||||
{
|
||||
CV_StealthSetValue(&cv_playercolor[0], savedata.skincolor);
|
||||
|
||||
// follower
|
||||
if (savedata.followerskin < 0 || savedata.followerskin >= numfollowers)
|
||||
CV_StealthSet(&cv_follower[0], "None");
|
||||
else
|
||||
CV_StealthSet(&cv_follower[0], followers[savedata.followerskin].name);
|
||||
|
||||
// finally, call the skin[x] console command.
|
||||
// This will call SendNameAndColor which will synch everything we sent here and apply the changes!
|
||||
|
||||
CV_StealthSet(&cv_skin[0], skins[savedata.skin].name);
|
||||
|
||||
// ...actually, let's do this last - Skin_OnChange has some return-early occasions
|
||||
// follower color
|
||||
CV_SetValue(&cv_followercolor[0], savedata.followercolor);
|
||||
}
|
||||
|
||||
// Skip Bonus rounds.
|
||||
if (roundqueue.entries[entry].gametype != roundqueue.entries[0].gametype
|
||||
&& roundqueue.entries[entry].rankrestricted == false)
|
||||
{
|
||||
G_GetNextMap(); // updates position in the roundqueue
|
||||
entry = roundqueue.position-1;
|
||||
}
|
||||
}
|
||||
|
||||
paused = false;
|
||||
|
||||
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
|
||||
|
||||
M_ClearMenus(true);
|
||||
restoreMenu = &PLAY_CupSelectDef;
|
||||
|
||||
if (entry < roundqueue.size)
|
||||
{
|
||||
D_MapChange(
|
||||
roundqueue.entries[entry].mapnum + 1,
|
||||
roundqueue.entries[entry].gametype,
|
||||
roundqueue.entries[entry].encore,
|
||||
true,
|
||||
1,
|
||||
false,
|
||||
roundqueue.entries[entry].rankrestricted
|
||||
);
|
||||
}
|
||||
else if (entry == 0)
|
||||
{
|
||||
I_Error("M_StartCup: roundqueue is empty on startup!!");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (K_StartCeremony() == false)
|
||||
{
|
||||
// Accomodate our buffoonery
|
||||
D_ClearState();
|
||||
M_StartControlPanel();
|
||||
|
||||
M_StartMessage(
|
||||
"Grand Prix Backup",
|
||||
"The session is concluded!\n"
|
||||
"You exited a final Bonus Round,\n"
|
||||
"and the Podium failed to load.\n",
|
||||
NULL, MM_NOTHING, NULL, NULL
|
||||
);
|
||||
|
||||
G_HandleSaveLevel(true);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void M_GPBackup(INT32 choice)
|
||||
{
|
||||
if (choice == MA_YES)
|
||||
{
|
||||
G_LoadGame();
|
||||
|
||||
if (savedata.lives != 0)
|
||||
{
|
||||
M_StartCup(roundqueue.position-1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
M_StartCup(0);
|
||||
}
|
||||
|
||||
void M_CupSelectHandler(INT32 choice)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
|
|
@ -104,67 +251,24 @@ void M_CupSelectHandler(INT32 choice)
|
|||
|
||||
if (cupgrid.grandprix == true)
|
||||
{
|
||||
UINT8 ssplayers = cv_splitplayers.value-1;
|
||||
|
||||
S_StartSound(NULL, sfx_s3k63);
|
||||
|
||||
paused = false;
|
||||
|
||||
S_StopMusicCredit();
|
||||
|
||||
// Early fadeout to let the sound finish playing
|
||||
F_WipeStartScreen();
|
||||
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
|
||||
F_WipeEndScreen();
|
||||
F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
|
||||
|
||||
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
|
||||
|
||||
if (cv_maxconnections.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxconnections, ssplayers+1);
|
||||
|
||||
if (splitscreen != ssplayers)
|
||||
if (newcup == cupsavedata.cup
|
||||
&& FIL_FileExists(gpbackup))
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
M_StartMessage(
|
||||
"Grand Prix Backup",
|
||||
"A progress backup was found.\n"
|
||||
"Do you want to resurrect your\n"
|
||||
"last Grand Prix session?\n",
|
||||
M_GPBackup,
|
||||
MM_YESNO,
|
||||
"Yes, let's try again",
|
||||
"No, start from Round 1"
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// read our dummy cvars
|
||||
|
||||
grandprixinfo.gamespeed = min(KARTSPEED_HARD, cv_dummygpdifficulty.value);
|
||||
grandprixinfo.masterbots = (cv_dummygpdifficulty.value == 3);
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.initalize = true;
|
||||
grandprixinfo.cup = newcup;
|
||||
|
||||
// Populate the roundqueue
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
G_GPCupIntoRoundQueue(newcup, levellist.newgametype, (boolean)cv_dummygpencore.value);
|
||||
roundqueue.position = roundqueue.roundnum = 1;
|
||||
roundqueue.netcommunicate = true; // relevant for future Online GP
|
||||
|
||||
paused = false;
|
||||
|
||||
// Don't restart the server if we're already in a game lol
|
||||
if (gamestate == GS_MENU)
|
||||
{
|
||||
SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame);
|
||||
}
|
||||
|
||||
D_MapChange(
|
||||
roundqueue.entries[0].mapnum + 1,
|
||||
roundqueue.entries[0].gametype,
|
||||
roundqueue.entries[0].encore,
|
||||
true,
|
||||
1,
|
||||
false,
|
||||
roundqueue.entries[0].rankrestricted
|
||||
);
|
||||
|
||||
M_ClearMenus(true);
|
||||
|
||||
restoreMenu = &PLAY_CupSelectDef;
|
||||
M_StartCup(0);
|
||||
}
|
||||
else if (count == 1 && levellist.levelsearch.timeattack == true)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
#include "../../r_local.h" // SplitScreen_OnChange
|
||||
#include "../../f_finale.h" // F_WipeStartScreen
|
||||
#include "../../v_video.h"
|
||||
#include "../../g_game.h" // G_GetBackupCupData
|
||||
#include "../../p_saveg.h" // cupsavedata
|
||||
|
||||
cupheader_t dummy_lostandfound;
|
||||
|
||||
|
|
@ -262,6 +264,11 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
const size_t pagelen = sizeof(cupheader_t*) * (CUPMENU_COLUMNS * CUPMENU_ROWS);
|
||||
boolean foundany = false, currentvalid = false;
|
||||
|
||||
G_GetBackupCupData(
|
||||
cupgrid.grandprix == true
|
||||
|| cv_splitplayers.value <= 1
|
||||
);
|
||||
|
||||
templevelsearch.cup = kartcupheaders;
|
||||
|
||||
#if 0
|
||||
|
|
@ -331,7 +338,8 @@ boolean M_LevelListFromGametype(INT16 gt)
|
|||
|
||||
if (Playing()
|
||||
? (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->cup == templevelsearch.cup)
|
||||
: (gt == -1 && levellist.levelsearch.cup == templevelsearch.cup))
|
||||
: (cupsavedata.cup == templevelsearch.cup
|
||||
|| (gt == -1 && levellist.levelsearch.cup == templevelsearch.cup)))
|
||||
{
|
||||
GRID_FOCUSCUP;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,14 @@ struct menumessage_s menumessage;
|
|||
//
|
||||
static inline size_t M_StringHeight(const char *string)
|
||||
{
|
||||
size_t h = 8, i;
|
||||
size_t h = 16, i, len = strlen(string);
|
||||
|
||||
for (i = 0; i < strlen(string); i++)
|
||||
if (string[i] == '\n')
|
||||
h += 8;
|
||||
for (i = 0; i < len-1; i++)
|
||||
{
|
||||
if (string[i] != '\n')
|
||||
continue;
|
||||
h += 8;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
|
@ -68,9 +71,10 @@ void M_StartMessage(const char *header, const char *string, void (*routine)(INT3
|
|||
menumessage.header = header;
|
||||
menumessage.flags = itemtype;
|
||||
menumessage.routine = routine;
|
||||
menumessage.answer = MA_NONE;
|
||||
menumessage.fadetimer = 1;
|
||||
menumessage.timer = 0;
|
||||
menumessage.closing = false;
|
||||
menumessage.closing = 0;
|
||||
menumessage.active = true;
|
||||
|
||||
start = 0;
|
||||
|
|
@ -79,18 +83,21 @@ void M_StartMessage(const char *header, const char *string, void (*routine)(INT3
|
|||
if (!routine)
|
||||
{
|
||||
menumessage.flags = MM_NOTHING;
|
||||
menumessage.routine = M_StopMessage;
|
||||
}
|
||||
|
||||
if (menumessage.flags == MM_YESNO && !defaultstr)
|
||||
// Set action strings
|
||||
switch (menumessage.flags)
|
||||
{
|
||||
menumessage.defaultstr = "No";
|
||||
menumessage.confirmstr = "Yes";
|
||||
}
|
||||
else
|
||||
{
|
||||
menumessage.defaultstr = defaultstr ? defaultstr : "OK";
|
||||
menumessage.confirmstr = confirmstr;
|
||||
// Send 1 to the routine if we're pressing A, 2 if B/X, 0 otherwise.
|
||||
case MM_YESNO:
|
||||
menumessage.defaultstr = defaultstr ? defaultstr : "No";
|
||||
menumessage.confirmstr = confirmstr ? confirmstr : "Yes";
|
||||
break;
|
||||
|
||||
default:
|
||||
menumessage.defaultstr = defaultstr ? defaultstr : "OK";
|
||||
menumessage.confirmstr = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
// event routine
|
||||
|
|
@ -133,11 +140,25 @@ void M_StartMessage(const char *header, const char *string, void (*routine)(INT3
|
|||
|
||||
void M_StopMessage(INT32 choice)
|
||||
{
|
||||
const char pid = 0;
|
||||
(void) choice;
|
||||
if (!menumessage.active || menumessage.closing)
|
||||
return;
|
||||
|
||||
const char pid = 0;
|
||||
|
||||
// Set the answer.
|
||||
menumessage.answer = choice;
|
||||
|
||||
#if 1
|
||||
// The below was cool, but it felt annoyingly unresponsive.
|
||||
menumessage.closing = MENUMESSAGECLOSE+1;
|
||||
#else
|
||||
// Intended length of time.
|
||||
menumessage.closing = (TICRATE/2);
|
||||
|
||||
// This weird operation is necessary so the text flash is consistently timed.
|
||||
menumessage.closing |= ((2*MENUMESSAGECLOSE) - 1);
|
||||
#endif
|
||||
|
||||
menumessage.closing = true;
|
||||
menumessage.timer = 0;
|
||||
M_SetMenuDelay(pid);
|
||||
}
|
||||
|
||||
|
|
@ -145,14 +166,26 @@ boolean M_MenuMessageTick(void)
|
|||
{
|
||||
if (menumessage.closing)
|
||||
{
|
||||
if (menumessage.fadetimer > 0)
|
||||
if (menumessage.closing > MENUMESSAGECLOSE)
|
||||
{
|
||||
menumessage.fadetimer--;
|
||||
menumessage.closing--;
|
||||
}
|
||||
|
||||
if (menumessage.fadetimer == 0)
|
||||
else
|
||||
{
|
||||
menumessage.active = false;
|
||||
if (menumessage.fadetimer > 0)
|
||||
{
|
||||
menumessage.fadetimer--;
|
||||
}
|
||||
|
||||
if (menumessage.fadetimer == 0)
|
||||
{
|
||||
menumessage.active = false;
|
||||
|
||||
if (menumessage.routine)
|
||||
{
|
||||
menumessage.routine(menumessage.answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -180,38 +213,22 @@ void M_HandleMenuMessage(void)
|
|||
|
||||
switch (menumessage.flags)
|
||||
{
|
||||
// Send 1 to the routine if we're pressing A/B/X
|
||||
case MM_NOTHING:
|
||||
{
|
||||
if (btok || btnok)
|
||||
menumessage.routine(0);
|
||||
|
||||
break;
|
||||
}
|
||||
// Send 1 to the routine if we're pressing A, 2 if B/X, 0 otherwise.
|
||||
case MM_YESNO:
|
||||
{
|
||||
INT32 answer = MA_NONE;
|
||||
if (btok)
|
||||
answer = MA_YES;
|
||||
M_StopMessage(MA_YES);
|
||||
else if (btnok)
|
||||
answer = MA_NO;
|
||||
|
||||
// send 1 if btok is pressed, 2 if nok is pressed, 0 otherwise.
|
||||
if (answer)
|
||||
{
|
||||
menumessage.routine(answer);
|
||||
M_StopMessage(0);
|
||||
}
|
||||
M_StopMessage(MA_NO);
|
||||
|
||||
break;
|
||||
}
|
||||
// MM_EVENTHANDLER: In M_Responder to allow full event compat.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
{
|
||||
if (btok || btnok)
|
||||
M_StopMessage(MA_NONE);
|
||||
|
||||
// if we detect any keypress, don't forget to set the menu delay regardless.
|
||||
if (btok || btnok)
|
||||
M_SetMenuDelay(pid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -358,6 +358,8 @@ void M_ConfirmEnterGame(INT32 choice)
|
|||
|
||||
static void M_ExitGameResponse(INT32 ch)
|
||||
{
|
||||
const UINT8 pid = 0;
|
||||
|
||||
if (ch != MA_YES)
|
||||
return;
|
||||
|
||||
|
|
@ -368,7 +370,7 @@ static void M_ExitGameResponse(INT32 ch)
|
|||
else
|
||||
{
|
||||
G_SetExitGameFlag();
|
||||
M_ClearMenus(true);
|
||||
M_SetMenuDelay(pid); // prevent another input
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,4 +20,5 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
instawhip.c
|
||||
block.c
|
||||
gachabom-rebound.cpp
|
||||
servant-hand.c
|
||||
)
|
||||
|
|
|
|||
|
|
@ -101,25 +101,35 @@ Obj_AudienceInit
|
|||
mobj->destscale = FixedMul(3*mobj->destscale, followers[followerpick].scale);
|
||||
P_SetScale(mobj, mobj->destscale);
|
||||
|
||||
audience_mainstate(mobj) = followers[followerpick].followstate;
|
||||
|
||||
P_SetMobjState(mobj, audience_mainstate(mobj));
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
return;
|
||||
|
||||
// The following is derived from the default bobamp
|
||||
if (mobj->type != MT_EMBLEM && !(mobj->flags & MF_NOGRAVITY) && followers[followerpick].bobamp < 4*FRACUNIT)
|
||||
if (mobj->flags2 & MF2_BOSSNOTRAP)
|
||||
{
|
||||
audience_bobamp(mobj) = 4*mobj->scale;
|
||||
audience_bobamp(mobj) = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
audience_bobamp(mobj) = FixedMul(mobj->scale, followers[followerpick].bobamp);
|
||||
// The following is derived from the default bobamp
|
||||
if (mobj->type != MT_EMBLEM && !(mobj->flags & MF_NOGRAVITY) && followers[followerpick].bobamp < 4*FRACUNIT)
|
||||
{
|
||||
audience_bobamp(mobj) = 4*mobj->scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
audience_bobamp(mobj) = FixedMul(mobj->scale, followers[followerpick].bobamp);
|
||||
}
|
||||
}
|
||||
|
||||
audience_bobspeed(mobj) = followers[followerpick].bobspeed;
|
||||
audience_focusplayer(mobj) = MAXPLAYERS;
|
||||
|
||||
audience_mainstate(mobj) =
|
||||
audience_bobamp(mobj) != 0
|
||||
? followers[followerpick].followstate
|
||||
: followers[followerpick].idlestate;
|
||||
|
||||
P_SetMobjState(mobj, audience_mainstate(mobj));
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
return;
|
||||
|
||||
if (P_RandomChance(PR_RANDOMAUDIENCE, FRACUNIT/2))
|
||||
{
|
||||
audience_animoffset(mobj) = 5;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
#include "../k_objects.h"
|
||||
#include "../p_local.h"
|
||||
|
||||
#define recharge_target(o) ((o)->target)
|
||||
#define recharge_offset(o) ((o)->movedir)
|
||||
|
||||
void Obj_InstaWhipThink (mobj_t *whip)
|
||||
{
|
||||
if (P_MobjWasRemoved(whip->target))
|
||||
|
|
@ -40,3 +43,32 @@ void Obj_InstaWhipThink (mobj_t *whip)
|
|||
whip->renderflags |= RF_DONTDRAW;
|
||||
}
|
||||
}
|
||||
|
||||
void Obj_SpawnInstaWhipRecharge(player_t *player, angle_t angleOffset)
|
||||
{
|
||||
mobj_t *x = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_INSTAWHIP_RECHARGE);
|
||||
|
||||
x->tics = max(player->instaShieldCooldown - states[x->info->raisestate].tics, 0);
|
||||
x->renderflags |= RF_SLOPESPLAT | RF_NOSPLATBILLBOARD;
|
||||
|
||||
P_SetTarget(&recharge_target(x), player->mo);
|
||||
recharge_offset(x) = angleOffset;
|
||||
}
|
||||
|
||||
void Obj_InstaWhipRechargeThink(mobj_t *x)
|
||||
{
|
||||
mobj_t *target = recharge_target(x);
|
||||
|
||||
if (P_MobjWasRemoved(target))
|
||||
{
|
||||
P_RemoveMobj(x);
|
||||
return;
|
||||
}
|
||||
|
||||
P_MoveOrigin(x, target->x, target->y, target->z + (target->height / 2));
|
||||
P_InstaScale(x, 2 * target->scale);
|
||||
x->angle = target->angle + recharge_offset(x);
|
||||
|
||||
// Flickers every other frame
|
||||
x->renderflags ^= RF_DONTDRAW;
|
||||
}
|
||||
|
|
|
|||
103
src/objects/servant-hand.c
Normal file
103
src/objects/servant-hand.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#include "../doomdef.h"
|
||||
#include "../p_local.h"
|
||||
#include "../p_mobj.h"
|
||||
#include "../d_player.h"
|
||||
#include "../k_kart.h"
|
||||
#include "../k_objects.h"
|
||||
#include "../v_video.h"
|
||||
|
||||
void Obj_ServantHandHandling(player_t *player)
|
||||
{
|
||||
if (player->pflags & PF_WRONGWAY)
|
||||
{
|
||||
if (player->handtimer < TICRATE)
|
||||
{
|
||||
player->handtimer++;
|
||||
if (player->hand == NULL && player->handtimer == TICRATE)
|
||||
{
|
||||
mobj_t *hand = P_SpawnMobj(
|
||||
player->mo->x,
|
||||
player->mo->y,
|
||||
player->mo->z + player->mo->height + 30*mapobjectscale,
|
||||
MT_SERVANTHAND
|
||||
);
|
||||
|
||||
if (hand)
|
||||
{
|
||||
K_FlipFromObject(hand, player->mo);
|
||||
hand->old_z = hand->z;
|
||||
|
||||
P_SetTarget(&hand->target, player->mo);
|
||||
P_SetTarget(&player->hand, hand);
|
||||
|
||||
hand->fuse = 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (player->hand)
|
||||
{
|
||||
player->hand->destscale = mapobjectscale;
|
||||
}
|
||||
}
|
||||
else if (player->handtimer != 0)
|
||||
{
|
||||
player->handtimer--;
|
||||
}
|
||||
|
||||
if (player->hand)
|
||||
{
|
||||
const fixed_t handpokespeed = 4;
|
||||
const fixed_t looping = handpokespeed - abs((player->hand->threshold % (handpokespeed*2)) - handpokespeed);
|
||||
fixed_t xoffs = 0, yoffs = 0;
|
||||
|
||||
player->hand->color = player->skincolor;
|
||||
player->hand->angle = player->besthanddirection;
|
||||
|
||||
if (player->hand->fuse != 0)
|
||||
{
|
||||
;
|
||||
}
|
||||
else if (looping != 0)
|
||||
{
|
||||
xoffs = FixedMul(2 * looping * mapobjectscale, FINECOSINE(player->hand->angle >> ANGLETOFINESHIFT)),
|
||||
yoffs = FixedMul(2 * looping * mapobjectscale, FINESINE(player->hand->angle >> ANGLETOFINESHIFT)),
|
||||
|
||||
player->hand->threshold++;
|
||||
}
|
||||
else if (player->handtimer == 0)
|
||||
{
|
||||
player->hand->fuse = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->hand->threshold++;
|
||||
}
|
||||
|
||||
if (player->hand->fuse != 0)
|
||||
{
|
||||
if ((player->hand->fuse > 4) ^ (player->handtimer < TICRATE/2))
|
||||
{
|
||||
player->hand->spritexscale = FRACUNIT/3;
|
||||
player->hand->spriteyscale = 3*FRACUNIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->hand->spritexscale = 2*FRACUNIT;
|
||||
player->hand->spriteyscale = FRACUNIT/2;
|
||||
}
|
||||
}
|
||||
|
||||
P_MoveOrigin(player->hand,
|
||||
player->mo->x + xoffs,
|
||||
player->mo->y + yoffs,
|
||||
player->mo->z + player->mo->height + 30*mapobjectscale
|
||||
);
|
||||
K_FlipFromObject(player->hand, player->mo);
|
||||
|
||||
player->hand->sprzoff = player->mo->sprzoff;
|
||||
|
||||
player->hand->renderflags &= ~RF_DONTDRAW;
|
||||
player->hand->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player));
|
||||
}
|
||||
}
|
||||
|
|
@ -678,6 +678,12 @@ static void SPBChase(mobj_t *spb, mobj_t *bestMobj)
|
|||
UINT8 spark = ((10 - chasePlayer->kartspeed) + chasePlayer->kartweight) / 2;
|
||||
fixed_t easiness = ((chasePlayer->kartspeed + (10 - spark)) << FRACBITS) / 2;
|
||||
|
||||
fixed_t scaleAdjust = FRACUNIT;
|
||||
if (chase->scale > mapobjectscale)
|
||||
scaleAdjust = GROW_PHYSICS_SCALE;
|
||||
if (chase->scale < mapobjectscale)
|
||||
scaleAdjust = SHRINK_PHYSICS_SCALE;
|
||||
|
||||
spb_lastplayer(spb) = chasePlayer - players; // Save the player num for death scumming...
|
||||
spbplace = chasePlayer->position;
|
||||
|
||||
|
|
@ -693,7 +699,7 @@ static void SPBChase(mobj_t *spb, mobj_t *bestMobj)
|
|||
// 7/8ths max speed for Knuckles, 3/4ths max speed for min accel, exactly max speed for max accel
|
||||
baseSpeed = FixedMul(
|
||||
((fracmax+1) << FRACBITS) - easiness,
|
||||
K_GetKartSpeed(chasePlayer, false, false)
|
||||
FixedMul(K_GetKartSpeed(chasePlayer, false, false), scaleAdjust)
|
||||
) / fracmax;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7220,13 +7220,14 @@ void A_ChangeRollAngleAbsolute(mobj_t *actor)
|
|||
//
|
||||
// var1 = sound # to play
|
||||
// var2:
|
||||
// lower 16 bits = If 1, play sound using calling object as origin. If 0, play sound without an origin
|
||||
// lower 16 bits = If 1, play sound using calling object as origin. If 2, use target. If 0, play sound without an origin
|
||||
// upper 16 bits = If 1, do not play sound during preticker.
|
||||
//
|
||||
void A_PlaySound(mobj_t *actor)
|
||||
{
|
||||
INT32 locvar1 = var1;
|
||||
INT32 locvar2 = var2;
|
||||
mobj_t *origin = NULL;
|
||||
|
||||
if (LUA_CallAction(A_PLAYSOUND, actor))
|
||||
return;
|
||||
|
|
@ -7234,7 +7235,18 @@ void A_PlaySound(mobj_t *actor)
|
|||
if (leveltime < 2 && (locvar2 >> 16))
|
||||
return;
|
||||
|
||||
S_StartSound((locvar2 & 65535) ? actor : NULL, locvar1);
|
||||
switch (locvar2 & 65535)
|
||||
{
|
||||
case 1:
|
||||
origin = actor;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
origin = actor->target ? actor->target : actor;
|
||||
break;
|
||||
}
|
||||
|
||||
S_StartSound(origin, locvar1);
|
||||
}
|
||||
|
||||
// Function: A_FindTarget
|
||||
|
|
|
|||
98
src/p_mobj.c
98
src/p_mobj.c
|
|
@ -6681,6 +6681,14 @@ static void P_MobjSceneryThink(mobj_t *mobj)
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case MT_INSTAWHIP_RECHARGE:
|
||||
Obj_InstaWhipRechargeThink(mobj);
|
||||
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MT_VWREF:
|
||||
case MT_VWREB:
|
||||
{
|
||||
|
|
@ -9764,6 +9772,22 @@ static boolean P_FuseThink(mobj_t *mobj)
|
|||
Obj_SPBExplode(mobj);
|
||||
break;
|
||||
}
|
||||
case MT_SERVANTHAND:
|
||||
{
|
||||
if (!mobj->target
|
||||
|| P_MobjWasRemoved(mobj->target)
|
||||
|| !mobj->target->player
|
||||
|| mobj->target->player->handtimer == 0)
|
||||
{
|
||||
P_RemoveMobj(mobj);
|
||||
return false;
|
||||
}
|
||||
|
||||
mobj->spritexscale = FRACUNIT;
|
||||
mobj->spriteyscale = FRACUNIT;
|
||||
|
||||
break;
|
||||
}
|
||||
case MT_PLAYER:
|
||||
break; // don't remove
|
||||
default:
|
||||
|
|
@ -12293,7 +12317,7 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
|
|||
return true;
|
||||
}
|
||||
|
||||
static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle)
|
||||
static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj)
|
||||
{
|
||||
fixed_t mlength, mmaxlength, mlengthset, mspeed, mphase, myaw, mpitch, mminlength, mnumspokes, mpinch, mroll, mnumnospokes, mwidth, mwidthset, mmin, msound, radiusfactor, widthfactor;
|
||||
angle_t mspokeangle;
|
||||
|
|
@ -12338,7 +12362,6 @@ static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle)
|
|||
mobj->lastlook = mspeed;
|
||||
mobj->movecount = mobj->lastlook;
|
||||
mobj->angle = FixedAngle(myaw << FRACBITS);
|
||||
*doangle = false;
|
||||
mobj->threshold = (FixedAngle(mpitch << FRACBITS) >> ANGLETOFINESHIFT);
|
||||
mobj->movefactor = mpinch;
|
||||
mobj->movedir = 0;
|
||||
|
|
@ -12733,7 +12756,7 @@ static boolean P_MapAlreadyHasStarPost(mobj_t *mobj)
|
|||
return false;
|
||||
}
|
||||
|
||||
static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean *doangle)
|
||||
static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj)
|
||||
{
|
||||
boolean override = LUA_HookMapThingSpawn(mobj, mthing);
|
||||
|
||||
|
|
@ -12845,7 +12868,7 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
|
|||
case MT_CHAINPOINT:
|
||||
case MT_FIREBARPOINT:
|
||||
case MT_CUSTOMMACEPOINT:
|
||||
if (!P_SetupMace(mthing, mobj, doangle))
|
||||
if (!P_SetupMace(mthing, mobj))
|
||||
return false;
|
||||
break;
|
||||
case MT_PARTICLEGEN:
|
||||
|
|
@ -13157,11 +13180,16 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
|
|||
}
|
||||
case MT_RANDOMAUDIENCE:
|
||||
{
|
||||
if (mthing->args[2] != 0)
|
||||
if (mthing->args[2] & TMAUDIM_FLOAT)
|
||||
{
|
||||
mobj->flags |= MF_NOGRAVITY;
|
||||
}
|
||||
|
||||
if (mthing->args[2] & TMAUDIM_BORED)
|
||||
{
|
||||
mobj->flags2 |= MF2_BOSSNOTRAP;
|
||||
}
|
||||
|
||||
if (mthing->args[3] != 0)
|
||||
{
|
||||
mobj->flags2 |= MF2_AMBUSH;
|
||||
|
|
@ -13338,23 +13366,18 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
|
|||
}
|
||||
case MT_DUELBOMB:
|
||||
{
|
||||
// Duel Bomb needs init to match real map thing's angle
|
||||
mobj->angle = FixedAngle(mthing->angle << FRACBITS);
|
||||
Obj_DuelBombInit(mobj);
|
||||
|
||||
if (mthing->args[1])
|
||||
{
|
||||
Obj_DuelBombReverse(mobj);
|
||||
}
|
||||
|
||||
*doangle = false;
|
||||
break;
|
||||
}
|
||||
case MT_BANANA:
|
||||
{
|
||||
// Give Duel bananas a random angle
|
||||
mobj->angle = FixedMul(P_RandomFixed(PR_DECORATION), ANGLE_MAX);
|
||||
*doangle = false;
|
||||
break;
|
||||
}
|
||||
case MT_HYUDORO_CENTER:
|
||||
|
|
@ -13386,37 +13409,9 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
|
|||
mobj->flags2 |= MF2_BOSSNOTRAP;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, fixed_t z, mobjtype_t i)
|
||||
{
|
||||
mobj_t *mobj = NULL;
|
||||
boolean doangle = true;
|
||||
size_t arg = SIZE_MAX;
|
||||
|
||||
mobj = P_SpawnMobj(x, y, z, i);
|
||||
mobj->spawnpoint = mthing;
|
||||
|
||||
P_SetScale(mobj, FixedMul(mobj->scale, mthing->scale));
|
||||
mobj->destscale = FixedMul(mobj->destscale, mthing->scale);
|
||||
|
||||
if (!P_SetupSpawnedMapThing(mthing, mobj, &doangle))
|
||||
{
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
return NULL;
|
||||
|
||||
return mobj;
|
||||
}
|
||||
|
||||
if (doangle)
|
||||
{
|
||||
mobj->angle = FixedAngle(mthing->angle << FRACBITS);
|
||||
}
|
||||
|
||||
if ((mobj->flags & MF_SPRING)
|
||||
&& mobj->info->damage != 0
|
||||
&& mobj->info->mass == 0)
|
||||
&& mobj->info->damage != 0
|
||||
&& mobj->info->mass == 0)
|
||||
{
|
||||
// Offset sprite of horizontal springs
|
||||
angle_t a = mobj->angle + ANGLE_180;
|
||||
|
|
@ -13424,9 +13419,24 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y,
|
|||
mobj->spryoff = FixedMul(mobj->radius, FINESINE(a >> ANGLETOFINESHIFT));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, fixed_t z, mobjtype_t i)
|
||||
{
|
||||
mobj_t *mobj = NULL;
|
||||
size_t arg = SIZE_MAX;
|
||||
|
||||
mobj = P_SpawnMobj(x, y, z, i);
|
||||
mobj->spawnpoint = mthing;
|
||||
|
||||
mobj->angle = FixedAngle(mthing->angle << FRACBITS);
|
||||
mobj->pitch = FixedAngle(mthing->pitch << FRACBITS);
|
||||
mobj->roll = FixedAngle(mthing->roll << FRACBITS);
|
||||
|
||||
P_SetScale(mobj, FixedMul(mobj->scale, mthing->scale));
|
||||
mobj->destscale = FixedMul(mobj->destscale, mthing->scale);
|
||||
|
||||
P_SetThingTID(mobj, mthing->tid);
|
||||
|
||||
mobj->special = mthing->special;
|
||||
|
|
@ -13456,6 +13466,14 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y,
|
|||
M_Memcpy(mobj->stringargs[arg], mthing->stringargs[arg], len + 1);
|
||||
}
|
||||
|
||||
if (!P_SetupSpawnedMapThing(mthing, mobj))
|
||||
{
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
return NULL;
|
||||
|
||||
return mobj;
|
||||
}
|
||||
|
||||
mthing->mobj = mobj;
|
||||
|
||||
// Generic reverse gravity for individual objects flag.
|
||||
|
|
|
|||
415
src/p_saveg.c
415
src/p_saveg.c
|
|
@ -38,6 +38,7 @@
|
|||
#include "m_cond.h" // netUnlocked
|
||||
|
||||
// SRB2Kart
|
||||
#include "k_grandprix.h"
|
||||
#include "k_battle.h"
|
||||
#include "k_pwrlv.h"
|
||||
#include "k_terrain.h"
|
||||
|
|
@ -47,6 +48,7 @@
|
|||
#include "k_zvote.h"
|
||||
|
||||
savedata_t savedata;
|
||||
savedata_cup_t cupsavedata;
|
||||
|
||||
// Block UINT32s to attempt to ensure that the correct data is
|
||||
// being sent and received
|
||||
|
|
@ -76,30 +78,112 @@ typedef enum
|
|||
SLIPTIDEZIP = 0x0080,
|
||||
RINGSHOOTER = 0x0100,
|
||||
WHIP = 0x0200,
|
||||
HAND = 0x0400,
|
||||
} player_saveflags;
|
||||
|
||||
static inline void P_ArchivePlayer(savebuffer_t *save)
|
||||
{
|
||||
const player_t *player = &players[consoleplayer];
|
||||
INT16 skininfo = player->skin;
|
||||
SINT8 pllives = player->lives;
|
||||
if (pllives < startinglivesbalance[numgameovers]) // Bump up to 3 lives if the player
|
||||
pllives = startinglivesbalance[numgameovers]; // has less than that.
|
||||
|
||||
WRITEUINT16(save->p, skininfo);
|
||||
WRITEUINT8(save->p, numgameovers);
|
||||
WRITESINT8(save->p, pllives);
|
||||
// Prevent an exploit from occuring.
|
||||
WRITESINT8(save->p, (player->lives - 1));
|
||||
WRITEUINT32(save->p, player->score);
|
||||
WRITEUINT16(save->p, player->totalring);
|
||||
|
||||
INT32 skin = player->skin;
|
||||
if (skin > numskins)
|
||||
skin = 0;
|
||||
|
||||
WRITESTRINGN(save->p, skins[skin].name, SKINNAMESIZE);
|
||||
|
||||
if (player->followerskin < 0 || player->followerskin >= numfollowers)
|
||||
WRITESTRINGN(save->p, "None", SKINNAMESIZE);
|
||||
else
|
||||
WRITESTRINGN(save->p, followers[player->followerskin].name, SKINNAMESIZE);
|
||||
|
||||
WRITEUINT16(save->p, player->skincolor);
|
||||
WRITEUINT16(save->p, player->followercolor);
|
||||
|
||||
UINT8 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false)
|
||||
continue;
|
||||
if (players[i].bot == false)
|
||||
continue;
|
||||
|
||||
WRITEUINT8(save->p, i);
|
||||
|
||||
skin = players[i].skin;
|
||||
if (skin > numskins)
|
||||
skin = 0;
|
||||
|
||||
WRITESTRINGN(save->p, skins[skin].name, SKINNAMESIZE);
|
||||
|
||||
WRITEUINT8(save->p, players[i].botvars.difficulty);
|
||||
WRITEUINT8(save->p, (UINT8)players[i].botvars.rival);
|
||||
|
||||
WRITEUINT32(save->p, players[i].score);
|
||||
}
|
||||
|
||||
WRITEUINT8(save->p, 0xFE);
|
||||
}
|
||||
|
||||
static inline void P_UnArchivePlayer(savebuffer_t *save)
|
||||
static boolean P_UnArchivePlayer(savebuffer_t *save)
|
||||
{
|
||||
INT16 skininfo = READUINT16(save->p);
|
||||
savedata.skin = skininfo;
|
||||
|
||||
savedata.numgameovers = READUINT8(save->p);
|
||||
savedata.lives = READSINT8(save->p);
|
||||
savedata.score = READUINT32(save->p);
|
||||
savedata.totalring = READUINT16(save->p);
|
||||
|
||||
char skinname[SKINNAMESIZE+1];
|
||||
INT32 skin;
|
||||
|
||||
READSTRINGN(save->p, skinname, SKINNAMESIZE);
|
||||
skin = R_SkinAvailable(skinname);
|
||||
|
||||
if (skin == -1)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchivePlayer: Character \"%s\" is not currently loaded.\n", skinname);
|
||||
return false;
|
||||
}
|
||||
|
||||
savedata.skin = skin;
|
||||
|
||||
READSTRINGN(save->p, skinname, SKINNAMESIZE);
|
||||
savedata.followerskin = K_FollowerAvailable(skinname);
|
||||
|
||||
savedata.skincolor = READUINT16(save->p);
|
||||
savedata.followercolor = READUINT16(save->p);
|
||||
|
||||
memset(&savedata.bots, 0, sizeof(savedata.bots));
|
||||
|
||||
UINT8 pid;
|
||||
const UINT8 defaultbotskin = R_BotDefaultSkin();
|
||||
|
||||
while ((pid = READUINT8(save->p)) < MAXPLAYERS)
|
||||
{
|
||||
savedata.bots[pid].valid = true;
|
||||
|
||||
READSTRINGN(save->p, skinname, SKINNAMESIZE);
|
||||
skin = R_SkinAvailable(skinname);
|
||||
|
||||
if (skin == -1)
|
||||
{
|
||||
// It is not worth destroying an otherwise good savedata over extra added skins.
|
||||
// Let's just say they didn't show up to the rematch, so some Eggrobos subbed in.
|
||||
CONS_Alert(CONS_WARNING, "P_UnArchivePlayer: Bot's character \"%s\" was not loaded, replacing with default \"%s\".\n", skinname, skins[defaultbotskin].name);
|
||||
skin = defaultbotskin;
|
||||
}
|
||||
|
||||
savedata.bots[pid].skin = skin;
|
||||
|
||||
savedata.bots[pid].difficulty = READUINT8(save->p);
|
||||
savedata.bots[pid].rival = (boolean)READUINT8(save->p);
|
||||
savedata.bots[pid].score = READUINT32(save->p);
|
||||
}
|
||||
|
||||
return (pid == 0xFE);
|
||||
}
|
||||
|
||||
static void P_NetArchivePlayers(savebuffer_t *save)
|
||||
|
|
@ -229,6 +313,9 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
if (players[i].whip)
|
||||
flags |= WHIP;
|
||||
|
||||
if (players[i].hand)
|
||||
flags |= HAND;
|
||||
|
||||
if (players[i].ringShooter)
|
||||
flags |= RINGSHOOTER;
|
||||
|
||||
|
|
@ -258,6 +345,9 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
if (flags & WHIP)
|
||||
WRITEUINT32(save->p, players[i].whip->mobjnum);
|
||||
|
||||
if (flags & HAND)
|
||||
WRITEUINT32(save->p, players[i].hand->mobjnum);
|
||||
|
||||
if (flags & RINGSHOOTER)
|
||||
WRITEUINT32(save->p, players[i].ringShooter->mobjnum);
|
||||
|
||||
|
|
@ -429,12 +519,17 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
|
||||
WRITEUINT8(save->p, players[i].instaShieldCooldown);
|
||||
WRITEUINT8(save->p, players[i].guardCooldown);
|
||||
|
||||
WRITEUINT8(save->p, players[i].handtimer);
|
||||
WRITEANGLE(save->p, players[i].besthanddirection);
|
||||
|
||||
WRITEINT16(save->p, players[i].incontrol);
|
||||
|
||||
WRITEUINT8(save->p, players[i].markedfordeath);
|
||||
|
||||
WRITEUINT8(save->p, players[i].ringboxdelay);
|
||||
WRITEUINT8(save->p, players[i].ringboxaward);
|
||||
WRITEFIXED(save->p, players[i].outrun);
|
||||
|
||||
// respawnvars_t
|
||||
WRITEUINT8(save->p, players[i].respawn.state);
|
||||
|
|
@ -655,6 +750,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
if (flags & WHIP)
|
||||
players[i].whip = (mobj_t *)(size_t)READUINT32(save->p);
|
||||
|
||||
if (flags & HAND)
|
||||
players[i].hand = (mobj_t *)(size_t)READUINT32(save->p);
|
||||
|
||||
if (flags & RINGSHOOTER)
|
||||
players[i].ringShooter = (mobj_t *)(size_t)READUINT32(save->p);
|
||||
|
||||
|
|
@ -827,12 +925,17 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
|
||||
players[i].instaShieldCooldown = READUINT8(save->p);
|
||||
players[i].guardCooldown = READUINT8(save->p);
|
||||
|
||||
players[i].handtimer = READUINT8(save->p);
|
||||
players[i].besthanddirection = READANGLE(save->p);
|
||||
|
||||
players[i].incontrol = READINT16(save->p);
|
||||
|
||||
players[i].markedfordeath = READUINT8(save->p);
|
||||
|
||||
players[i].ringboxdelay = READUINT8(save->p);
|
||||
players[i].ringboxaward = READUINT8(save->p);
|
||||
players[i].outrun = READFIXED(save->p);
|
||||
|
||||
// respawnvars_t
|
||||
players[i].respawn.state = READUINT8(save->p);
|
||||
|
|
@ -1023,6 +1126,9 @@ static void P_NetUnArchiveRoundQueue(savebuffer_t *save)
|
|||
|
||||
roundqueue.position = READUINT8(save->p);
|
||||
roundqueue.size = READUINT8(save->p);
|
||||
if (roundqueue.size > ROUNDQUEUE_MAX)
|
||||
I_Error("Bad $$$.sav at illegitimate roundqueue size");
|
||||
|
||||
roundqueue.roundnum = READUINT8(save->p);
|
||||
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
|
|
@ -2361,6 +2467,8 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
|
|||
if (mobj->type == MT_SPARK)
|
||||
return;
|
||||
|
||||
diff2 = 0;
|
||||
|
||||
if (mobj->spawnpoint)
|
||||
{
|
||||
// spawnpoint is not modified but we must save it since it is an identifier
|
||||
|
|
@ -2414,8 +2522,6 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
|
|||
}
|
||||
}
|
||||
|
||||
diff2 = 0;
|
||||
|
||||
// not the default but the most probable
|
||||
if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0 || mobj->pmomz != 0)
|
||||
diff |= MD_MOM;
|
||||
|
|
@ -5142,6 +5248,13 @@ static void P_RelinkPointers(void)
|
|||
if (!P_SetTarget(&players[i].whip, P_FindNewPosition(temp)))
|
||||
CONS_Debug(DBG_GAMELOGIC, "whip not found on player %d\n", i);
|
||||
}
|
||||
if (players[i].hand)
|
||||
{
|
||||
temp = (UINT32)(size_t)players[i].hand;
|
||||
players[i].hand = NULL;
|
||||
if (!P_SetTarget(&players[i].hand, P_FindNewPosition(temp)))
|
||||
CONS_Debug(DBG_GAMELOGIC, "hand not found on player %d\n", i);
|
||||
}
|
||||
if (players[i].ringShooter)
|
||||
{
|
||||
temp = (UINT32)(size_t)players[i].ringShooter;
|
||||
|
|
@ -5234,55 +5347,230 @@ static void P_NetUnArchiveSpecials(savebuffer_t *save)
|
|||
// =======================================================================
|
||||
// Misc
|
||||
// =======================================================================
|
||||
static inline void P_ArchiveMisc(savebuffer_t *save, INT16 mapnum)
|
||||
static inline void P_ArchiveMisc(savebuffer_t *save)
|
||||
{
|
||||
//lastmapsaved = mapnum;
|
||||
lastmaploaded = mapnum;
|
||||
|
||||
if (gamecomplete)
|
||||
mapnum |= 8192;
|
||||
|
||||
WRITEINT16(save->p, mapnum);
|
||||
WRITEUINT16(save->p, emeralds+357);
|
||||
WRITESTRINGN(save->p, timeattackfolder, sizeof(timeattackfolder));
|
||||
|
||||
// Grand Prix information
|
||||
|
||||
WRITEUINT8(save->p, grandprixinfo.gamespeed);
|
||||
WRITEUINT8(save->p, (UINT8)grandprixinfo.encore);
|
||||
WRITEUINT8(save->p, (UINT8)grandprixinfo.masterbots);
|
||||
|
||||
WRITESTRINGL(save->p, grandprixinfo.cup->name, MAXCUPNAME);
|
||||
|
||||
// Round Queue information
|
||||
|
||||
WRITEUINT8(save->p, roundqueue.position);
|
||||
WRITEUINT8(save->p, roundqueue.size);
|
||||
WRITEUINT8(save->p, roundqueue.roundnum);
|
||||
|
||||
UINT8 i;
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
{
|
||||
UINT16 mapnum = roundqueue.entries[i].mapnum;
|
||||
UINT32 val = 0; // no good default, will all-but-guarantee bad save
|
||||
if (mapnum < nummapheaders && mapheaderinfo[mapnum] != NULL)
|
||||
val = mapheaderinfo[mapnum]->lumpnamehash;
|
||||
|
||||
WRITEUINT32(save->p, val);
|
||||
}
|
||||
|
||||
// Rank information
|
||||
|
||||
{
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.players);
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.totalPlayers);
|
||||
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.position);
|
||||
WRITEUINT8(save->p, grandprixinfo.rank.skin);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.winPoints);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalPoints);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.laps);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalLaps);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.continuesUsed);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.prisons);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalPrisons);
|
||||
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.rings);
|
||||
WRITEUINT32(save->p, grandprixinfo.rank.totalRings);
|
||||
|
||||
WRITEUINT8(save->p, (UINT8)grandprixinfo.rank.specialWon);
|
||||
}
|
||||
|
||||
// Marathon information
|
||||
|
||||
WRITEUINT8(save->p, (marathonmode & ~MA_INIT));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static inline void P_UnArchiveSPGame(savebuffer_t *save, INT16 mapoverride)
|
||||
void P_GetBackupCupData(savebuffer_t *save)
|
||||
{
|
||||
char testname[sizeof(timeattackfolder)];
|
||||
|
||||
gamemap = READINT16(save->p);
|
||||
|
||||
if (mapoverride != 0)
|
||||
{
|
||||
gamemap = mapoverride;
|
||||
gamecomplete = 1;
|
||||
}
|
||||
else
|
||||
gamecomplete = 0;
|
||||
|
||||
// gamemap changed; we assume that its map header is always valid,
|
||||
// so make it so
|
||||
if (!gamemap || gamemap > nummapheaders || !mapheaderinfo[gamemap-1])
|
||||
I_Error("P_UnArchiveSPGame: Internal map ID %d not found (nummapheaders = %d)", gamemap-1, nummapheaders);
|
||||
|
||||
//lastmapsaved = gamemap;
|
||||
lastmaploaded = gamemap;
|
||||
|
||||
savedata.emeralds = READUINT16(save->p)-357;
|
||||
|
||||
READSTRINGN(save->p, testname, sizeof(testname));
|
||||
|
||||
if (strcmp(testname, timeattackfolder))
|
||||
{
|
||||
if (modifiedgame)
|
||||
I_Error("Save game not for this modification.");
|
||||
else
|
||||
I_Error("This save file is for a particular mod, it cannot be used with the regular game.");
|
||||
cupsavedata.cup = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(playeringame, 0, sizeof(*playeringame));
|
||||
playeringame[consoleplayer] = true;
|
||||
// Grand Prix information
|
||||
|
||||
cupsavedata.difficulty = READUINT8(save->p);
|
||||
cupsavedata.encore = (boolean)READUINT8(save->p);
|
||||
boolean masterbots = (boolean)READUINT8(save->p);
|
||||
|
||||
if (masterbots == true)
|
||||
cupsavedata.difficulty = KARTGP_MASTER;
|
||||
|
||||
// Find the relevant cup.
|
||||
char cupname[MAXCUPNAME];
|
||||
READSTRINGL(save->p, cupname, sizeof(cupname));
|
||||
UINT32 hash = quickncasehash(cupname, MAXCUPNAME);
|
||||
|
||||
for (cupsavedata.cup = kartcupheaders; cupsavedata.cup; cupsavedata.cup = cupsavedata.cup->next)
|
||||
{
|
||||
if (cupsavedata.cup->namehash != hash)
|
||||
continue;
|
||||
|
||||
if (strcmp(cupsavedata.cup->name, cupname))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Okay, no further! We've got everything we need.
|
||||
}
|
||||
|
||||
static boolean P_UnArchiveSPGame(savebuffer_t *save)
|
||||
{
|
||||
char testname[sizeof(timeattackfolder)];
|
||||
|
||||
READSTRINGN(save->p, testname, sizeof(testname));
|
||||
|
||||
if (strcmp(testname, timeattackfolder))
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Corrupt mod ID.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO do not work off grandprixinfo/roundqueue directly
|
||||
// This is only strictly necessary if we ever re-add a save
|
||||
// select screen or something, for live event backup only
|
||||
// it's *fine* and, more importantly, shippable
|
||||
|
||||
memset(&grandprixinfo, 0, sizeof(grandprixinfo));
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
|
||||
// Grand Prix information
|
||||
|
||||
grandprixinfo.gamespeed = READUINT8(save->p);
|
||||
grandprixinfo.encore = (boolean)READUINT8(save->p);
|
||||
grandprixinfo.masterbots = (boolean)READUINT8(save->p);
|
||||
|
||||
// Find the relevant cup.
|
||||
char cupname[MAXCUPNAME];
|
||||
READSTRINGL(save->p, cupname, sizeof(cupname));
|
||||
UINT32 hash = quickncasehash(cupname, MAXCUPNAME);
|
||||
|
||||
for (grandprixinfo.cup = kartcupheaders; grandprixinfo.cup; grandprixinfo.cup = grandprixinfo.cup->next)
|
||||
{
|
||||
if (grandprixinfo.cup->namehash != hash)
|
||||
continue;
|
||||
|
||||
if (strcmp(grandprixinfo.cup->name, cupname))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!grandprixinfo.cup)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Cup \"%s\" is not currently loaded.\n", cupname);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Round Queue information
|
||||
|
||||
memset(&roundqueue, 0, sizeof(roundqueue));
|
||||
|
||||
G_GPCupIntoRoundQueue(grandprixinfo.cup, GT_RACE, grandprixinfo.encore);
|
||||
|
||||
roundqueue.position = READUINT8(save->p);
|
||||
UINT8 size = READUINT8(save->p);
|
||||
roundqueue.roundnum = READUINT8(save->p);
|
||||
|
||||
if (roundqueue.size != size)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Cup \"%s\"'s level composition has changed between game launches (differs in level count).\n", cupname);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (roundqueue.position == 0 || roundqueue.position > size)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Position in the round queue is invalid.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT8 i;
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
{
|
||||
UINT32 val = READUINT32(save->p);
|
||||
UINT16 mapnum = roundqueue.entries[i].mapnum;
|
||||
|
||||
if (mapnum < nummapheaders && mapheaderinfo[mapnum] != NULL)
|
||||
{
|
||||
if (mapheaderinfo[mapnum]->lumpnamehash == val)
|
||||
continue;
|
||||
}
|
||||
|
||||
CONS_Alert(CONS_ERROR, "P_UnArchiveSPGame: Cup \"%s\"'s level composition has changed between game launches (differs at level %u).\n", cupname, i);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rank information
|
||||
|
||||
{
|
||||
grandprixinfo.rank.players = READUINT8(save->p);
|
||||
grandprixinfo.rank.totalPlayers = READUINT8(save->p);
|
||||
|
||||
grandprixinfo.rank.position = READUINT8(save->p);
|
||||
grandprixinfo.rank.skin = READUINT8(save->p);
|
||||
|
||||
grandprixinfo.rank.winPoints = READUINT32(save->p);
|
||||
grandprixinfo.rank.totalPoints = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.laps = READUINT32(save->p);
|
||||
grandprixinfo.rank.totalLaps = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.continuesUsed = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.prisons = READUINT32(save->p);
|
||||
grandprixinfo.rank.totalPrisons = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.rings = READUINT32(save->p);
|
||||
grandprixinfo.rank.totalRings = READUINT32(save->p);
|
||||
|
||||
grandprixinfo.rank.specialWon = (boolean)READUINT8(save->p);
|
||||
}
|
||||
|
||||
// Marathon information
|
||||
|
||||
marathonmode = READUINT8(save->p);
|
||||
marathontime = READUINT32(save->p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
|
||||
|
|
@ -5695,9 +5983,9 @@ static inline void P_NetUnArchiveRNG(savebuffer_t *save)
|
|||
}
|
||||
}
|
||||
|
||||
void P_SaveGame(savebuffer_t *save, INT16 mapnum)
|
||||
void P_SaveGame(savebuffer_t *save)
|
||||
{
|
||||
P_ArchiveMisc(save, mapnum);
|
||||
P_ArchiveMisc(save);
|
||||
P_ArchivePlayer(save);
|
||||
P_ArchiveLuabanksAndConsistency(save);
|
||||
}
|
||||
|
|
@ -5752,7 +6040,7 @@ void P_SaveNetGame(savebuffer_t *save, boolean resending)
|
|||
P_ArchiveLuabanksAndConsistency(save);
|
||||
}
|
||||
|
||||
boolean P_LoadGame(savebuffer_t *save, INT16 mapoverride)
|
||||
boolean P_LoadGame(savebuffer_t *save)
|
||||
{
|
||||
if (gamestate == GS_INTERMISSION)
|
||||
Y_EndIntermission();
|
||||
|
|
@ -5760,17 +6048,24 @@ boolean P_LoadGame(savebuffer_t *save, INT16 mapoverride)
|
|||
Y_EndVote();
|
||||
G_SetGamestate(GS_NULL); // should be changed in P_UnArchiveMisc
|
||||
|
||||
P_UnArchiveSPGame(save, mapoverride);
|
||||
P_UnArchivePlayer(save);
|
||||
if (!P_UnArchiveSPGame(save))
|
||||
goto badloadgame;
|
||||
if (!P_UnArchivePlayer(save))
|
||||
goto badloadgame;
|
||||
|
||||
if (!P_UnArchiveLuabanksAndConsistency(save))
|
||||
return false;
|
||||
|
||||
// Only do this after confirming savegame is ok
|
||||
G_DeferedInitNew(false, gamemap, savedata.skin, 0, true);
|
||||
COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
|
||||
goto badloadgame;
|
||||
|
||||
return true;
|
||||
|
||||
badloadgame:
|
||||
// these are the side effects of P_UnarchiveSPGame
|
||||
savedata.lives = 0;
|
||||
roundqueue.size = 0;
|
||||
grandprixinfo.gp = false;
|
||||
marathonmode = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean P_LoadNetGame(savebuffer_t *save, boolean reloading)
|
||||
|
|
|
|||
|
|
@ -31,24 +31,51 @@ extern "C" {
|
|||
// Persistent storage/archiving.
|
||||
// These are the load / save game routines.
|
||||
|
||||
void P_SaveGame(savebuffer_t *save, INT16 mapnum);
|
||||
// Local Play
|
||||
void P_SaveGame(savebuffer_t *save);
|
||||
boolean P_LoadGame(savebuffer_t *save);
|
||||
void P_GetBackupCupData(savebuffer_t *save);
|
||||
|
||||
// Online
|
||||
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);
|
||||
|
||||
struct savedata_bot_s
|
||||
{
|
||||
boolean valid;
|
||||
UINT8 skin;
|
||||
UINT8 difficulty;
|
||||
boolean rival;
|
||||
UINT32 score;
|
||||
};
|
||||
|
||||
struct savedata_t
|
||||
{
|
||||
UINT32 score;
|
||||
SINT8 lives;
|
||||
UINT16 totalring;
|
||||
|
||||
UINT8 skin;
|
||||
INT32 score;
|
||||
INT32 lives;
|
||||
UINT16 emeralds;
|
||||
UINT8 numgameovers;
|
||||
UINT16 skincolor;
|
||||
INT32 followerskin;
|
||||
UINT16 followercolor;
|
||||
|
||||
struct savedata_bot_s bots[MAXPLAYERS];
|
||||
};
|
||||
|
||||
extern savedata_t savedata;
|
||||
|
||||
struct savedata_cup_t
|
||||
{
|
||||
cupheader_t *cup;
|
||||
UINT8 difficulty;
|
||||
boolean encore;
|
||||
};
|
||||
|
||||
extern savedata_cup_t cupsavedata;
|
||||
|
||||
struct savebuffer_t
|
||||
{
|
||||
UINT8 *buffer;
|
||||
|
|
|
|||
|
|
@ -438,6 +438,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num)
|
|||
mapheaderinfo[num]->mobj_scale = FRACUNIT;
|
||||
mapheaderinfo[num]->default_waypoint_radius = 0;
|
||||
mapheaderinfo[num]->light_contrast = 16;
|
||||
mapheaderinfo[num]->sprite_backlight = 0;
|
||||
mapheaderinfo[num]->use_light_angle = false;
|
||||
mapheaderinfo[num]->light_angle = 0;
|
||||
#if 1 // equivalent to "Followers = DEFAULT"
|
||||
|
|
@ -6281,6 +6282,8 @@ static void P_ConvertBinaryLinedefTypes(void)
|
|||
if (lines[i].flags & ML_BLOCKMONSTERS)
|
||||
lines[i].args[1] |= TMSAF_MIRROR;
|
||||
|
||||
lines[i].args[2] = tag;
|
||||
|
||||
lines[i].special = LT_SLOPE_ANCHORS;
|
||||
break;
|
||||
}
|
||||
|
|
@ -6956,7 +6959,11 @@ static void P_ConvertBinaryThingTypes(void)
|
|||
mapthings[i].args[0] = !!(mapthings[i].options & MTF_AMBUSH);
|
||||
break;
|
||||
case 1488: // Follower Audience (unfortunately numbered)
|
||||
mapthings[i].args[2] = !!(mapthings[i].options & MTF_OBJECTSPECIAL);
|
||||
if (mapthings[i].options & MTF_OBJECTSPECIAL)
|
||||
mapthings[i].args[2] |= TMAUDIM_FLOAT;
|
||||
if (mapthings[i].options & MTF_EXTRA)
|
||||
mapthings[i].args[2] |= TMAUDIM_BORED;
|
||||
|
||||
mapthings[i].args[3] = !!(mapthings[i].options & MTF_AMBUSH);
|
||||
break;
|
||||
case 1500: //Glaregoyle
|
||||
|
|
@ -7140,7 +7147,7 @@ static void P_ConvertBinaryThingTypes(void)
|
|||
break;
|
||||
case FLOOR_SLOPE_THING:
|
||||
case CEILING_SLOPE_THING:
|
||||
mapthings[i].tid = mapthings[i].extrainfo;
|
||||
mapthings[i].args[0] = mapthings[i].extrainfo;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -7727,7 +7734,12 @@ static void P_InitGametype(void)
|
|||
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
if (grandprixinfo.initalize == true)
|
||||
if (savedata.lives > 0)
|
||||
{
|
||||
K_LoadGrandPrixSaveGame();
|
||||
savedata.lives = 0;
|
||||
}
|
||||
else if (grandprixinfo.initalize == true)
|
||||
{
|
||||
K_InitGrandPrixRank(&grandprixinfo.rank);
|
||||
K_InitGrandPrixBots();
|
||||
|
|
@ -7739,7 +7751,7 @@ static void P_InitGametype(void)
|
|||
grandprixinfo.wonround = false;
|
||||
}
|
||||
}
|
||||
else if (!modeattacking)
|
||||
else
|
||||
{
|
||||
// We're in a Match Race, use simplistic randomized bots.
|
||||
K_UpdateMatchRaceBots();
|
||||
|
|
@ -8145,15 +8157,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
R_InitMobjInterpolators();
|
||||
P_InitCachedActions();
|
||||
|
||||
if (!fromnetsave && savedata.lives > 0)
|
||||
{
|
||||
numgameovers = savedata.numgameovers;
|
||||
players[consoleplayer].lives = savedata.lives;
|
||||
players[consoleplayer].score = savedata.score;
|
||||
emeralds = savedata.emeralds;
|
||||
savedata.lives = 0;
|
||||
}
|
||||
|
||||
// internal game map
|
||||
maplumpname = mapheaderinfo[gamemap-1]->lumpname;
|
||||
lastloadedmaplumpnum = mapheaderinfo[gamemap-1]->lumpnum;
|
||||
|
|
@ -8304,22 +8307,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
|
||||
P_MapEnd(); // tm.thing is no longer needed from this point onwards
|
||||
|
||||
// Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap...
|
||||
if (gamestate == GS_LEVEL)
|
||||
{
|
||||
if (!lastmaploaded) // Start a new game?
|
||||
{
|
||||
// I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020
|
||||
if (!(ultimatemode || netgame || multiplayer || demo.playback || demo.recording || metalrecording || modeattacking || marathonmode)
|
||||
&& !usedCheats && cursaveslot > 0)
|
||||
{
|
||||
G_SaveGame((UINT32)cursaveslot, gamemap);
|
||||
}
|
||||
// If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted.
|
||||
}
|
||||
lastmaploaded = gamemap; // HAS to be set after saving!!
|
||||
}
|
||||
|
||||
if (!fromnetsave)
|
||||
{
|
||||
INT32 buf = gametic % BACKUPTICS;
|
||||
|
|
@ -8375,6 +8362,8 @@ void P_PostLoadLevel(void)
|
|||
|
||||
P_RunCachedActions();
|
||||
|
||||
G_HandleSaveLevel(gamestate == GS_CEREMONY);
|
||||
|
||||
if (marathonmode & MA_INGAME)
|
||||
{
|
||||
marathonmode &= ~MA_INIT;
|
||||
|
|
|
|||
|
|
@ -855,6 +855,8 @@ void P_SpawnSlopes(const boolean fromsave) {
|
|||
/// Setup anchor based slopes.
|
||||
P_SetupAnchoredSlopes();
|
||||
|
||||
// end of jart
|
||||
|
||||
/// Copies slopes from tagged sectors via line specials.
|
||||
/// \note Doesn't actually copy, but instead they share the same pointers.
|
||||
for (i = 0; i < numlines; i++)
|
||||
|
|
|
|||
|
|
@ -6732,6 +6732,7 @@ void P_InitSpecials(void)
|
|||
|
||||
// Set map lighting settings.
|
||||
maplighting.contrast = mapheaderinfo[gamemap-1]->light_contrast;
|
||||
maplighting.backlight = mapheaderinfo[gamemap-1]->sprite_backlight;
|
||||
maplighting.directional = mapheaderinfo[gamemap-1]->use_light_angle;
|
||||
maplighting.angle = mapheaderinfo[gamemap-1]->light_angle;
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,12 @@ typedef enum
|
|||
TMWPF_FINISHLINE = 1<<3,
|
||||
} textmapwaypointflags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TMAUDIM_FLOAT = 1,
|
||||
TMAUDIM_BORED = 1<<1,
|
||||
} textmapaudiencemovementflags_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TMBCF_BACKANDFORTH = 1,
|
||||
|
|
|
|||
|
|
@ -4609,6 +4609,11 @@ void P_PlayerAfterThink(player_t *player)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (P_IsObjectOnGround(player->mo) == true)
|
||||
{
|
||||
player->outrun = 0;
|
||||
}
|
||||
|
||||
#ifdef SECTORSPECIALSAFTERTHINK
|
||||
if (player->onconveyor != 1 || !P_IsObjectOnGround(player->mo))
|
||||
player->onconveyor = 0;
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ static void rasterize_segment_tex(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32
|
|||
void R_DrawFloorSplat(vissprite_t *spr)
|
||||
{
|
||||
floorsplat_t splat;
|
||||
pslope_t localslope;
|
||||
mobj_t *mobj = spr->mobj;
|
||||
fixed_t tr_x, tr_y, rot_x, rot_y, rot_z;
|
||||
|
||||
|
|
@ -258,6 +259,9 @@ void R_DrawFloorSplat(vissprite_t *spr)
|
|||
|
||||
if (standingslope && (renderflags & RF_OBJECTSLOPESPLAT))
|
||||
splat.slope = standingslope;
|
||||
|
||||
if (!splat.slope && R_SplatSlope(mobj, (vector3_t){splat.x, splat.y, splat.z}, &localslope))
|
||||
splat.slope = &localslope;
|
||||
}
|
||||
|
||||
// Translate
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
/// \brief Special effects for sprite rendering
|
||||
|
||||
#include "d_player.h"
|
||||
#include "info.h"
|
||||
#include "p_tick.h"
|
||||
#include "r_splats.h"
|
||||
#include "r_things.h"
|
||||
|
||||
INT32 R_ThingLightLevel(mobj_t* thing)
|
||||
|
|
@ -29,3 +31,31 @@ INT32 R_ThingLightLevel(mobj_t* thing)
|
|||
|
||||
return lightlevel;
|
||||
}
|
||||
|
||||
// Use this function to set the slope of a splat sprite.
|
||||
//
|
||||
// slope->o, slope->d and slope->zdelta must be set, none of
|
||||
// the other fields on pslope_t are used.
|
||||
//
|
||||
// Return true if you want the slope to be used. The object
|
||||
// must have RF_SLOPESPLAT and mobj_t.floorspriteslope must be
|
||||
// NULL. (If RF_OBJECTSLOPESPLAT is set, then
|
||||
// mobj_t.standingslope must also be NULL.)
|
||||
boolean R_SplatSlope(mobj_t* mobj, vector3_t position, pslope_t* slope)
|
||||
{
|
||||
switch (mobj->type)
|
||||
{
|
||||
case MT_INSTAWHIP_RECHARGE: {
|
||||
// Create an acute angle
|
||||
slope->o = position;
|
||||
FV2_Load(&slope->d, FCOS(mobj->angle) / 2, FSIN(mobj->angle) / 2);
|
||||
slope->zdelta = FRACUNIT;
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1630,6 +1630,26 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
|
|||
box->x2test = 0;
|
||||
}
|
||||
|
||||
static fixed_t R_GetSpriteDirectionalLighting(angle_t angle)
|
||||
{
|
||||
// Copied from P_UpdateSegLightOffset
|
||||
const UINT8 contrast = min(max(0, maplighting.contrast - maplighting.backlight), UINT8_MAX);
|
||||
const fixed_t contrastFixed = ((fixed_t)contrast) * FRACUNIT;
|
||||
|
||||
fixed_t light = FRACUNIT;
|
||||
fixed_t extralight = 0;
|
||||
|
||||
light = FixedMul(FINECOSINE(angle >> ANGLETOFINESHIFT), FINECOSINE(maplighting.angle >> ANGLETOFINESHIFT))
|
||||
+ FixedMul(FINESINE(angle >> ANGLETOFINESHIFT), FINESINE(maplighting.angle >> ANGLETOFINESHIFT));
|
||||
light = (light + FRACUNIT) / 2;
|
||||
|
||||
light = FixedMul(light, FRACUNIT - FSIN(abs(AngleDeltaSigned(angle, maplighting.angle)) / 2));
|
||||
|
||||
extralight = -contrastFixed + FixedMul(light, contrastFixed * 2);
|
||||
|
||||
return extralight;
|
||||
}
|
||||
|
||||
//
|
||||
// R_ProjectSprite
|
||||
// Generates a vissprite for a thing
|
||||
|
|
@ -2275,6 +2295,38 @@ static void R_ProjectSprite(mobj_t *thing)
|
|||
|
||||
lightnum = (lightnum + R_ThingLightLevel(oldthing)) >> LIGHTSEGSHIFT;
|
||||
|
||||
if (maplighting.directional == true)
|
||||
{
|
||||
fixed_t extralight = R_GetSpriteDirectionalLighting(papersprite
|
||||
? interp.angle + (ang >= ANGLE_180 ? -ANGLE_90 : ANGLE_90)
|
||||
: R_PointToAngle(interp.x, interp.y));
|
||||
|
||||
// Less change in contrast in dark sectors
|
||||
extralight = FixedMul(extralight, min(max(0, lightnum), LIGHTLEVELS - 1) * FRACUNIT / (LIGHTLEVELS - 1));
|
||||
|
||||
if (papersprite)
|
||||
{
|
||||
// Papersprite contrast should match walls
|
||||
lightnum += FixedFloor((extralight / 8) + (FRACUNIT / 2)) / FRACUNIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed_t n = FixedDiv(FixedMul(xscale, LIGHTRESOLUTIONFIX), ((MAXLIGHTSCALE-1) << LIGHTSCALESHIFT));
|
||||
|
||||
// Less change in contrast at further distances, to counteract DOOM diminished light
|
||||
extralight = FixedMul(extralight, min(n, FRACUNIT));
|
||||
|
||||
// Contrast is stronger for normal sprites, stronger than wall lighting is at the same distance
|
||||
lightnum += FixedFloor((extralight / 4) + (FRACUNIT / 2)) / FRACUNIT;
|
||||
}
|
||||
|
||||
// Semibright objects will be made slightly brighter to compensate contrast
|
||||
if (R_ThingIsSemiBright(oldthing))
|
||||
{
|
||||
lightnum += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (lightnum < 0)
|
||||
lights_array = scalelight[0];
|
||||
else if (lightnum >= LIGHTLEVELS)
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ boolean R_ThingIsFullDark (mobj_t *thing);
|
|||
boolean R_ThingIsFlashing(mobj_t *thing);
|
||||
|
||||
INT32 R_ThingLightLevel(mobj_t *thing);
|
||||
boolean R_SplatSlope(mobj_t *thing, vector3_t position, pslope_t *slope);
|
||||
|
||||
// --------------
|
||||
// MASKED DRAWING
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ get_anchor
|
|||
mapthing_t ** anchors,
|
||||
fixed_t distances[3],
|
||||
const struct anchor_list * list,
|
||||
const mtag_t tag,
|
||||
const INT32 group,
|
||||
const vertex_t * v
|
||||
){
|
||||
size_t i;
|
||||
|
|
@ -199,7 +199,7 @@ get_anchor
|
|||
|
||||
for (i = 0; i < list->count; ++i)
|
||||
{
|
||||
if (list->points[i] == v && list->anchors[i]->tid == tag)
|
||||
if (list->points[i] == v && list->anchors[i]->args[0] == group)
|
||||
{
|
||||
for (k = 0; k < 3; ++k)
|
||||
{
|
||||
|
|
@ -240,15 +240,15 @@ get_sector_anchors
|
|||
mapthing_t ** anchors,
|
||||
fixed_t distances[3],
|
||||
const struct anchor_list * list,
|
||||
const mtag_t tag,
|
||||
const INT32 group,
|
||||
const sector_t * sector
|
||||
){
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sector->linecount; ++i)
|
||||
{
|
||||
get_anchor(anchors, distances, list, tag, sector->lines[i]->v1);
|
||||
get_anchor(anchors, distances, list, tag, sector->lines[i]->v2);
|
||||
get_anchor(anchors, distances, list, group, sector->lines[i]->v1);
|
||||
get_anchor(anchors, distances, list, group, sector->lines[i]->v2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -257,7 +257,7 @@ find_closest_anchors
|
|||
(
|
||||
const sector_t * sector,
|
||||
const struct anchor_list * list,
|
||||
const mtag_t tag
|
||||
const INT32 group
|
||||
){
|
||||
fixed_t distances[3] = { INT32_MAX, INT32_MAX, INT32_MAX };
|
||||
|
||||
|
|
@ -279,12 +279,12 @@ find_closest_anchors
|
|||
for (i = 0; i < sector->numattached; ++i)
|
||||
{
|
||||
get_sector_anchors
|
||||
(anchors, distances, list, tag, §ors[sector->attached[i]]);
|
||||
(anchors, distances, list, group, §ors[sector->attached[i]]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
get_sector_anchors(anchors, distances, list, tag, sector);
|
||||
get_sector_anchors(anchors, distances, list, group, sector);
|
||||
}
|
||||
|
||||
if (distances[2] < INT32_MAX)
|
||||
|
|
@ -310,13 +310,13 @@ find_closest_anchors
|
|||
|
||||
I_Error(
|
||||
"(Control Sector #%s)"
|
||||
" Slope requires anchors (with Parameter %d)"
|
||||
" Slope requires anchors (with group ID %d)"
|
||||
" near 3 of its target sectors' vertices (%d found)"
|
||||
|
||||
"\n\nCheck the log to see which sectors were searched.",
|
||||
|
||||
sizeu1 (sector - sectors),
|
||||
tag,
|
||||
group,
|
||||
last
|
||||
);
|
||||
}
|
||||
|
|
@ -324,11 +324,11 @@ find_closest_anchors
|
|||
{
|
||||
I_Error(
|
||||
"(Sector #%s)"
|
||||
" Slope requires anchors (with Parameter %d)"
|
||||
" Slope requires anchors (with group ID %d)"
|
||||
" near 3 of its vertices (%d found)",
|
||||
|
||||
sizeu1 (sector - sectors),
|
||||
tag,
|
||||
group,
|
||||
last
|
||||
);
|
||||
}
|
||||
|
|
@ -402,9 +402,9 @@ slope_sector
|
|||
sector_t * sector,
|
||||
const INT16 flags,
|
||||
const struct anchor_list * list,
|
||||
const mtag_t tag
|
||||
const INT32 group
|
||||
){
|
||||
mapthing_t ** anchors = find_closest_anchors(sector, list, tag);
|
||||
mapthing_t ** anchors = find_closest_anchors(sector, list, group);
|
||||
|
||||
if (anchors != NULL)
|
||||
{
|
||||
|
|
@ -432,7 +432,7 @@ make_anchored_slope
|
|||
|
||||
sector_t * s;
|
||||
|
||||
mtag_t tag = Tag_FGet(&line->tags);
|
||||
INT32 group = line->args[2];
|
||||
|
||||
if (side == 0 || (line->flags & ML_TWOSIDED))
|
||||
{
|
||||
|
|
@ -446,17 +446,44 @@ make_anchored_slope
|
|||
if (plane & TMSA_FLOOR)
|
||||
{
|
||||
slope_sector
|
||||
(&s->f_slope, &s->c_slope, s, flags, &floor_anchors, tag);
|
||||
(&s->f_slope, &s->c_slope, s, flags, &floor_anchors, group);
|
||||
}
|
||||
|
||||
if (plane & TMSA_CEILING)
|
||||
{
|
||||
slope_sector
|
||||
(&s->c_slope, &s->f_slope, s, flags, &ceiling_anchors, tag);
|
||||
(&s->c_slope, &s->f_slope, s, flags, &ceiling_anchors, group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
make_anchored_slope_from_sector
|
||||
(
|
||||
sector_t * s,
|
||||
const int plane
|
||||
){
|
||||
INT16 flags = s->args[1];
|
||||
INT32 group = s->args[2];
|
||||
|
||||
if (plane == (TMSA_FLOOR|TMSA_CEILING))
|
||||
{
|
||||
flags &= ~TMSAF_MIRROR;
|
||||
}
|
||||
|
||||
if (plane & TMSA_FLOOR)
|
||||
{
|
||||
slope_sector
|
||||
(&s->f_slope, &s->c_slope, s, flags, &floor_anchors, group);
|
||||
}
|
||||
|
||||
if (plane & TMSA_CEILING)
|
||||
{
|
||||
slope_sector
|
||||
(&s->c_slope, &s->f_slope, s, flags, &ceiling_anchors, group);
|
||||
}
|
||||
}
|
||||
|
||||
static void P_BuildSlopeAnchorList (void) {
|
||||
allocate_anchors();
|
||||
build_anchors();
|
||||
|
|
@ -480,4 +507,20 @@ static void P_SetupAnchoredSlopes (void) {
|
|||
make_anchored_slope(&lines[i], plane);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < numsectors; ++i)
|
||||
{
|
||||
if (sectors[i].action == LT_SLOPE_ANCHORS)
|
||||
{
|
||||
int plane = (sectors[i].args[0] & (TMSA_FLOOR|TMSA_CEILING));
|
||||
|
||||
if (plane == 0)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "Slope anchor sector %s has no planes set.\n", sizeu1(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
make_anchored_slope_from_sector(§ors[i], plane);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -297,6 +297,7 @@ TYPEDEF (polyfadedata_t);
|
|||
|
||||
// p_saveg.h
|
||||
TYPEDEF (savedata_t);
|
||||
TYPEDEF (savedata_cup_t);
|
||||
TYPEDEF (savebuffer_t);
|
||||
|
||||
// p_setup.h
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue