Merge branch 'next' into awayview-fix

This commit is contained in:
SMS Alfredo 2023-06-30 16:13:15 -05:00
commit ebdc36f56b
59 changed files with 4130 additions and 7270 deletions

View file

@ -15,7 +15,7 @@ common
ignoredextensions = "wad pk3 pk7 bak backup1 backup2 backup3 zip rar 7z";
// Default testing parameters
testparameters = "-file \"%AP\" \"%F\" -warp %L";
testparameters = "-folder \"%AF\" -file \"%AA\" \"%F\" -warp %L";
testshortpaths = true;
// Action special help
@ -26,7 +26,7 @@ common
generalizedsectors = true;
// Maximum safe map size check (0 means skip check)
safeboundary = 1;
safeboundary = 0;
// Map boundaries. Map objects can only be placed within these boundaries
leftboundary = -32768;
@ -40,6 +40,8 @@ common
defaultflatscale = 1.0f;
scaledtextureoffsets = true;
maxcolormapalpha = 25;
// Thing number for start position in 3D Mode
start3dmode = 3328;
@ -68,137 +70,6 @@ common
}
}
mapformat_doom
{
// The format interface handles the map data format
formatinterface = "DoomMapSetIO";
// Default nodebuilder configurations
defaultsavecompiler = "zennode_normal";
defaulttestcompiler = "zennode_fast";
/*
GAME DETECT PATTERN
Used to guess the game for which a WAD file is made.
1 = One of these lumps must exist
2 = None of these lumps must exist
3 = All of these lumps must exist
*/
gamedetect
{
EXTENDED = 2;
BEHAVIOR = 2;
E#M# = 2;
MAP?? = 1;
}
/*
MAP LUMP NAMES
Map lumps are loaded with the map as long as they are right after each other. When the editor
meets a lump which is not defined in this list it will ignore the map if not satisfied.
The order of items defines the order in which lumps will be written to WAD file on save.
To indicate the map header lump, use ~MAP
Legenda:
required = Lump is required to exist.
blindcopy = Lump will be copied along with the map blindly. (usefull for lumps Doom Builder doesn't use)
nodebuild = The nodebuilder generates this lump.
allowempty = The nodebuilder is allowed to leave this lump empty.
script = This lump is a text-based script. Specify the filename of the script configuration to use.
*/
maplumpnames
{
include("SRB222_misc.cfg", "doommaplumpnames");
}
// When this is set to true, sectors with the same tag will light up when a line is highlighted
linetagindicatesectors = true;
// Special linedefs
include("SRB222_misc.cfg", "speciallinedefs");
// Default flags for first new thing
defaultthingflags
{
}
// DEFAULT SECTOR BRIGHTNESS LEVELS
sectorbrightness
{
include("SRB222_misc.cfg", "sectorbrightness");
}
// SECTOR TYPES
sectortypes
{
include("SRB222_sectors.cfg", "sectortypes");
}
// GENERALISED SECTOR TYPES
gen_sectortypes
{
include("SRB222_sectors.cfg", "gen_sectortypes");
}
// LINEDEF FLAGS
linedefflags
{
include("SRB222_misc.cfg", "linedefflags");
}
// Linedef flags UDMF translation table
// This is needed for copy/paste and prefabs to work properly
// When the UDMF field name is prefixed with ! it is inverted
linedefflagstranslation
{
include("SRB222_misc.cfg", "linedefflagstranslation");
}
// LINEDEF ACTIVATIONS
linedefactivations
{
}
// LINEDEF TYPES
linedeftypes
{
include("SRB222_linedefs.cfg", "doom");
}
// THING FLAGS
thingflags
{
include("SRB222_misc.cfg", "thingflags");
}
// Thing flags UDMF translation table
// This is needed for copy/paste and prefabs to work properly
// When the UDMF field name is prefixed with ! it is inverted
thingflagstranslation
{
include("SRB222_misc.cfg", "thingflagstranslation");
}
// THING FLAGS ERROR MASK
// Mask for the thing flags which indicates the options
// that make the same thing appear in the same modes
thingflagsmask1 = 7; // 1 + 2 + 4
thingflagsmask2 = 0;
// THING TYPES
thingtypes
{
include("SRB222_things.cfg", "doom");
}
}
mapformat_udmf
{
// The format interface handles the map data format
@ -222,9 +93,17 @@ mapformat_udmf
{
include("SRB222_misc.cfg", "universalfields");
}
// Disable Doom-related modes that don't make sense for SRB2
soundsupport = false;
automapsupport = false;
// When this is set to true, sectors with the same tag will light up when a line is highlighted
linetagindicatesectors = false;
localsidedeftextureoffsets = true;
distinctfloorandceilingbrightness = true;
planeequationsupport = true;
// Special linedefs
include("SRB222_misc.cfg", "speciallinedefs_udmf");
@ -240,6 +119,11 @@ mapformat_udmf
include("SRB222_misc.cfg", "sectorflags");
}
sectorflagscategories
{
include("SRB222_misc.cfg", "sectorflagscategories");
}
// DEFAULT SECTOR BRIGHTNESS LEVELS
sectorbrightness
{
@ -247,6 +131,7 @@ mapformat_udmf
}
damagetypes = "Generic Water Fire Lava Electric Spike DeathPitTilt DeathPitNoTilt Instakill SpecialStage";
triggerertypes = "Player AllPlayers Mobj";
// LINEDEF FLAGS
linedefflags
@ -282,7 +167,6 @@ mapformat_udmf
// How to compare thing flags (for the stuck things error checker)
thingflagscompare
{
include("UDMF_misc.cfg", "thingflagscompare");
}
// THING TYPES

File diff suppressed because it is too large Load diff

View file

@ -1,24 +1,3 @@
linedefflags
{
1 = "[0] Impassable";
2 = "[1] Block Enemies";
4 = "[2] Double-Sided";
8 = "[3] Upper Unpegged";
16 = "[4] Lower Unpegged";
32 = "[5] Slope Skew (E1)";
64 = "[6] Not Climbable";
128 = "[7] No Midtexture Skew (E2)";
256 = "[8] Peg Midtexture (E3)";
512 = "[9] Solid Midtexture (E4)";
1024 = "[10] Repeat Midtexture (E5)";
2048 = "[11] Netgame Only";
4096 = "[12] No Netgame";
8192 = "[13] Effect 6";
16384 = "[14] Bouncy Wall";
32768 = "[15] Transfer Line";
}
// Linedef flags UDMF translation table
// This is needed for copy/paste and prefabs to work properly
// When the UDMF field name is prefixed with ! it is inverted
@ -42,7 +21,6 @@ linedefflagstranslation
32768 = "transfer";
}
linedefflags_udmf
{
blocking = "Impassable";
@ -74,19 +52,13 @@ linedefrenderstyles
sectorflags
{
colormapfog = "Fog Planes in Colormap";
colormapfadesprites = "Fade Fullbright in Colormap";
colormapprotected = "Protected Colormap";
flipspecial_nofloor = "No Trigger on Floor Touch";
flipspecial_ceiling = "Trigger on Ceiling Touch";
triggerspecial_touch = "Trigger on Edge Touch";
triggerspecial_headbump = "Trigger on Headbump";
triggerline_plane = "Linedef Trigger Requires Plane Touch";
triggerline_mobj = "Non-Pushables Can Trigger Linedef";
invertprecip = "Invert Precipitation";
gravityflip = "Flip Objects in Reverse Gravity";
heatwave = "Heat Wave";
noclipcamera = "Intangible to the Camera";
colormapfog = "Fog Planes";
colormapfadesprites = "Fade Fullbright";
colormapprotected = "Protected from Tagging";
outerspace = "Space Countdown";
doublestepup = "Ramp Sector (double step-up/down)";
nostepdown = "Non-Ramp Sector (No step-down)";
@ -104,23 +76,59 @@ sectorflags
zoomtubeend = "Zoom Tube End";
finishline = "Circuit Finish Line";
ropehang = "Rope Hang";
jumpflip = "Flip Gravity on Jump";
gravityoverride = "Make Reverse Gravity Temporary";
flipspecial_nofloor = "No Trigger on Floor Touch";
flipspecial_ceiling = "Trigger on Ceiling Touch";
triggerspecial_touch = "Trigger on Edge Touch";
triggerspecial_headbump = "Trigger on Headbump";
triggerline_plane = "Linedef Trigger Requires Plane Touch";
triggerline_mobj = "Non-Pushables Can Trigger Linedef";
}
thingflags
sectorflagscategories
{
1 = "[1] Extra";
2 = "[2] Flip";
4 = "[4] Special";
8 = "[8] Ambush";
invertprecip = "regular";
gravityflip = "regular";
heatwave = "regular";
noclipcamera = "regular";
colormapfog = "colormap";
colormapfadesprites = "colormap";
colormapprotected = "colormap";
outerspace = "special";
doublestepup = "special";
nostepdown = "special";
speedpad = "special";
starpostactivator = "special";
exit = "special";
specialstagepit = "special";
returnflag = "special";
redteambase = "special";
blueteambase = "special";
fan = "special";
supertransform = "special";
forcespin = "special";
zoomtubestart = "special";
zoomtubeend = "special";
finishline = "special";
ropehang = "special";
jumpflip = "special";
gravityoverride = "special";
flipspecial_nofloor = "trigger";
flipspecial_ceiling = "trigger";
triggerspecial_touch = "trigger";
triggerspecial_headbump = "trigger";
triggerline_plane = "trigger";
triggerline_mobj = "trigger";
}
// THING FLAGS
thingflags_udmf
{
flip = "Flip";
absolutez = "Absolute Z height";
}
// Thing flags UDMF translation table
// This is needed for copy/paste and prefabs to work properly
// When the UDMF field name is prefixed with ! it is inverted
@ -130,9 +138,9 @@ thingflagstranslation
2 = "flip";
4 = "special";
8 = "ambush";
16 = "absolutez";
}
// DEFAULT SECTOR BRIGHTNESS LEVELS
sectorbrightness
{
@ -171,6 +179,8 @@ sectorbrightness
0;
}
numbrightnesslevels = 32;
/*
TEXTURES AND FLAT SOURCES
This tells Doom Builder where to find the information for textures
@ -221,145 +231,18 @@ universalfields
{
sector
{
lightalpha
{
type = 0;
default = 25;
}
fadealpha
{
type = 0;
default = 25;
}
fadestart
{
type = 0;
default = 0;
}
fadeend
{
type = 0;
default = 33;
}
foglighting
{
type = 3;
default = false;
}
friction
{
type = 1;
default = 0.90625;
}
triggertag
{
type = 15;
default = 0;
}
triggerer
{
type = 2;
default = "Player";
}
}
linedef
{
arg5
{
type = 0;
default = 0;
}
arg6
{
type = 0;
default = 0;
}
arg7
{
type = 0;
default = 0;
}
arg8
{
type = 0;
default = 0;
}
arg9
{
type = 0;
default = 0;
}
stringarg0
{
type = 2;
default = "";
}
stringarg1
{
type = 2;
default = "";
}
executordelay
{
type = 0;
default = 0;
}
}
sidedef
{
repeatcnt
{
type = 0;
default = 0;
}
}
thing
{
arg5
{
type = 0;
default = 0;
}
arg6
{
type = 0;
default = 0;
}
arg7
{
type = 0;
default = 0;
}
arg8
{
type = 0;
default = 0;
}
arg9
{
type = 0;
default = 0;
}
stringarg0
{
type = 2;
default = "";
}
stringarg1
{
type = 2;
default = "";
}
}
}
@ -378,87 +261,6 @@ allowempty = The nodebuilder is allowed to leave this lump empty.
scriptbuild = This lump is a text-based script, which should be compiled using current script compiler;
script = This lump is a text-based script. Specify the filename of the script configuration to use.
*/
doommaplumpnames
{
~MAP
{
required = true;
blindcopy = true;
nodebuild = false;
}
THINGS
{
required = true;
nodebuild = true;
allowempty = true;
}
LINEDEFS
{
required = true;
nodebuild = true;
allowempty = false;
}
SIDEDEFS
{
required = true;
nodebuild = true;
allowempty = false;
}
VERTEXES
{
required = true;
nodebuild = true;
allowempty = false;
}
SEGS
{
required = false;
nodebuild = true;
allowempty = false;
}
SSECTORS
{
required = false;
nodebuild = true;
allowempty = false;
}
NODES
{
required = false;
nodebuild = true;
allowempty = false;
}
SECTORS
{
required = true;
nodebuild = true;
allowempty = false;
}
REJECT
{
required = false;
nodebuild = true;
allowempty = false;
}
BLOCKMAP
{
required = false;
nodebuild = true;
allowempty = true;
}
}
udmfmaplumpnames
{
ZNODES
@ -682,48 +484,32 @@ thingsfilters
}
//filter3
//{
// name = "Normal Gravity";
// category = "";
// type = -1;
//
// fields
// {
// 2 = false;
// }
//}
filter3
{
name = "Normal Gravity";
category = "";
type = -1;
fields
{
2 = false;
}
}
filter4
{
name = "Reverse Gravity";
category = "";
type = -1;
fields
{
2 = true;
}
}
//filter4
//{
// name = "Reverse Gravity";
// category = "";
// type = -1;
//
// fields
// {
// 2 = true;
// }
//}
}
// Special linedefs
speciallinedefs
{
soundlinedefflag = 64; // See linedefflags
singlesidedflag = 1; // See linedefflags
doublesidedflag = 4; // See linedefflags
impassableflag = 1;
upperunpeggedflag = 8;
lowerunpeggedflag = 16;
repeatmidtextureflag = 1024;
pegmidtextureflag = 256;
}
speciallinedefs_udmf
{
soundlinedefflag = "noclimb";
@ -734,6 +520,8 @@ speciallinedefs_udmf
lowerunpeggedflag = "dontpegbottom";
repeatmidtextureflag = "wrapmidtex";
pegmidtextureflag = "midpeg";
slopeskewflag = "skewtd";
nomidtextureskewflag = "noskew";
}
scriptlumpnames

View file

@ -1,107 +0,0 @@
sectortypes
{
0 = "Normal";
1 = "Damage";
2 = "Damage (Water)";
3 = "Damage (Fire)";
4 = "Damage (Electrical)";
5 = "Spikes";
6 = "Death Pit (Camera Tilt)";
7 = "Death Pit (No Camera Tilt)";
8 = "Instant Kill";
9 = "Ring Drainer (Floor Touch)";
10 = "Ring Drainer (Anywhere in Sector)";
11 = "Special Stage Damage";
12 = "Space Countdown";
13 = "Ramp Sector (double step-up/down)";
14 = "Non-Ramp Sector (no step-down)";
15 = "Bouncy FOF <deprecated>";
16 = "Trigger Line Ex. (Pushable Objects)";
32 = "Trigger Line Ex. (Anywhere, All Players)";
48 = "Trigger Line Ex. (Floor Touch, All Players)";
64 = "Trigger Line Ex. (Anywhere in Sector)";
80 = "Trigger Line Ex. (Floor Touch)";
96 = "Trigger Line Ex. (Emerald Check) <deprecated>";
112 = "Trigger Line Ex. (NiGHTS Mare) <deprecated>";
128 = "Check for Linedef Executor on FOFs";
144 = "Egg Capsule";
160 = "Special Stage Time/Spheres Parameters <deprecated>";
176 = "Custom Global Gravity <deprecated>";
1280 = "Speed Pad";
1536 = "Flip Gravity on Jump";
4096 = "Star Post Activator";
8192 = "Exit/Special Stage Pit/Return Flag";
12288 = "CTF Red Team Base";
16384 = "CTF Blue Team Base";
20480 = "Fan Sector";
24576 = "Super Sonic Transform";
28672 = "Force Spin";
32768 = "Zoom Tube Start";
36864 = "Zoom Tube End";
40960 = "Circuit Finish Line";
45056 = "Rope Hang";
49152 = "Intangible to the Camera";
}
gen_sectortypes
{
first
{
0 = "Normal";
1 = "Damage";
2 = "Damage (Water)";
3 = "Damage (Fire)";
4 = "Damage (Electrical)";
5 = "Spikes";
6 = "Death Pit (Camera Tilt)";
7 = "Death Pit (No Camera Tilt)";
8 = "Instant Kill";
9 = "Ring Drainer (Floor Touch)";
10 = "Ring Drainer (Anywhere in Sector)";
11 = "Special Stage Damage";
12 = "Space Countdown";
13 = "Ramp Sector (double step-up/down)";
14 = "Non-Ramp Sector (no step-down)";
15 = "Bouncy FOF <deprecated>";
}
second
{
0 = "Normal";
16 = "Trigger Line Ex. (Pushable Objects)";
32 = "Trigger Line Ex. (Anywhere, All Players)";
48 = "Trigger Line Ex. (Floor Touch, All Players)";
64 = "Trigger Line Ex. (Anywhere in Sector)";
80 = "Trigger Line Ex. (Floor Touch)";
96 = "Trigger Line Ex. (Emerald Check) <deprecated>";
112 = "Trigger Line Ex. (NiGHTS Mare) <deprecated>";
128 = "Check for Linedef Executor on FOFs";
144 = "Egg Capsule";
160 = "Special Stage Time/Spheres Parameters <deprecated>";
176 = "Custom Global Gravity <deprecated>";
}
third
{
0 = "Normal";
1280 = "Speed Pad";
1536 = "Flip Gravity on Jump";
}
fourth
{
0 = "Normal";
4096 = "Star Post Activator";
8192 = "Exit/Special Stage Pit/Return Flag";
12288 = "CTF Red Team Base";
16384 = "CTF Blue Team Base";
20480 = "Fan Sector";
24576 = "Super Sonic Transform";
28672 = "Force Spin";
32768 = "Zoom Tube Start";
36864 = "Zoom Tube End";
40960 = "Circuit Finish Line";
45056 = "Rope Hang";
49152 = "Intangible to the Camera";
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,32 +0,0 @@
/************************************************************************\
Ultimate Doom Builder Game Configuration for Sonic Robo Blast 2 Version 2.2
\************************************************************************/
// This is required to prevent accidental use of a different configuration
type = "Doom Builder 2 Game Configuration";
// This is the title to show for this game
game = "Sonic Robo Blast 2 - 2.2 (Doom format)";
// This is the simplified game engine/sourceport name
engine = "zdoom";
// Settings common to all games and all map formats
include("Includes\\SRB222_common.cfg", "common");
// Settings common to Doom map format
include("Includes\\SRB222_common.cfg", "mapformat_doom");
include("Includes\\Game_SRB222.cfg");
// Script lumps detection
scriptlumpnames
{
include("Includes\\SRB222_misc.cfg", "scriptlumpnames");
}
//Default things filters
thingsfilters
{
include("Includes\\SRB222_misc.cfg", "thingsfilters");
}

View file

@ -395,16 +395,16 @@ static void CON_SetupColormaps(void)
// 0x1 0x3 0x9 0xF
colset(magentamap, 177, 177, 178, 178, 178, 180, 180, 180, 182, 182, 182, 182, 184, 184, 184, 185);
colset(yellowmap, 82, 82, 73, 73, 73, 64, 64, 64, 66, 66, 66, 66, 67, 67, 67, 68);
colset(lgreenmap, 96, 96, 98, 98, 98, 101, 101, 101, 104, 104, 104, 104, 106, 106, 106, 107);
colset(bluemap, 146, 146, 147, 147, 147, 149, 149, 149, 152, 152, 152, 152, 155, 155, 155, 157);
colset(redmap, 32, 32, 33, 33, 33, 35, 35, 35, 39, 39, 39, 39, 42, 42, 42, 44);
colset(yellowmap, 82, 82, 73, 73, 73, 74, 74, 74, 66, 66, 66, 66, 67, 67, 67, 68);
colset(lgreenmap, 96, 96, 98, 98, 98, 100, 100, 100, 103, 103, 103, 103, 105, 105, 105, 107);
colset(bluemap, 146, 146, 147, 147, 147, 148, 148, 148, 149, 149, 149, 149, 150, 150, 150, 151);
colset(redmap, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 35, 37, 37, 37, 39);
colset(graymap, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23);
colset(orangemap, 50, 50, 52, 52, 52, 54, 54, 54, 56, 56, 56, 56, 59, 59, 59, 60);
colset(skymap, 129, 129, 130, 130, 130, 131, 131, 131, 133, 133, 133, 133, 135, 135, 135, 136);
colset(purplemap, 160, 160, 161, 161, 161, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165);
colset(aquamap, 120, 120, 121, 121, 121, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 125);
colset(peridotmap, 72, 72, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191, 94);
colset(peridotmap, 73, 73, 188, 188, 188, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191, 94);
colset(azuremap, 144, 144, 145, 145, 145, 146, 146, 146, 170, 170, 170, 170, 171, 171, 171, 172);
colset(brownmap, 219, 219, 221, 221, 221, 222, 222, 222, 224, 224, 224, 224, 227, 227, 227, 229);
colset(rosymap, 200, 200, 201, 201, 201, 202, 202, 202, 203, 203, 203, 203, 204, 204, 204, 205);

View file

@ -1318,9 +1318,9 @@ static boolean CL_SendJoin(void)
static INT32 FindRejoinerNum(SINT8 node)
{
char strippednodeaddress[64];
char addressbuffer[64];
const char *nodeaddress;
char *port;
const char *strippednodeaddress;
INT32 i;
// Make sure there is no dead dress before proceeding to the stripping
@ -1331,10 +1331,8 @@ static INT32 FindRejoinerNum(SINT8 node)
return -1;
// Strip the address of its port
strcpy(strippednodeaddress, nodeaddress);
port = strchr(strippednodeaddress, ':');
if (port)
*port = '\0';
strcpy(addressbuffer, nodeaddress);
strippednodeaddress = I_NetSplitAddress(addressbuffer, NULL);
// Check if any player matches the stripped address
for (i = 0; i < MAXPLAYERS; i++)
@ -2487,7 +2485,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
{
if (!snake)
{
F_MenuPresTicker(true); // title sky
F_MenuPresTicker(); // title sky
F_TitleScreenTicker(true);
F_TitleScreenDrawer();
}
@ -3645,6 +3643,9 @@ void SV_ResetServer(void)
CV_RevertNetVars();
// Ensure synched when creating a new server
M_CopyGameData(serverGamedata, clientGamedata);
DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n");
}
@ -3768,14 +3769,13 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
if (server && I_GetNodeAddress)
{
char addressbuffer[64];
const char *address = I_GetNodeAddress(node);
char *port = NULL;
if (address) // MI: fix msvcrt.dll!_mbscat crash?
{
strcpy(playeraddress[newplayernum], address);
port = strchr(playeraddress[newplayernum], ':');
if (port)
*port = '\0';
strcpy(addressbuffer, address);
strcpy(playeraddress[newplayernum],
I_NetSplitAddress(addressbuffer, NULL));
}
}
}

View file

@ -1350,6 +1350,9 @@ void D_SRB2Main(void)
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init();
clientGamedata = M_NewGameDataStruct();
serverGamedata = M_NewGameDataStruct();
// Do this up here so that WADs loaded through the command line can use ExecCfg
COM_Init();
@ -1479,7 +1482,9 @@ void D_SRB2Main(void)
// confusion issues when loading mods.
strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename);
}
G_LoadGameData();
G_LoadGameData(clientGamedata);
M_CopyGameData(serverGamedata, clientGamedata);
#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL)
VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen
@ -1710,7 +1715,7 @@ void D_SRB2Main(void)
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
else if (!dedicated && M_MapLocked(pstartmap))
else if (!dedicated && M_MapLocked(pstartmap, serverGamedata))
I_Error("You need to unlock this level before you can warp to it!\n");
else
{

View file

@ -1207,26 +1207,32 @@ static void Internal_FreeNodenum(INT32 nodenum)
(void)nodenum;
}
char *I_NetSplitAddress(char *host, char **port)
{
boolean v4 = (strchr(host, '.') != NULL);
host = strtok(host, v4 ? ":" : "[]");
if (port)
*port = strtok(NULL, ":");
return host;
}
SINT8 I_NetMakeNode(const char *hostname)
{
SINT8 newnode = -1;
if (I_NetMakeNodewPort)
{
char *localhostname = strdup(hostname);
char *t = localhostname;
const char *port;
char *port;
if (!localhostname)
return newnode;
// retrieve portnum from address!
strtok(localhostname, ":");
port = strtok(NULL, ":");
hostname = I_NetSplitAddress(localhostname, &port);
// remove the port in the hostname as we've it already
while ((*t != ':') && (*t != '\0'))
t++;
*t = '\0';
newnode = I_NetMakeNodewPort(localhostname, port);
newnode = I_NetMakeNodewPort(hostname, port);
free(localhostname);
}
return newnode;

View file

@ -2036,7 +2036,7 @@ static void Command_Map_f(void)
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
// lives hell.
if (!dedicated && M_MapLocked(newmapnum))
if (!dedicated && M_MapLocked(newmapnum, serverGamedata))
{
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
Z_Free(realmapname);
@ -3945,18 +3945,12 @@ void ItemFinder_OnChange(void)
if (!cv_itemfinder.value)
return; // it's fine.
if (!M_SecretUnlocked(SECRET_ITEMFINDER))
if (!M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata))
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_itemfinder, 0);
return;
}
else if (netgame || multiplayer)
{
CONS_Printf(M_GetText("This only works in single player.\n"));
CV_StealthSetValue(&cv_itemfinder, 0);
return;
}
}
/** Deals with a pointlimit change by printing the change to the console.
@ -4305,7 +4299,7 @@ void D_GameTypeChanged(INT32 lastgametype)
static void Ringslinger_OnChange(void)
{
if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && cv_ringslinger.value && !cv_debug)
if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && cv_ringslinger.value && !cv_debug)
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));
CV_StealthSetValue(&cv_ringslinger, 0);
@ -4318,7 +4312,7 @@ static void Ringslinger_OnChange(void)
static void Gravity_OnChange(void)
{
if (!M_SecretUnlocked(SECRET_PANDORA) && !netgame && !cv_debug
if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !netgame && !cv_debug
&& strcmp(cv_gravity.string, cv_gravity.defaultvalue))
{
CONS_Printf(M_GetText("You haven't earned this yet.\n"));

View file

@ -10,20 +10,7 @@
/// \file deh_lua.c
/// \brief Lua SOC library
#include "g_game.h"
#include "s_sound.h"
#include "z_zone.h"
#include "m_menu.h"
#include "m_misc.h"
#include "p_local.h"
#include "st_stuff.h"
#include "fastcmp.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "dehacked.h"
#include "deh_lua.h"
#include "deh_tables.h"
// freeslot takes a name (string only!)
// and allocates it to the appropriate free slot.
@ -89,6 +76,8 @@ static inline int lib_freeslot(lua_State *L)
strncpy(sprnames[j],word,4);
//sprnames[j][4] = 0;
used_spr[(j-SPR_FIRSTFREESLOT)/8] |= 1<<(j%8); // Okay, this sprite slot has been named now.
// Lua needs to update the value in _G if it exists
LUA_UpdateSprName(word, j);
lua_pushinteger(L, j);
r++;
break;
@ -219,18 +208,27 @@ static int lib_dummysuper(lua_State *L)
return luaL_error(L, "Can't call super() outside of hardcode-replacing A_Action functions being called by state changes!"); // convoluted, I know. @_@;;
}
static inline int lib_getenum(lua_State *L)
static void CacheAndPushConstant(lua_State *L, const char *name, lua_Integer value)
{
const char *word, *p;
// "cache" into _G
lua_pushstring(L, name);
lua_pushinteger(L, value);
lua_rawset(L, LUA_GLOBALSINDEX);
// push
lua_pushinteger(L, value);
}
// Search for a matching constant variable.
// Result is stored into _G for faster subsequent use. (Except for SPR_ in the SOC parser)
static int ScanConstants(lua_State *L, boolean mathlib, const char *word)
{
const char *p;
fixed_t i;
boolean mathlib = lua_toboolean(L, lua_upvalueindex(1));
if (lua_type(L,2) != LUA_TSTRING)
return 0;
word = lua_tostring(L,2);
if (strlen(word) == 1) { // Assume sprite frame if length 1.
if (*word >= 'A' && *word <= '~')
{
lua_pushinteger(L, *word-'A');
CacheAndPushConstant(L, word, *word-'A');
return 1;
}
if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word);
@ -240,7 +238,7 @@ static inline int lib_getenum(lua_State *L)
p = word+3;
for (i = 0; MOBJFLAG_LIST[i]; i++)
if (fastcmp(p, MOBJFLAG_LIST[i])) {
lua_pushinteger(L, ((lua_Integer)1<<i));
CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
return 1;
}
if (mathlib) return luaL_error(L, "mobjflag '%s' could not be found.\n", word);
@ -250,7 +248,7 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; MOBJFLAG2_LIST[i]; i++)
if (fastcmp(p, MOBJFLAG2_LIST[i])) {
lua_pushinteger(L, ((lua_Integer)1<<i));
CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
return 1;
}
if (mathlib) return luaL_error(L, "mobjflag2 '%s' could not be found.\n", word);
@ -260,12 +258,12 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; MOBJEFLAG_LIST[i]; i++)
if (fastcmp(p, MOBJEFLAG_LIST[i])) {
lua_pushinteger(L, ((lua_Integer)1<<i));
CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
return 1;
}
if (fastcmp(p, "REVERSESUPER"))
{
lua_pushinteger(L, (lua_Integer)MFE_REVERSESUPER);
CacheAndPushConstant(L, word, (lua_Integer)MFE_REVERSESUPER);
return 1;
}
if (mathlib) return luaL_error(L, "mobjeflag '%s' could not be found.\n", word);
@ -275,7 +273,7 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; i < 4; i++)
if (MAPTHINGFLAG_LIST[i] && fastcmp(p, MAPTHINGFLAG_LIST[i])) {
lua_pushinteger(L, ((lua_Integer)1<<i));
CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
return 1;
}
if (mathlib) return luaL_error(L, "mapthingflag '%s' could not be found.\n", word);
@ -285,17 +283,17 @@ static inline int lib_getenum(lua_State *L)
p = word+3;
for (i = 0; PLAYERFLAG_LIST[i]; i++)
if (fastcmp(p, PLAYERFLAG_LIST[i])) {
lua_pushinteger(L, ((lua_Integer)1<<i));
CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
return 1;
}
if (fastcmp(p, "FULLSTASIS"))
{
lua_pushinteger(L, (lua_Integer)PF_FULLSTASIS);
CacheAndPushConstant(L, word, (lua_Integer)PF_FULLSTASIS);
return 1;
}
else if (fastcmp(p, "USEDOWN")) // Remove case when 2.3 nears release...
{
lua_pushinteger(L, (lua_Integer)PF_SPINDOWN);
CacheAndPushConstant(L, word, (lua_Integer)PF_SPINDOWN);
return 1;
}
if (mathlib) return luaL_error(L, "playerflag '%s' could not be found.\n", word);
@ -305,7 +303,7 @@ static inline int lib_getenum(lua_State *L)
p = word;
for (i = 0; Gametype_ConstantNames[i]; i++)
if (fastcmp(p, Gametype_ConstantNames[i])) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
if (mathlib) return luaL_error(L, "gametype '%s' could not be found.\n", word);
@ -315,7 +313,7 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; GAMETYPERULE_LIST[i]; i++)
if (fastcmp(p, GAMETYPERULE_LIST[i])) {
lua_pushinteger(L, ((lua_Integer)1<<i));
CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
return 1;
}
if (mathlib) return luaL_error(L, "game type rule '%s' could not be found.\n", word);
@ -325,7 +323,7 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; TYPEOFLEVEL[i].name; i++)
if (fastcmp(p, TYPEOFLEVEL[i].name)) {
lua_pushinteger(L, TYPEOFLEVEL[i].flag);
CacheAndPushConstant(L, word, TYPEOFLEVEL[i].flag);
return 1;
}
if (mathlib) return luaL_error(L, "typeoflevel '%s' could not be found.\n", word);
@ -333,9 +331,10 @@ static inline int lib_getenum(lua_State *L)
}
else if (fastncmp("ML_", word, 3)) {
p = word+3;
for (i = 0; ML_LIST[i]; i++)
if (fastcmp(p, ML_LIST[i])) {
lua_pushinteger(L, ((lua_Integer)1<<i));
CacheAndPushConstant(L, word, ((lua_Integer)1<<i));
return 1;
}
// Aliases
@ -418,13 +417,13 @@ static inline int lib_getenum(lua_State *L)
if (!FREE_STATES[i])
break;
if (fastcmp(p, FREE_STATES[i])) {
lua_pushinteger(L, S_FIRSTFREESLOT+i);
CacheAndPushConstant(L, word, S_FIRSTFREESLOT+i);
return 1;
}
}
for (i = 0; i < S_FIRSTFREESLOT; i++)
if (fastcmp(p, STATE_LIST[i]+2)) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
return luaL_error(L, "state '%s' does not exist.\n", word);
@ -435,13 +434,13 @@ static inline int lib_getenum(lua_State *L)
if (!FREE_MOBJS[i])
break;
if (fastcmp(p, FREE_MOBJS[i])) {
lua_pushinteger(L, MT_FIRSTFREESLOT+i);
CacheAndPushConstant(L, word, MT_FIRSTFREESLOT+i);
return 1;
}
}
for (i = 0; i < MT_FIRSTFREESLOT; i++)
if (fastcmp(p, MOBJTYPE_LIST[i]+3)) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
return luaL_error(L, "mobjtype '%s' does not exist.\n", word);
@ -450,7 +449,12 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; i < NUMSPRITES; i++)
if (!sprnames[i][4] && fastncmp(p,sprnames[i],4)) {
lua_pushinteger(L, i);
// updating overridden sprnames is not implemented for soc parser,
// so don't use cache
if (mathlib)
lua_pushinteger(L, i);
else
CacheAndPushConstant(L, word, i);
return 1;
}
if (mathlib) return luaL_error(L, "sprite '%s' could not be found.\n", word);
@ -465,12 +469,12 @@ static inline int lib_getenum(lua_State *L)
// the spr2names entry will have "_" on the end, as in "RUN_"
if (spr2names[i][3] == '_' && !p[3]) {
if (fastncmp(p,spr2names[i],3)) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
}
else if (fastncmp(p,spr2names[i],4)) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
}
@ -481,7 +485,7 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; i < NUMSFX; i++)
if (S_sfx[i].name && fastcmp(p, S_sfx[i].name)) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
return 0;
@ -490,7 +494,7 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; i < NUMSFX; i++)
if (S_sfx[i].name && fasticmp(p, S_sfx[i].name)) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
return luaL_error(L, "sfx '%s' could not be found.\n", word);
@ -499,7 +503,7 @@ static inline int lib_getenum(lua_State *L)
p = word+2;
for (i = 0; i < NUMSFX; i++)
if (S_sfx[i].name && fasticmp(p, S_sfx[i].name)) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
if (mathlib) return luaL_error(L, "sfx '%s' could not be found.\n", word);
@ -509,7 +513,7 @@ static inline int lib_getenum(lua_State *L)
p = word+3;
for (i = 0; i < NUMPOWERS; i++)
if (fasticmp(p, POWERS_LIST[i])) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
return 0;
@ -518,7 +522,7 @@ static inline int lib_getenum(lua_State *L)
p = word+3;
for (i = 0; i < NUMPOWERS; i++)
if (fastcmp(p, POWERS_LIST[i])) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
return luaL_error(L, "power '%s' could not be found.\n", word);
@ -527,7 +531,7 @@ static inline int lib_getenum(lua_State *L)
p = word+4;
for (i = 0; i < NUMHUDITEMS; i++)
if (fastcmp(p, HUDITEMS_LIST[i])) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
if (mathlib) return luaL_error(L, "huditem '%s' could not be found.\n", word);
@ -539,13 +543,13 @@ static inline int lib_getenum(lua_State *L)
if (!FREE_SKINCOLORS[i])
break;
if (fastcmp(p, FREE_SKINCOLORS[i])) {
lua_pushinteger(L, SKINCOLOR_FIRSTFREESLOT+i);
CacheAndPushConstant(L, word, SKINCOLOR_FIRSTFREESLOT+i);
return 1;
}
}
for (i = 0; i < SKINCOLOR_FIRSTFREESLOT; i++)
if (fastcmp(p, COLOR_ENUMS[i])) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
return luaL_error(L, "skincolor '%s' could not be found.\n", word);
@ -556,7 +560,7 @@ static inline int lib_getenum(lua_State *L)
for (i = 0; NIGHTSGRADE_LIST[i]; i++)
if (*p == NIGHTSGRADE_LIST[i])
{
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word);
@ -566,13 +570,41 @@ static inline int lib_getenum(lua_State *L)
p = word+3;
for (i = 0; i < NUMMENUTYPES; i++)
if (fastcmp(p, MENUTYPES_LIST[i])) {
lua_pushinteger(L, i);
CacheAndPushConstant(L, word, i);
return 1;
}
if (mathlib) return luaL_error(L, "menutype '%s' could not be found.\n", word);
return 0;
}
else if (!mathlib && fastncmp("A_",word,2)) {
if (fastcmp(word, "BT_USE")) // Remove case when 2.3 nears release...
{
CacheAndPushConstant(L, word, (lua_Integer)BT_SPIN);
return 1;
}
for (i = 0; INT_CONST[i].n; i++)
if (fastcmp(word,INT_CONST[i].n)) {
CacheAndPushConstant(L, word, INT_CONST[i].v);
return 1;
}
return 0;
}
static inline int lib_getenum(lua_State *L)
{
const char *word;
fixed_t i;
boolean mathlib = lua_toboolean(L, lua_upvalueindex(1));
if (lua_type(L,2) != LUA_TSTRING)
return 0;
word = lua_tostring(L,2);
// check actions, super and globals first, as they don't have _G caching implemented
// so they benefit from being checked first
if (!mathlib && fastncmp("A_",word,2)) {
char *caps;
// Try to get a Lua action first.
/// \todo Push a closure that sets superactions[] and superstack.
@ -611,25 +643,34 @@ static inline int lib_getenum(lua_State *L)
}
return 0;
}
if (fastcmp(word, "BT_USE")) // Remove case when 2.3 nears release...
{
lua_pushinteger(L, (lua_Integer)BT_SPIN);
else if ((!mathlib && LUA_PushGlobals(L, word)) || ScanConstants(L, mathlib, word))
return 1;
}
for (i = 0; INT_CONST[i].n; i++)
if (fastcmp(word,INT_CONST[i].n)) {
lua_pushinteger(L, INT_CONST[i].v);
return 1;
}
if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word);
// DYNAMIC variables too!!
// Try not to add anything that would break netgames or timeattack replays here.
// You know, like consoleplayer, displayplayer, secondarydisplayplayer, or gametime.
return LUA_PushGlobals(L, word);
return 0;
}
// If a sprname has been "cached" to _G, update it to a new value.
void LUA_UpdateSprName(const char *name, lua_Integer value)
{
char fullname[9] = "SPR_XXXX";
if (!gL)
return;
strncpy(&fullname[4], name, 4);
lua_pushstring(gL, fullname);
lua_rawget(gL, LUA_GLOBALSINDEX);
if (!lua_isnil(gL, -1))
{
lua_pushstring(gL, name);
lua_pushinteger(gL, value);
lua_rawset(gL, LUA_GLOBALSINDEX);
}
lua_pop(gL, 1); // pop the rawget result
}
int LUA_EnumLib(lua_State *L)

View file

@ -13,6 +13,21 @@
#ifndef __DEH_LUA_H__
#define __DEH_LUA_H__
#include "g_game.h"
#include "s_sound.h"
#include "z_zone.h"
#include "m_menu.h"
#include "m_misc.h"
#include "p_local.h"
#include "st_stuff.h"
#include "fastcmp.h"
#include "lua_script.h"
#include "lua_libs.h"
#include "dehacked.h"
#include "deh_tables.h"
void LUA_UpdateSprName(const char *name, lua_Integer value);
boolean LUA_SetLuaAction(void *state, const char *actiontocompare);
const char *LUA_GetActionName(void *action);
void LUA_SetActionByName(void *state, const char *actiontocompare);

View file

@ -45,6 +45,7 @@
#include "dehacked.h"
#include "deh_soc.h"
#include "deh_lua.h" // included due to some LUA_SetLuaAction hack smh
// also used for LUA_UpdateSprName
#include "deh_tables.h"
// Loops through every constant and operation in word and performs its calculations, returning the final value.
@ -439,6 +440,8 @@ void readfreeslots(MYFILE *f)
strncpy(sprnames[i],word,4);
//sprnames[i][4] = 0;
used_spr[(i-SPR_FIRSTFREESLOT)/8] |= 1<<(i%8); // Okay, this sprite slot has been named now.
// Lua needs to update the value in _G if it exists
LUA_UpdateSprName(word, i);
break;
}
}
@ -3839,6 +3842,10 @@ void readmaincfg(MYFILE *f)
{
useContinues = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
}
else if (fastcmp(word, "SHAREEMBLEMS"))
{
shareEmblems = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y');
}
else if (fastcmp(word, "GAMEDATA"))
{
@ -3849,7 +3856,7 @@ void readmaincfg(MYFILE *f)
if (!GoodDataFileName(word2))
I_Error("Maincfg: bad data file name '%s'\n", word2);
G_SaveGameData();
G_SaveGameData(clientGamedata);
strlcpy(gamedatafilename, word2, sizeof (gamedatafilename));
strlwr(gamedatafilename);
savemoddata = true;

View file

@ -575,7 +575,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
} // end while
if (gamedataadded)
G_LoadGameData();
G_LoadGameData(clientGamedata);
if (gamestate == GS_TITLESCREEN)
{

View file

@ -132,8 +132,6 @@ extern INT32 postimgparam2;
extern INT32 viewwindowx, viewwindowy;
extern INT32 viewwidth, scaledviewwidth;
extern boolean gamedataloaded;
// Player taking events, and displaying.
extern INT32 consoleplayer;
extern INT32 displayplayer;
@ -495,8 +493,6 @@ typedef struct
extern tolinfo_t TYPEOFLEVEL[NUMTOLNAMES];
extern UINT32 lastcustomtol;
extern tic_t totalplaytime;
extern boolean stagefailed;
// Emeralds stored as bits to throw savegame hackers off.
@ -515,52 +511,6 @@ extern INT32 luabanks[NUM_LUABANKS];
extern INT32 nummaprings; //keep track of spawned rings/coins
/** Time attack information, currently a very small structure.
*/
typedef struct
{
tic_t time; ///< Time in which the level was finished.
UINT32 score; ///< Score when the level was finished.
UINT16 rings; ///< Rings when the level was finished.
} recorddata_t;
/** Setup for one NiGHTS map.
* These are dynamically allocated because I am insane
*/
#define GRADE_F 0
#define GRADE_E 1
#define GRADE_D 2
#define GRADE_C 3
#define GRADE_B 4
#define GRADE_A 5
#define GRADE_S 6
typedef struct
{
// 8 mares, 1 overall (0)
UINT8 nummares;
UINT32 score[9];
UINT8 grade[9];
tic_t time[9];
} nightsdata_t;
extern nightsdata_t *nightsrecords[NUMMAPS];
extern recorddata_t *mainrecords[NUMMAPS];
// mapvisited is now a set of flags that says what we've done in the map.
#define MV_VISITED 1
#define MV_BEATEN 2
#define MV_ALLEMERALDS 4
#define MV_ULTIMATE 8
#define MV_PERFECT 16
#define MV_PERFECTRA 32
#define MV_MAX 63 // used in gamedata check, update whenever MV's are added
#define MV_MP 128
extern UINT8 mapvisited[NUMMAPS];
// Temporary holding place for nights data for the current map
extern nightsdata_t ntemprecords;
extern UINT32 token; ///< Number of tokens collected in a level
extern UINT32 tokenlist; ///< List of tokens collected
extern boolean gottoken; ///< Did you get a token? Used for end of act
@ -593,9 +543,12 @@ extern UINT8 useBlackRock;
extern UINT8 use1upSound;
extern UINT8 maxXtraLife; // Max extra lives from rings
extern UINT8 useContinues;
#define continuesInSession (!multiplayer && (ultimatemode || (useContinues && !marathonmode) || (!modeattacking && !(cursaveslot > 0))))
extern UINT8 shareEmblems;
extern mobj_t *hunt1, *hunt2, *hunt3; // Emerald hunt locations
// For racing
@ -616,10 +569,6 @@ extern INT32 cheats;
extern tic_t hidetime;
extern UINT32 timesBeaten; // # of times the game has been beaten.
extern UINT32 timesBeatenWithEmeralds;
extern UINT32 timesBeatenUltimate;
// ===========================
// Internal parameters, fixed.
// ===========================

View file

@ -63,7 +63,6 @@ static tic_t stoptimer;
static boolean keypressed = false;
// (no longer) De-Demo'd Title Screen
static INT32 menuanimtimer; // Title screen: background animation timing
mobj_t *titlemapcameraref = NULL;
// menu presentation state
@ -75,6 +74,8 @@ INT32 curbgyspeed;
boolean curbghide;
boolean hidetitlemap; // WARNING: set to false by M_SetupNextMenu and M_ClearMenus
static fixed_t curbgx = 0;
static fixed_t curbgy = 0;
static UINT8 curDemo = 0;
static UINT32 demoDelayLeft;
static UINT32 demoIdleLeft;
@ -1411,7 +1412,7 @@ boolean F_CreditResponder(event_t *event)
break;
}
if (!(timesBeaten) && !(netgame || multiplayer) && !cv_debug)
if (!(serverGamedata->timesBeaten) && !(netgame || multiplayer) && !cv_debug)
return false;
if (event->type != ev_keydown)
@ -1573,23 +1574,17 @@ void F_GameEvaluationDrawer(void)
#if 0 // the following looks like hot garbage the more unlockables we add, and we now have a lot of unlockables
if (finalecount >= 5*TICRATE)
{
INT32 startcoord = 32;
V_DrawString(8, 16, V_YELLOWMAP, "Unlocked:");
if (netgame)
V_DrawString(8, 96, V_YELLOWMAP, "Multiplayer games\ncan't unlock\nextras!");
else
for (i = 0; i < MAXUNLOCKABLES; i++)
{
INT32 startcoord = 32;
for (i = 0; i < MAXUNLOCKABLES; i++)
if (unlockables[i].conditionset && unlockables[i].conditionset < MAXCONDITIONSETS
&& unlockables[i].type && !unlockables[i].nocecho)
{
if (unlockables[i].conditionset && unlockables[i].conditionset < MAXCONDITIONSETS
&& unlockables[i].type && !unlockables[i].nocecho)
{
if (unlockables[i].unlocked)
V_DrawString(8, startcoord, 0, unlockables[i].name);
startcoord += 8;
}
if (clientGamedata->unlocked[i])
V_DrawString(8, startcoord, 0, unlockables[i].name);
startcoord += 8;
}
}
}
@ -1648,28 +1643,27 @@ void F_GameEvaluationTicker(void)
if (finalecount == 5*TICRATE)
{
if (netgame || multiplayer) // modify this when we finally allow unlocking stuff in 2P
serverGamedata->timesBeaten++;
clientGamedata->timesBeaten++;
if (ALL7EMERALDS(emeralds))
{
HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8);
HU_SetCEchoDuration(6);
HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Multiplayer games can't unlock extras!");
serverGamedata->timesBeatenWithEmeralds++;
clientGamedata->timesBeatenWithEmeralds++;
}
if (ultimatemode)
{
serverGamedata->timesBeatenUltimate++;
clientGamedata->timesBeatenUltimate++;
}
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
S_StartSound(NULL, sfx_s3k68);
}
else
{
++timesBeaten;
if (ALL7EMERALDS(emeralds))
++timesBeatenWithEmeralds;
if (ultimatemode)
++timesBeatenUltimate;
if (M_UpdateUnlockablesAndExtraEmblems())
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData();
}
G_SaveGameData(clientGamedata);
}
}
@ -2183,7 +2177,7 @@ void F_EndingDrawer(void)
//colset(linkmap, 164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret
V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), str);
V_DrawCharacter(32, BASEVIDHEIGHT-16, '>'|(trans<<V_ALPHASHIFT), false);
V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((serverGamedata->timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
}
if (finalecount > STOPPINGPOINT-(20+(2*TICRATE)))
@ -2249,7 +2243,8 @@ void F_GameEndTicker(void)
void F_InitMenuPresValues(void)
{
menuanimtimer = 0;
curbgx = 0;
curbgy = 0;
prevMenuId = 0;
activeMenuId = MainDef.menuid;
@ -2282,17 +2277,11 @@ void F_InitMenuPresValues(void)
//
// F_SkyScroll
//
void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname)
void F_SkyScroll(const char *patchname)
{
INT32 xscrolled, x, xneg = (scrollxspeed > 0) - (scrollxspeed < 0), tilex;
INT32 yscrolled, y, yneg = (scrollyspeed > 0) - (scrollyspeed < 0), tiley;
boolean xispos = (scrollxspeed >= 0), yispos = (scrollyspeed >= 0);
INT32 x, basey = 0;
INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
INT16 patwidth, patheight;
INT32 pw, ph; // scaled by dupz
patch_t *pat;
INT32 i, j;
fixed_t fracmenuanimtimer, xscrolltimer, yscrolltimer;
if (rendermode == render_none)
return;
@ -2303,43 +2292,34 @@ void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname)
return;
}
if (!scrollxspeed && !scrollyspeed)
pat = W_CachePatchName(patchname, PU_PATCH_LOWPRIORITY);
if (!curbgxspeed && !curbgyspeed)
{
V_DrawPatchFill(W_CachePatchName(patchname, PU_PATCH_LOWPRIORITY));
V_DrawPatchFill(pat);
W_UnlockCachedPatch(pat);
return;
}
pat = W_CachePatchName(patchname, PU_PATCH_LOWPRIORITY);
// Modulo the background scrolling to prevent jumps from integer overflows
// We already load the background patch here, so we can modulo it here
// to avoid also having to load the patch in F_MenuPresTicker
curbgx %= pat->width * 16;
curbgy %= pat->height * 16;
patwidth = pat->width;
patheight = pat->height;
pw = patwidth * dupz;
ph = patheight * dupz;
// Ooh, fancy frame interpolation
x = ((curbgx*dupz) + FixedInt((rendertimefrac-FRACUNIT) * curbgxspeed*dupz)) / 16;
basey = ((curbgy*dupz) + FixedInt((rendertimefrac-FRACUNIT) * curbgyspeed*dupz)) / 16;
tilex = max(FixedCeil(FixedDiv(vid.width, pw)) >> FRACBITS, 1)+2; // one tile on both sides of center
tiley = max(FixedCeil(FixedDiv(vid.height, ph)) >> FRACBITS, 1)+2;
if (x > 0) // Make sure that we don't leave the left or top sides empty
x -= pat->width * dupz;
if (basey > 0)
basey -= pat->height * dupz;
fracmenuanimtimer = (menuanimtimer * FRACUNIT) - (FRACUNIT - rendertimefrac);
xscrolltimer = ((fracmenuanimtimer*scrollxspeed)/16 + patwidth*xneg*FRACUNIT) % (patwidth * FRACUNIT);
yscrolltimer = ((fracmenuanimtimer*scrollyspeed)/16 + patheight*yneg*FRACUNIT) % (patheight * FRACUNIT);
// coordinate offsets
xscrolled = FixedInt(xscrolltimer * dupz);
yscrolled = FixedInt(yscrolltimer * dupz);
for (x = (xispos) ? -pw*(tilex-1)+pw : 0, i = 0;
i < tilex;
x += pw, i++)
for (; x < vid.width; x += pat->width * dupz)
{
for (y = (yispos) ? -ph*(tiley-1)+ph : 0, j = 0;
j < tiley;
y += ph, j++)
{
V_DrawScaledPatch(
(xispos) ? xscrolled - x : x + xscrolled,
(yispos) ? yscrolled - y : y + yscrolled,
V_NOSCALESTART, pat);
}
for (INT32 y = basey; y < vid.height; y += pat->height * dupz)
V_DrawScaledPatch(x, y, V_NOSCALESTART, pat);
}
W_UnlockCachedPatch(pat);
@ -2662,7 +2642,7 @@ void F_TitleScreenDrawer(void)
if (curbgcolor >= 0)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
else if (!curbghide || !titlemapinaction || gamestate == GS_WAITINGPLAYERS)
F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
F_SkyScroll(curbgname);
// Don't draw outside of the title screen, or if the patch isn't there.
if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS)
@ -3417,10 +3397,10 @@ luahook:
// separate animation timer for backgrounds, since we also count
// during GS_TIMEATTACK
void F_MenuPresTicker(boolean run)
void F_MenuPresTicker(void)
{
if (run)
menuanimtimer++;
curbgx += curbgxspeed;
curbgy += curbgyspeed;
}
// (no longer) De-Demo'd Title Screen

View file

@ -40,7 +40,7 @@ void F_TextPromptTicker(void);
void F_GameEndDrawer(void);
void F_IntroDrawer(void);
void F_TitleScreenDrawer(void);
void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname);
void F_SkyScroll(const char *patchname);
void F_GameEvaluationDrawer(void);
void F_StartGameEvaluation(void);
@ -131,7 +131,7 @@ extern UINT16 curtttics;
#define TITLEBACKGROUNDACTIVE (curfadevalue >= 0 || curbgname[0])
void F_InitMenuPresValues(void);
void F_MenuPresTicker(boolean run);
void F_MenuPresTicker(void);
//
// WIPE

View file

@ -185,18 +185,6 @@ INT32 tokenbits; // Used for setting token bits
// Old Special Stage
INT32 sstimer; // Time allotted in the special stage
tic_t totalplaytime;
boolean gamedataloaded = false;
// Time attack data for levels
// These are dynamically allocated for space reasons now
recorddata_t *mainrecords[NUMMAPS] = {NULL};
nightsdata_t *nightsrecords[NUMMAPS] = {NULL};
UINT8 mapvisited[NUMMAPS];
// Temporary holding place for nights data for the current map
nightsdata_t ntemprecords;
UINT32 bluescore, redscore; // CTF and Team Match team scores
// ring count... for PERFECT!
@ -227,6 +215,7 @@ UINT8 ammoremovaltics = 2*TICRATE;
UINT8 use1upSound = 0;
UINT8 maxXtraLife = 2; // Max extra lives from rings
UINT8 useContinues = 0; // Set to 1 to enable continues outside of no-save scenarioes
UINT8 shareEmblems = 0; // Set to 1 to share all picked up emblems in multiplayer
UINT8 introtoplay;
UINT8 creditscutscene;
@ -252,11 +241,6 @@ INT32 cheats; //for multiplayer cheat commands
tic_t hidetime;
// Grading
UINT32 timesBeaten;
UINT32 timesBeatenWithEmeralds;
UINT32 timesBeatenUltimate;
typedef struct joystickvector2_s
{
INT32 xaxis;
@ -452,86 +436,86 @@ INT16 rw_maximums[NUM_WEAPONS] =
};
// Allocation for time and nights data
void G_AllocMainRecordData(INT16 i)
void G_AllocMainRecordData(INT16 i, gamedata_t *data)
{
if (!mainrecords[i])
mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL);
memset(mainrecords[i], 0, sizeof(recorddata_t));
if (!data->mainrecords[i])
data->mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL);
memset(data->mainrecords[i], 0, sizeof(recorddata_t));
}
void G_AllocNightsRecordData(INT16 i)
void G_AllocNightsRecordData(INT16 i, gamedata_t *data)
{
if (!nightsrecords[i])
nightsrecords[i] = Z_Malloc(sizeof(nightsdata_t), PU_STATIC, NULL);
memset(nightsrecords[i], 0, sizeof(nightsdata_t));
if (!data->nightsrecords[i])
data->nightsrecords[i] = Z_Malloc(sizeof(nightsdata_t), PU_STATIC, NULL);
memset(data->nightsrecords[i], 0, sizeof(nightsdata_t));
}
// MAKE SURE YOU SAVE DATA BEFORE CALLING THIS
void G_ClearRecords(void)
void G_ClearRecords(gamedata_t *data)
{
INT16 i;
for (i = 0; i < NUMMAPS; ++i)
{
if (mainrecords[i])
if (data->mainrecords[i])
{
Z_Free(mainrecords[i]);
mainrecords[i] = NULL;
Z_Free(data->mainrecords[i]);
data->mainrecords[i] = NULL;
}
if (nightsrecords[i])
if (data->nightsrecords[i])
{
Z_Free(nightsrecords[i]);
nightsrecords[i] = NULL;
Z_Free(data->nightsrecords[i]);
data->nightsrecords[i] = NULL;
}
}
}
// For easy retrieval of records
UINT32 G_GetBestScore(INT16 map)
UINT32 G_GetBestScore(INT16 map, gamedata_t *data)
{
if (!mainrecords[map-1])
if (!data->mainrecords[map-1])
return 0;
return mainrecords[map-1]->score;
return data->mainrecords[map-1]->score;
}
tic_t G_GetBestTime(INT16 map)
tic_t G_GetBestTime(INT16 map, gamedata_t *data)
{
if (!mainrecords[map-1] || mainrecords[map-1]->time <= 0)
if (!data->mainrecords[map-1] || data->mainrecords[map-1]->time <= 0)
return (tic_t)UINT32_MAX;
return mainrecords[map-1]->time;
return data->mainrecords[map-1]->time;
}
UINT16 G_GetBestRings(INT16 map)
UINT16 G_GetBestRings(INT16 map, gamedata_t *data)
{
if (!mainrecords[map-1])
if (!data->mainrecords[map-1])
return 0;
return mainrecords[map-1]->rings;
return data->mainrecords[map-1]->rings;
}
UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare)
UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare, gamedata_t *data)
{
if (!nightsrecords[map-1])
if (!data->nightsrecords[map-1])
return 0;
return nightsrecords[map-1]->score[mare];
return data->nightsrecords[map-1]->score[mare];
}
tic_t G_GetBestNightsTime(INT16 map, UINT8 mare)
tic_t G_GetBestNightsTime(INT16 map, UINT8 mare, gamedata_t *data)
{
if (!nightsrecords[map-1] || nightsrecords[map-1]->time[mare] <= 0)
if (!data->nightsrecords[map-1] || data->nightsrecords[map-1]->time[mare] <= 0)
return (tic_t)UINT32_MAX;
return nightsrecords[map-1]->time[mare];
return data->nightsrecords[map-1]->time[mare];
}
UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare)
UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data)
{
if (!nightsrecords[map-1])
if (!data->nightsrecords[map-1])
return 0;
return nightsrecords[map-1]->grade[mare];
return data->nightsrecords[map-1]->grade[mare];
}
// For easy adding of NiGHTS records
@ -553,7 +537,7 @@ void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare)
// Update replay files/data, etc. for Record Attack
// See G_SetNightsRecords for NiGHTS Attack.
//
static void G_UpdateRecordReplays(void)
static void G_UpdateRecordReplays(gamedata_t *data)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
@ -561,17 +545,17 @@ static void G_UpdateRecordReplays(void)
UINT8 earnedEmblems;
// Record new best time
if (!mainrecords[gamemap-1])
G_AllocMainRecordData(gamemap-1);
if (!data->mainrecords[gamemap-1])
G_AllocMainRecordData(gamemap-1, data);
if (players[consoleplayer].score > mainrecords[gamemap-1]->score)
mainrecords[gamemap-1]->score = players[consoleplayer].score;
if (players[consoleplayer].score > data->mainrecords[gamemap-1]->score)
data->mainrecords[gamemap-1]->score = players[consoleplayer].score;
if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
if ((data->mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < data->mainrecords[gamemap-1]->time))
data->mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
if ((UINT16)(players[consoleplayer].rings) > mainrecords[gamemap-1]->rings)
mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
if ((UINT16)(players[consoleplayer].rings) > data->mainrecords[gamemap-1]->rings)
data->mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
// Save demo!
bestdemo[255] = '\0';
@ -627,14 +611,14 @@ static void G_UpdateRecordReplays(void)
free(gpath);
// Check emblems when level data is updated
if ((earnedEmblems = M_CheckLevelEmblems()))
if ((earnedEmblems = M_CheckLevelEmblems(data)))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// Update timeattack menu's replay availability.
Nextmap_OnChange();
}
void G_SetNightsRecords(void)
void G_SetNightsRecords(gamedata_t *data)
{
INT32 i;
UINT32 totalscore = 0;
@ -675,9 +659,9 @@ void G_SetNightsRecords(void)
{
nightsdata_t *maprecords;
if (!nightsrecords[gamemap-1])
G_AllocNightsRecordData(gamemap-1);
maprecords = nightsrecords[gamemap-1];
if (!data->nightsrecords[gamemap-1])
G_AllocNightsRecordData(gamemap-1, data);
maprecords = data->nightsrecords[gamemap-1];
if (maprecords->nummares != ntemprecords.nummares)
maprecords->nummares = ntemprecords.nummares;
@ -739,7 +723,7 @@ void G_SetNightsRecords(void)
}
free(gpath);
if ((earnedEmblems = M_CheckLevelEmblems()))
if ((earnedEmblems = M_CheckLevelEmblems(data)))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// If the mare count changed, this will update the score display
@ -2407,7 +2391,8 @@ void G_Ticker(boolean run)
break;
case GS_TIMEATTACK:
F_MenuPresTicker(run);
if (run)
F_MenuPresTicker();
break;
case GS_INTRO:
@ -2455,7 +2440,8 @@ void G_Ticker(boolean run)
// then intentionally fall through
/* FALLTHRU */
case GS_WAITINGPLAYERS:
F_MenuPresTicker(run);
if (run)
F_MenuPresTicker();
F_TitleScreenTicker(run);
break;
@ -3169,6 +3155,9 @@ void G_DoReborn(INT32 playernum)
if (resetlevel)
{
// Don't give completion emblems for reloading the level...
stagefailed = true;
// reload the level from scratch
if (countdowntimeup)
{
@ -3838,7 +3827,7 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
for (ix = 0; ix < NUMMAPS; ix++)
if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags
&& ix != pprevmap // Don't pick the same map.
&& (dedicated || !M_MapLocked(ix+1)) // Don't pick locked maps.
&& (dedicated || !M_MapLocked(ix+1, serverGamedata)) // Don't pick locked maps.
)
okmaps[numokmaps++] = ix;
@ -3855,40 +3844,52 @@ static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
//
// G_UpdateVisited
//
static void G_UpdateVisited(void)
static void G_UpdateVisited(gamedata_t *data, boolean silent)
{
boolean spec = G_IsSpecialStage(gamemap);
// Update visitation flags?
if (!multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode
if (!demoplayback
&& G_CoopGametype() // Campaign mode
&& !stagefailed) // Did not fail the stage
{
UINT8 earnedEmblems;
// Update visitation flags
mapvisited[gamemap-1] |= MV_BEATEN;
data->mapvisited[gamemap-1] |= MV_BEATEN;
// eh, what the hell
if (ultimatemode)
mapvisited[gamemap-1] |= MV_ULTIMATE;
data->mapvisited[gamemap-1] |= MV_ULTIMATE;
// may seem incorrect but IS possible in what the main game uses as mp special stages, and nummaprings will be -1 in NiGHTS
if (nummaprings > 0 && players[consoleplayer].rings >= nummaprings)
{
mapvisited[gamemap-1] |= MV_PERFECT;
data->mapvisited[gamemap-1] |= MV_PERFECT;
if (modeattacking)
mapvisited[gamemap-1] |= MV_PERFECTRA;
data->mapvisited[gamemap-1] |= MV_PERFECTRA;
}
if (!spec)
{
// not available to special stages because they can only really be done in one order in an unmodified game, so impossible for first six and trivial for seventh
if (ALL7EMERALDS(emeralds))
mapvisited[gamemap-1] |= MV_ALLEMERALDS;
data->mapvisited[gamemap-1] |= MV_ALLEMERALDS;
}
if (modeattacking == ATTACKING_RECORD)
G_UpdateRecordReplays();
else if (modeattacking == ATTACKING_NIGHTS)
G_SetNightsRecords();
if (silent)
{
if (modeattacking)
M_CheckLevelEmblems(data);
}
else
{
if (modeattacking == ATTACKING_RECORD)
G_UpdateRecordReplays(data);
else if (modeattacking == ATTACKING_NIGHTS)
G_SetNightsRecords(data);
}
if ((earnedEmblems = M_CompletionEmblems()))
if ((earnedEmblems = M_CompletionEmblems(data)) && !silent)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
}
}
@ -4091,7 +4092,8 @@ static void G_DoCompleted(void)
if ((skipstats && !modeattacking) || (modeattacking && stagefailed) || (intertype == int_none))
{
G_UpdateVisited();
G_UpdateVisited(serverGamedata, true);
G_UpdateVisited(clientGamedata, false);
G_HandleSaveLevel();
G_AfterIntermission();
}
@ -4100,7 +4102,8 @@ static void G_DoCompleted(void)
G_SetGamestate(GS_INTERMISSION);
Y_StartIntermission();
Y_LoadIntermissionData();
G_UpdateVisited();
G_UpdateVisited(serverGamedata, true);
G_UpdateVisited(clientGamedata, false);
G_HandleSaveLevel();
}
}
@ -4287,7 +4290,7 @@ void G_LoadGameSettings(void)
// G_LoadGameData
// Loads the main data file, which stores information such as emblems found, etc.
void G_LoadGameData(void)
void G_LoadGameData(gamedata_t *data)
{
size_t length;
INT32 i, j;
@ -4304,13 +4307,13 @@ void G_LoadGameData(void)
INT32 curmare;
// Stop saving, until we successfully load it again.
gamedataloaded = false;
data->loaded = false;
// Clear things so previously read gamedata doesn't transfer
// to new gamedata
G_ClearRecords(); // main and nights records
M_ClearSecrets(); // emblems, unlocks, maps visited, etc
totalplaytime = 0; // total play time (separate from all)
G_ClearRecords(data); // main and nights records
M_ClearSecrets(data); // emblems, unlocks, maps visited, etc
data->totalplaytime = 0; // total play time (separate from all)
if (M_CheckParm("-nodata"))
{
@ -4321,7 +4324,7 @@ void G_LoadGameData(void)
if (M_CheckParm("-resetdata"))
{
// Don't load, but do save. (essentially, reset)
gamedataloaded = true;
data->loaded = true;
return;
}
@ -4329,7 +4332,7 @@ void G_LoadGameData(void)
if (!length)
{
// No gamedata. We can save a new one.
gamedataloaded = true;
data->loaded = true;
return;
}
@ -4352,7 +4355,7 @@ void G_LoadGameData(void)
I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
}
totalplaytime = READUINT32(save_p);
data->totalplaytime = READUINT32(save_p);
#ifdef COMPAT_GAMEDATA_ID
if (versionID == COMPAT_GAMEDATA_ID)
@ -4386,7 +4389,7 @@ void G_LoadGameData(void)
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
if ((mapvisited[i] = READUINT8(save_p)) > MV_MAX)
if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
goto datacorrupt;
// To save space, use one bit per collected/achieved/unlocked flag
@ -4394,34 +4397,34 @@ void G_LoadGameData(void)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
emblemlocations[j+i].collected = ((rtemp >> j) & 1);
data->collected[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXEXTRAEMBLEMS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
extraemblems[j+i].collected = ((rtemp >> j) & 1);
data->extraCollected[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXUNLOCKABLES;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
unlockables[j+i].unlocked = ((rtemp >> j) & 1);
data->unlocked[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXCONDITIONSETS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
conditionSets[j+i].achieved = ((rtemp >> j) & 1);
data->achieved[j+i] = ((rtemp >> j) & 1);
i += j;
}
timesBeaten = READUINT32(save_p);
timesBeatenWithEmeralds = READUINT32(save_p);
timesBeatenUltimate = READUINT32(save_p);
data->timesBeaten = READUINT32(save_p);
data->timesBeatenWithEmeralds = READUINT32(save_p);
data->timesBeatenUltimate = READUINT32(save_p);
// Main records
for (i = 0; i < NUMMAPS; ++i)
@ -4436,10 +4439,10 @@ void G_LoadGameData(void)
if (recscore || rectime || recrings)
{
G_AllocMainRecordData((INT16)i);
mainrecords[i]->score = recscore;
mainrecords[i]->time = rectime;
mainrecords[i]->rings = recrings;
G_AllocMainRecordData((INT16)i, data);
data->mainrecords[i]->score = recscore;
data->mainrecords[i]->time = rectime;
data->mainrecords[i]->rings = recrings;
}
}
@ -4449,19 +4452,21 @@ void G_LoadGameData(void)
if ((recmares = READUINT8(save_p)) == 0)
continue;
G_AllocNightsRecordData((INT16)i);
G_AllocNightsRecordData((INT16)i, data);
for (curmare = 0; curmare < (recmares+1); ++curmare)
{
nightsrecords[i]->score[curmare] = READUINT32(save_p);
nightsrecords[i]->grade[curmare] = READUINT8(save_p);
nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
data->nightsrecords[i]->score[curmare] = READUINT32(save_p);
data->nightsrecords[i]->grade[curmare] = READUINT8(save_p);
data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
if (nightsrecords[i]->grade[curmare] > GRADE_S)
if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
{
goto datacorrupt;
}
}
nightsrecords[i]->nummares = recmares;
data->nightsrecords[i]->nummares = recmares;
}
// done
@ -4472,10 +4477,11 @@ void G_LoadGameData(void)
// It used to do this much earlier, but this would cause the gamedata to
// save over itself when it I_Errors from the corruption landing point below,
// which can accidentally delete players' legitimate data if the code ever has any tiny mistakes!
gamedataloaded = true;
data->loaded = true;
// Silent update unlockables in case they're out of sync with conditions
M_SilentUpdateUnlockablesAndEmblems();
M_SilentUpdateUnlockablesAndEmblems(data);
M_SilentUpdateSkinAvailabilites();
return;
@ -4495,7 +4501,7 @@ void G_LoadGameData(void)
// G_SaveGameData
// Saves the main data file, which stores information such as emblems found, etc.
void G_SaveGameData(void)
void G_SaveGameData(gamedata_t *data)
{
size_t length;
INT32 i, j;
@ -4503,7 +4509,7 @@ void G_SaveGameData(void)
INT32 curmare;
if (!gamedataloaded)
if (!data->loaded)
return; // If never loaded (-nodata), don't save
save_p = savebuffer = (UINT8 *)malloc(GAMEDATASIZE);
@ -4523,20 +4529,20 @@ void G_SaveGameData(void)
// Version test
WRITEUINT32(save_p, GAMEDATA_ID);
WRITEUINT32(save_p, totalplaytime);
WRITEUINT32(save_p, data->totalplaytime);
WRITEUINT32(save_p, quickncasehash(timeattackfolder, sizeof timeattackfolder));
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
WRITEUINT8(save_p, (mapvisited[i] & MV_MAX));
WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX));
// To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < MAXEMBLEMS;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
btemp |= (emblemlocations[j+i].collected << j);
btemp |= (data->collected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4544,7 +4550,7 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
btemp |= (extraemblems[j+i].collected << j);
btemp |= (data->extraCollected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4552,7 +4558,7 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
btemp |= (unlockables[j+i].unlocked << j);
btemp |= (data->unlocked[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
@ -4560,23 +4566,23 @@ void G_SaveGameData(void)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
btemp |= (conditionSets[j+i].achieved << j);
btemp |= (data->achieved[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
WRITEUINT32(save_p, timesBeaten);
WRITEUINT32(save_p, timesBeatenWithEmeralds);
WRITEUINT32(save_p, timesBeatenUltimate);
WRITEUINT32(save_p, data->timesBeaten);
WRITEUINT32(save_p, data->timesBeatenWithEmeralds);
WRITEUINT32(save_p, data->timesBeatenUltimate);
// Main records
for (i = 0; i < NUMMAPS; i++)
{
if (mainrecords[i])
if (data->mainrecords[i])
{
WRITEUINT32(save_p, mainrecords[i]->score);
WRITEUINT32(save_p, mainrecords[i]->time);
WRITEUINT16(save_p, mainrecords[i]->rings);
WRITEUINT32(save_p, data->mainrecords[i]->score);
WRITEUINT32(save_p, data->mainrecords[i]->time);
WRITEUINT16(save_p, data->mainrecords[i]->rings);
}
else
{
@ -4590,19 +4596,19 @@ void G_SaveGameData(void)
// NiGHTS records
for (i = 0; i < NUMMAPS; i++)
{
if (!nightsrecords[i] || !nightsrecords[i]->nummares)
if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares)
{
WRITEUINT8(save_p, 0);
continue;
}
WRITEUINT8(save_p, nightsrecords[i]->nummares);
WRITEUINT8(save_p, data->nightsrecords[i]->nummares);
for (curmare = 0; curmare < (nightsrecords[i]->nummares + 1); ++curmare)
for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare)
{
WRITEUINT32(save_p, nightsrecords[i]->score[curmare]);
WRITEUINT8(save_p, nightsrecords[i]->grade[curmare]);
WRITEUINT32(save_p, nightsrecords[i]->time[curmare]);
WRITEUINT32(save_p, data->nightsrecords[i]->score[curmare]);
WRITEUINT8(save_p, data->nightsrecords[i]->grade[curmare]);
WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]);
}
}

View file

@ -19,6 +19,7 @@
#include "d_event.h"
#include "g_demo.h"
#include "m_cheat.h" // objectplacing
#include "m_cond.h"
extern char gamedatafilename[64];
extern char timeattackfolder[64];
@ -183,7 +184,7 @@ 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_SaveGameData(void);
void G_SaveGameData(gamedata_t *data);
void G_SaveGame(UINT32 slot, INT16 mapnum);
@ -239,7 +240,7 @@ void G_SetModeAttackRetryFlag(void);
void G_ClearModeAttackRetryFlag(void);
boolean G_GetModeAttackRetryFlag(void);
void G_LoadGameData(void);
void G_LoadGameData(gamedata_t *data);
void G_LoadGameSettings(void);
void G_SetGameModified(boolean silent);
@ -248,19 +249,19 @@ void G_SetUsedCheats(boolean silent);
void G_SetGamestate(gamestate_t newstate);
// Gamedata record shit
void G_AllocMainRecordData(INT16 i);
void G_AllocNightsRecordData(INT16 i);
void G_ClearRecords(void);
void G_AllocMainRecordData(INT16 i, gamedata_t *data);
void G_AllocNightsRecordData(INT16 i, gamedata_t *data);
void G_ClearRecords(gamedata_t *data);
UINT32 G_GetBestScore(INT16 map);
tic_t G_GetBestTime(INT16 map);
UINT16 G_GetBestRings(INT16 map);
UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare);
tic_t G_GetBestNightsTime(INT16 map, UINT8 mare);
UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare);
UINT32 G_GetBestScore(INT16 map, gamedata_t *data);
tic_t G_GetBestTime(INT16 map, gamedata_t *data);
UINT16 G_GetBestRings(INT16 map, gamedata_t *data);
UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare, gamedata_t *data);
tic_t G_GetBestNightsTime(INT16 map, UINT8 mare, gamedata_t *data);
UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare, gamedata_t *data);
void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare);
void G_SetNightsRecords(void);
void G_SetNightsRecords(gamedata_t *data);
FUNCMATH INT32 G_TicsToHours(tic_t tics);
FUNCMATH INT32 G_TicsToMinutes(tic_t tics, boolean full);

View file

@ -1153,7 +1153,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
else
texturevpeg = gl_backsector->ceilingheight + textureheight[gl_toptexture] - gl_frontsector->ceilingheight;
texturevpeg += gl_sidedef->rowoffset;
texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_top;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
texturevpeg %= textureheight[gl_toptexture];
@ -1162,8 +1162,8 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_backsector->ceilingheight) * grTex->scaleY;
wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_top) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_top) * grTex->scaleX;
// Adjust t value for sloped walls
if (!(gl_linedef->flags & ML_SKEWTD))
@ -1213,7 +1213,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
else
texturevpeg = gl_frontsector->floorheight - gl_backsector->floorheight;
texturevpeg += gl_sidedef->rowoffset;
texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_bot;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
texturevpeg %= textureheight[gl_bottomtexture];
@ -1222,8 +1222,8 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_backsector->floorheight - gl_frontsector->floorheight) * grTex->scaleY;
wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_bot) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_bot) * grTex->scaleX;
// Adjust t value for sloped walls
if (!(gl_linedef->flags & ML_SKEWTD))
@ -1333,13 +1333,13 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
// Peg it to the floor
if (gl_linedef->flags & ML_MIDPEG)
{
polybottom = max(front->floorheight, back->floorheight) + gl_sidedef->rowoffset;
polybottom = max(front->floorheight, back->floorheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
polytop = polybottom + midtexheight;
}
// Peg it to the ceiling
else
{
polytop = min(front->ceilingheight, back->ceilingheight) + gl_sidedef->rowoffset;
polytop = min(front->ceilingheight, back->ceilingheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
polybottom = polytop - midtexheight;
}
@ -1350,9 +1350,9 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
// Skew the texture, but peg it to the floor
else if (gl_linedef->flags & ML_MIDPEG)
{
polybottom = popenbottom + gl_sidedef->rowoffset;
polybottom = popenbottom + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
polytop = polybottom + midtexheight;
polybottomslope = popenbottomslope + gl_sidedef->rowoffset;
polybottomslope = popenbottomslope + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
polytopslope = polybottomslope + midtexheight;
}
// Skew it according to the ceiling's slope
@ -1407,12 +1407,12 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
// Left side
wallVerts[3].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = (h - l + texturevpeg) * grTex->scaleY;
wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
// Right side
wallVerts[2].t = texturevpegslope * grTex->scaleY;
wallVerts[1].t = (hS - lS + texturevpegslope) * grTex->scaleY;
wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
// set top/bottom coords
// Take the texture peg into account, rather than changing the offsets past
@ -1474,19 +1474,19 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
// PEGGING
if ((gl_linedef->flags & (ML_DONTPEGBOTTOM|ML_NOSKEW)) == (ML_DONTPEGBOTTOM|ML_NOSKEW))
texturevpeg = gl_frontsector->floorheight + textureheight[gl_sidedef->midtexture] - gl_frontsector->ceilingheight + gl_sidedef->rowoffset;
texturevpeg = gl_frontsector->floorheight + textureheight[gl_sidedef->midtexture] - gl_frontsector->ceilingheight + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
else if (gl_linedef->flags & ML_DONTPEGBOTTOM)
texturevpeg = worldbottom + textureheight[gl_sidedef->midtexture] - worldtop + gl_sidedef->rowoffset;
texturevpeg = worldbottom + textureheight[gl_sidedef->midtexture] - worldtop + gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
else
// top of texture at top
texturevpeg = gl_sidedef->rowoffset;
texturevpeg = gl_sidedef->rowoffset + gl_sidedef->offsety_mid;
grTex = HWR_GetTexture(gl_midtexture);
wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_frontsector->floorheight) * grTex->scaleY;
wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
// Texture correction for slopes
if (gl_linedef->flags & ML_NOSKEW) {
@ -1634,13 +1634,13 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
// -- Monster Iestyn 26/06/18
if (newline)
{
texturevpeg = sides[newline->sidenum[0]].rowoffset;
texturevpeg = sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid;
attachtobottom = !!(newline->flags & ML_DONTPEGBOTTOM);
slopeskew = !!(newline->flags & ML_SKEWTD);
}
else
{
texturevpeg = sides[rover->master->sidenum[0]].rowoffset;
texturevpeg = sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid;
attachtobottom = !!(gl_linedef->flags & ML_DONTPEGBOTTOM);
slopeskew = !!(rover->master->flags & ML_SKEWTD);
}
@ -1672,8 +1672,8 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
}
}
wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
}
if (rover->fofflags & FOF_FOG)
{
@ -1785,17 +1785,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
if (newline)
{
wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset) * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset)) * grTex->scaleY;
wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid) * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset) + sides[newline->sidenum[0]].offsety_mid) * grTex->scaleY;
}
else
{
wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset) * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset)) * grTex->scaleY;
wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid) * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid)) * grTex->scaleY;
}
wallVerts[0].s = wallVerts[3].s = cliplow * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = cliphigh * grTex->scaleX;
wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX;
wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX;
}
if (rover->fofflags & FOF_FOG)
@ -3606,6 +3606,8 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
scalemul = FixedMul(FRACUNIT - floordiff/640, scale);
scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height);
if ((thing->scale != thing->old_scale) && (thing->scale >= FRACUNIT/1024)) // Interpolate shadows when scaling mobjs
scalemul = FixedMul(scalemul, FixedDiv(interp.scale, thing->scale));
fscale = FIXED_TO_FLOAT(scalemul);
fx = FIXED_TO_FLOAT(interp.x);

View file

@ -1660,7 +1660,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
#endif
// SRB2CBTODO: MD2 scaling support
finalscale *= FIXED_TO_FLOAT(spr->mobj->scale);
finalscale *= FIXED_TO_FLOAT(interp.scale);
p.flip = atransform.flip;
#ifdef USE_FTRANSFORM_MIRROR

View file

@ -216,7 +216,11 @@ HMS_connect (const char *format, ...)
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
#ifndef NO_IPV6
if (M_CheckParm("-noipv6"))
#endif
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read);

View file

@ -2862,18 +2862,6 @@ static void HU_DrawRankings(void)
V_DrawCenteredString(256, 16, 0, va("%d", cv_pointlimit.value));
}
}
else if (gametyperankings[gametype] == GT_COOP)
{
INT32 totalscore = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
totalscore += players[i].score;
}
V_DrawCenteredString(256, 8, 0, "TOTAL SCORE");
V_DrawCenteredString(256, 16, 0, va("%u", totalscore));
}
else
{
if (circuitmap)
@ -2996,7 +2984,7 @@ static void HU_DrawCoopOverlay(void)
if (LUA_HudEnabled(hud_tabemblems))
{
V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(), numemblems+numextraemblems));
V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(clientGamedata), numemblems+numextraemblems));
V_DrawScaledPatch(128, 144 - emblemicon->height/4, 0, emblemicon);
}
@ -3029,6 +3017,15 @@ static void HU_DrawNetplayCoopOverlay(void)
V_DrawSmallScaledPatch(148, 6, 0, tokenicon);
}
if (G_CoopGametype() && LUA_HudEnabled(hud_tabemblems))
{
V_DrawCenteredString(256, 14, 0, "/");
V_DrawString(256 + 4, 14, 0, va("%d", numemblems + numextraemblems));
V_DrawRightAlignedString(256 - 4, 14, 0, va("%d", M_CountEmblems(clientGamedata)));
V_DrawSmallScaledPatch(256 - (emblemicon->width / 4), 6, 0, emblemicon);
}
if (!LUA_HudEnabled(hud_coopemeralds))
return;

View file

@ -109,6 +109,17 @@ extern boolean (*I_NetCanSend)(void);
*/
extern void (*I_NetFreeNodenum)(INT32 nodenum);
/**
\brief split a string into address and port
\param address string to split
\param port double pointer to hold port component (optional)
\return address component
*/
extern char *I_NetSplitAddress(char *address, char **port);
/** \brief open a connection with specified address
\param address address to connect to

View file

@ -340,8 +340,14 @@ static inline void I_UPnP_rem(const char *port, const char * servicetype)
static const char *SOCK_AddrToStr(mysockaddr_t *sk)
{
static char s[64]; // 255.255.255.255:65535 or IPv6:65535
static char s[64]; // 255.255.255.255:65535 or
// [ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535
#ifdef HAVE_NTOP
#ifdef HAVE_IPV6
int v6 = (sk->any.sa_family == AF_INET6);
#else
int v6 = 0;
#endif
void *addr;
if(sk->any.sa_family == AF_INET)
@ -355,14 +361,21 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk)
if(addr == NULL)
sprintf(s, "No address");
else if(inet_ntop(sk->any.sa_family, addr, s, sizeof (s)) == NULL)
else if(inet_ntop(sk->any.sa_family, addr, &s[v6], sizeof (s) - v6) == NULL)
sprintf(s, "Unknown family type, error #%u", errno);
#ifdef HAVE_IPV6
else if(sk->any.sa_family == AF_INET6 && sk->ip6.sin6_port != 0)
strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
else if(sk->any.sa_family == AF_INET6)
{
s[0] = '[';
strcat(s, "]");
if (sk->ip6.sin6_port != 0)
strcat(s, va(":%d", ntohs(sk->ip6.sin6_port)));
}
#endif
else if(sk->any.sa_family == AF_INET && sk->ip4.sin_port != 0)
strcat(s, va(":%d", ntohs(sk->ip4.sin_port)));
#else
if (sk->any.sa_family == AF_INET)
{
@ -427,7 +440,7 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
&& (b->ip4.sin_port == 0 || (a->ip4.sin_port == b->ip4.sin_port));
#ifdef HAVE_IPV6
else if (b->any.sa_family == AF_INET6)
return memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
return !memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr))
&& (b->ip6.sin6_port == 0 || (a->ip6.sin6_port == b->ip6.sin6_port));
#endif
else
@ -735,8 +748,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
unsigned long trueval = true;
#endif
mysockaddr_t straddr;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
socklen_t len = sizeof(straddr);
if (s == (SOCKET_TYPE)ERRSOCKET)
return (SOCKET_TYPE)ERRSOCKET;
@ -754,14 +766,12 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
}
#endif
straddr.any = *addr;
memcpy(&straddr, addr, addrlen);
I_OutputMsg("Binding to %s\n", SOCK_AddrToStr(&straddr));
if (family == AF_INET)
{
mysockaddr_t tmpaddr;
tmpaddr.any = *addr ;
if (tmpaddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
if (straddr.ip4.sin_addr.s_addr == htonl(INADDR_ANY))
{
opt = true;
opts = (socklen_t)sizeof(opt);
@ -778,7 +788,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
#ifdef HAVE_IPV6
else if (family == AF_INET6)
{
if (memcmp(addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
if (memcmp(&straddr.ip6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) //IN6_ARE_ADDR_EQUAL
{
opt = true;
opts = (socklen_t)sizeof(opt);
@ -788,7 +798,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
// make it IPv6 ony
opt = true;
opts = (socklen_t)sizeof(opt);
if (setsockopt(s, SOL_SOCKET, IPV6_V6ONLY, (char *)&opt, opts))
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, opts))
{
CONS_Alert(CONS_WARNING, M_GetText("Could not limit IPv6 bind\n")); // I do not care anymore
}
@ -830,10 +840,17 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10);
}
if (getsockname(s, (struct sockaddr *)&sin, &len) == -1)
if (getsockname(s, &straddr.any, &len) == -1)
CONS_Alert(CONS_WARNING, M_GetText("Failed to get port number\n"));
else
current_port = (UINT16)ntohs(sin.sin_port);
{
if (family == AF_INET)
current_port = (UINT16)ntohs(straddr.ip4.sin_port);
#ifdef HAVE_IPV6
else if (family == AF_INET6)
current_port = (UINT16)ntohs(straddr.ip6.sin6_port);
#endif
}
return s;
}
@ -844,7 +861,7 @@ static boolean UDP_Socket(void)
struct my_addrinfo *ai, *runp, hints;
int gaie;
#ifdef HAVE_IPV6
const INT32 b_ipv6 = M_CheckParm("-ipv6");
const INT32 b_ipv6 = !M_CheckParm("-noipv6");
#endif
const char *serv;
@ -1156,6 +1173,7 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
SINT8 newnode = -1;
struct my_addrinfo *ai = NULL, *runp, hints;
int gaie;
size_t i;
if (!port || !port[0])
port = DEFAULTPORT;
@ -1183,13 +1201,24 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
while (runp != NULL)
{
// find ip of the server
if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0)
// test ip address of server
for (i = 0; i < mysocketses; ++i)
{
memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
break;
/* sendto tests that there is a network to this
address */
if (runp->ai_addr->sa_family == myfamily[i] &&
sendto(mysockets[i], NULL, 0, 0,
runp->ai_addr, runp->ai_addrlen) == 0)
{
memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
break;
}
}
runp = runp->ai_next;
if (i < mysocketses)
runp = runp->ai_next;
else
break;
}
I_freeaddrinfo(ai);
return newnode;

View file

@ -566,27 +566,59 @@ static luaL_Reg lib[] = {
{NULL, NULL}
};
enum cvar_e
{
cvar_name,
cvar_defaultvalue,
cvar_flags,
cvar_value,
cvar_string,
cvar_changed,
};
static const char *const cvar_opt[] = {
"name",
"defaultvalue",
"flags",
"value",
"string",
"changed",
NULL,
};
static int cvar_fields_ref = LUA_NOREF;
static int cvar_get(lua_State *L)
{
consvar_t *cvar = *(consvar_t **)luaL_checkudata(L, 1, META_CVAR);
const char *field = luaL_checkstring(L, 2);
enum cvar_e field = Lua_optoption(L, 2, -1, cvar_fields_ref);
if(fastcmp(field,"name"))
switch (field)
{
case cvar_name:
lua_pushstring(L, cvar->name);
else if(fastcmp(field,"defaultvalue"))
break;
case cvar_defaultvalue:
lua_pushstring(L, cvar->defaultvalue);
else if(fastcmp(field,"flags"))
break;
case cvar_flags:
lua_pushinteger(L, cvar->flags);
else if(fastcmp(field,"value"))
break;
case cvar_value:
lua_pushinteger(L, cvar->value);
else if(fastcmp(field,"string"))
break;
case cvar_string:
lua_pushstring(L, cvar->string);
else if(fastcmp(field,"changed"))
break;
case cvar_changed:
lua_pushboolean(L, cvar->changed);
else if (devparm)
return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS, field);
else
return 0;
break;
default:
if (devparm)
return luaL_error(L, LUA_QL("consvar_t") " has no field named " LUA_QS, field);
else
return 0;
}
return 1;
}
@ -598,6 +630,8 @@ int LUA_ConsoleLib(lua_State *L)
lua_setfield(L, -2, "__index");
lua_pop(L,1);
cvar_fields_ref = Lua_CreateFieldTable(L, cvar_opt);
// Set empty registry tables
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, "COM_Command");

View file

@ -95,6 +95,8 @@ static const char *const patch_opt[] = {
"topoffset",
NULL};
static int patch_fields_ref = LUA_NOREF;
// alignment types for v.drawString
enum align {
align_left = 0,
@ -196,6 +198,8 @@ static const char *const camera_opt[] = {
"momz",
NULL};
static int camera_fields_ref = LUA_NOREF;
static int lib_getHudInfo(lua_State *L)
{
UINT32 i;
@ -276,7 +280,7 @@ static int colormap_get(lua_State *L)
static int patch_get(lua_State *L)
{
patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH));
enum patch field = luaL_checkoption(L, 2, NULL, patch_opt);
enum patch field = Lua_optoption(L, 2, -1, patch_fields_ref);
// patches are invalidated when switching renderers
if (!patch) {
@ -316,7 +320,7 @@ static int patch_set(lua_State *L)
static int camera_get(lua_State *L)
{
camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
enum cameraf field = luaL_checkoption(L, 2, NULL, camera_opt);
enum cameraf field = Lua_optoption(L, 2, -1, camera_fields_ref);
// cameras should always be valid unless I'm a nutter
I_Assert(cam != NULL);
@ -372,7 +376,7 @@ static int camera_get(lua_State *L)
static int camera_set(lua_State *L)
{
camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA));
enum cameraf field = luaL_checkoption(L, 2, NULL, camera_opt);
enum cameraf field = Lua_optoption(L, 2, -1, camera_fields_ref);
I_Assert(cam != NULL);
@ -1444,6 +1448,8 @@ int LUA_HudLib(lua_State *L)
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
patch_fields_ref = Lua_CreateFieldTable(L, patch_opt);
luaL_newmetatable(L, META_CAMERA);
lua_pushcfunction(L, camera_get);
lua_setfield(L, -2, "__index");
@ -1452,6 +1458,8 @@ int LUA_HudLib(lua_State *L)
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
camera_fields_ref = Lua_CreateFieldTable(L, camera_opt);
luaL_register(L, "hud", lib_hud);
return 0;
}

View file

@ -1106,75 +1106,161 @@ static int lib_mobjinfolen(lua_State *L)
return 1;
}
enum mobjinfo_e
{
mobjinfo_doomednum,
mobjinfo_spawnstate,
mobjinfo_spawnhealth,
mobjinfo_seestate,
mobjinfo_seesound,
mobjinfo_reactiontime,
mobjinfo_attacksound,
mobjinfo_painstate,
mobjinfo_painchance,
mobjinfo_painsound,
mobjinfo_meleestate,
mobjinfo_missilestate,
mobjinfo_deathstate,
mobjinfo_xdeathstate,
mobjinfo_deathsound,
mobjinfo_speed,
mobjinfo_radius,
mobjinfo_height,
mobjinfo_dispoffset,
mobjinfo_mass,
mobjinfo_damage,
mobjinfo_activesound,
mobjinfo_flags,
mobjinfo_raisestate,
};
const char *const mobjinfo_opt[] = {
"doomednum",
"spawnstate",
"spawnhealth",
"seestate",
"seesound",
"reactiontime",
"attacksound",
"painstate",
"painchance",
"painsound",
"meleestate",
"missilestate",
"deathstate",
"xdeathstate",
"deathsound",
"speed",
"radius",
"height",
"dispoffset",
"mass",
"damage",
"activesound",
"flags",
"raisestate",
NULL,
};
static int mobjinfo_fields_ref = LUA_NOREF;
// mobjinfo_t *, field -> number
static int mobjinfo_get(lua_State *L)
{
mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
const char *field = luaL_checkstring(L, 2);
enum mobjinfo_e field = luaL_checkoption(L, 2, mobjinfo_opt[0], mobjinfo_opt);
I_Assert(info != NULL);
I_Assert(info >= mobjinfo);
if (fastcmp(field,"doomednum"))
switch (field)
{
case mobjinfo_doomednum:
lua_pushinteger(L, info->doomednum);
else if (fastcmp(field,"spawnstate"))
break;
case mobjinfo_spawnstate:
lua_pushinteger(L, info->spawnstate);
else if (fastcmp(field,"spawnhealth"))
break;
case mobjinfo_spawnhealth:
lua_pushinteger(L, info->spawnhealth);
else if (fastcmp(field,"seestate"))
break;
case mobjinfo_seestate:
lua_pushinteger(L, info->seestate);
else if (fastcmp(field,"seesound"))
break;
case mobjinfo_seesound:
lua_pushinteger(L, info->seesound);
else if (fastcmp(field,"reactiontime"))
break;
case mobjinfo_reactiontime:
lua_pushinteger(L, info->reactiontime);
else if (fastcmp(field,"attacksound"))
break;
case mobjinfo_attacksound:
lua_pushinteger(L, info->attacksound);
else if (fastcmp(field,"painstate"))
break;
case mobjinfo_painstate:
lua_pushinteger(L, info->painstate);
else if (fastcmp(field,"painchance"))
break;
case mobjinfo_painchance:
lua_pushinteger(L, info->painchance);
else if (fastcmp(field,"painsound"))
break;
case mobjinfo_painsound:
lua_pushinteger(L, info->painsound);
else if (fastcmp(field,"meleestate"))
break;
case mobjinfo_meleestate:
lua_pushinteger(L, info->meleestate);
else if (fastcmp(field,"missilestate"))
break;
case mobjinfo_missilestate:
lua_pushinteger(L, info->missilestate);
else if (fastcmp(field,"deathstate"))
break;
case mobjinfo_deathstate:
lua_pushinteger(L, info->deathstate);
else if (fastcmp(field,"xdeathstate"))
break;
case mobjinfo_xdeathstate:
lua_pushinteger(L, info->xdeathstate);
else if (fastcmp(field,"deathsound"))
break;
case mobjinfo_deathsound:
lua_pushinteger(L, info->deathsound);
else if (fastcmp(field,"speed"))
break;
case mobjinfo_speed:
lua_pushinteger(L, info->speed); // sometimes it's fixed_t, sometimes it's not...
else if (fastcmp(field,"radius"))
break;
case mobjinfo_radius:
lua_pushfixed(L, info->radius);
else if (fastcmp(field,"height"))
break;
case mobjinfo_height:
lua_pushfixed(L, info->height);
else if (fastcmp(field,"dispoffset"))
break;
case mobjinfo_dispoffset:
lua_pushinteger(L, info->dispoffset);
else if (fastcmp(field,"mass"))
break;
case mobjinfo_mass:
lua_pushinteger(L, info->mass);
else if (fastcmp(field,"damage"))
break;
case mobjinfo_damage:
lua_pushinteger(L, info->damage);
else if (fastcmp(field,"activesound"))
break;
case mobjinfo_activesound:
lua_pushinteger(L, info->activesound);
else if (fastcmp(field,"flags"))
break;
case mobjinfo_flags:
lua_pushinteger(L, info->flags);
else if (fastcmp(field,"raisestate"))
break;
case mobjinfo_raisestate:
lua_pushinteger(L, info->raisestate);
else {
break;
default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, info);
lua_rawget(L, -2);
if (!lua_istable(L, -1)) { // no extra values table
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", field);
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", lua_tostring(L, 2));
return 0;
}
lua_getfield(L, -1, field);
lua_pushvalue(L, 2); // field name
lua_gettable(L, -2);
if (lua_isnil(L, -1)) // no value for this field
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", field);
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "mobjinfo_t", lua_tostring(L, 2));
break;
}
return 1;
}
@ -1183,7 +1269,7 @@ static int mobjinfo_get(lua_State *L)
static int mobjinfo_set(lua_State *L)
{
mobjinfo_t *info = *((mobjinfo_t **)luaL_checkudata(L, 1, META_MOBJINFO));
const char *field = luaL_checkstring(L, 2);
enum mobjinfo_e field = Lua_optoption(L, 2, -1, mobjinfo_fields_ref);
if (hud_running)
return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
@ -1193,55 +1279,81 @@ static int mobjinfo_set(lua_State *L)
I_Assert(info != NULL);
I_Assert(info >= mobjinfo);
if (fastcmp(field,"doomednum"))
switch (field)
{
case mobjinfo_doomednum:
info->doomednum = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"spawnstate"))
break;
case mobjinfo_spawnstate:
info->spawnstate = luaL_checkinteger(L, 3);
else if (fastcmp(field,"spawnhealth"))
break;
case mobjinfo_spawnhealth:
info->spawnhealth = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"seestate"))
break;
case mobjinfo_seestate:
info->seestate = luaL_checkinteger(L, 3);
else if (fastcmp(field,"seesound"))
break;
case mobjinfo_seesound:
info->seesound = luaL_checkinteger(L, 3);
else if (fastcmp(field,"reactiontime"))
break;
case mobjinfo_reactiontime:
info->reactiontime = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"attacksound"))
break;
case mobjinfo_attacksound:
info->attacksound = luaL_checkinteger(L, 3);
else if (fastcmp(field,"painstate"))
break;
case mobjinfo_painstate:
info->painstate = luaL_checkinteger(L, 3);
else if (fastcmp(field,"painchance"))
break;
case mobjinfo_painchance:
info->painchance = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"painsound"))
break;
case mobjinfo_painsound:
info->painsound = luaL_checkinteger(L, 3);
else if (fastcmp(field,"meleestate"))
break;
case mobjinfo_meleestate:
info->meleestate = luaL_checkinteger(L, 3);
else if (fastcmp(field,"missilestate"))
break;
case mobjinfo_missilestate:
info->missilestate = luaL_checkinteger(L, 3);
else if (fastcmp(field,"deathstate"))
break;
case mobjinfo_deathstate:
info->deathstate = luaL_checkinteger(L, 3);
else if (fastcmp(field,"xdeathstate"))
break;
case mobjinfo_xdeathstate:
info->xdeathstate = luaL_checkinteger(L, 3);
else if (fastcmp(field,"deathsound"))
break;
case mobjinfo_deathsound:
info->deathsound = luaL_checkinteger(L, 3);
else if (fastcmp(field,"speed"))
break;
case mobjinfo_speed:
info->speed = luaL_checkfixed(L, 3);
else if (fastcmp(field,"radius"))
break;
case mobjinfo_radius:
info->radius = luaL_checkfixed(L, 3);
else if (fastcmp(field,"height"))
break;
case mobjinfo_height:
info->height = luaL_checkfixed(L, 3);
else if (fastcmp(field,"dispoffset"))
break;
case mobjinfo_dispoffset:
info->dispoffset = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"mass"))
break;
case mobjinfo_mass:
info->mass = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"damage"))
break;
case mobjinfo_damage:
info->damage = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"activesound"))
break;
case mobjinfo_activesound:
info->activesound = luaL_checkinteger(L, 3);
else if (fastcmp(field,"flags"))
break;
case mobjinfo_flags:
info->flags = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"raisestate"))
break;
case mobjinfo_raisestate:
info->raisestate = luaL_checkinteger(L, 3);
else {
break;
default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1));
lua_pushlightuserdata(L, info);
@ -1249,18 +1361,17 @@ static int mobjinfo_set(lua_State *L)
if (lua_isnil(L, -1)) {
// This index doesn't have a table for extra values yet, let's make one.
lua_pop(L, 1);
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobjinfo_t", field);
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; adding it as Lua data.\n"), "mobjinfo_t", lua_tostring(L, 2));
lua_newtable(L);
lua_pushlightuserdata(L, info);
lua_pushvalue(L, -2); // ext value table
lua_rawset(L, -4); // LREG_EXTVARS table
}
lua_pushvalue(L, 2); // key
lua_pushvalue(L, 3); // value to store
lua_setfield(L, -2, field);
lua_settable(L, -3);
lua_pop(L, 2);
}
//else
//return luaL_error(L, LUA_QL("mobjinfo_t") " has no field named " LUA_QS, field);
return 0;
}
@ -1788,6 +1899,8 @@ int LUA_InfoLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
mobjinfo_fields_ref = Lua_CreateFieldTable(L, mobjinfo_opt);
luaL_newmetatable(L, META_SKINCOLOR);
lua_pushcfunction(L, skincolor_get);
lua_setfield(L, -2, "__index");

View file

@ -88,6 +88,8 @@ static const char *const sector_opt[] = {
"gravity",
NULL};
static int sector_fields_ref = LUA_NOREF;
enum subsector_e {
subsector_valid = 0,
subsector_sector,
@ -104,6 +106,8 @@ static const char *const subsector_opt[] = {
"polyList",
NULL};
static int subsector_fields_ref = LUA_NOREF;
enum line_e {
line_valid = 0,
line_v1,
@ -156,10 +160,18 @@ static const char *const line_opt[] = {
"callcount",
NULL};
static int line_fields_ref = LUA_NOREF;
enum side_e {
side_valid = 0,
side_textureoffset,
side_rowoffset,
side_offsetx_top,
side_offsety_top,
side_offsetx_mid,
side_offsety_mid,
side_offsetx_bot,
side_offsety_bot,
side_toptexture,
side_bottomtexture,
side_midtexture,
@ -174,6 +186,12 @@ static const char *const side_opt[] = {
"valid",
"textureoffset",
"rowoffset",
"offsetx_top",
"offsety_top",
"offsetx_mid",
"offsety_mid",
"offsetx_bot",
"offsety_bot",
"toptexture",
"bottomtexture",
"midtexture",
@ -184,6 +202,8 @@ static const char *const side_opt[] = {
"text",
NULL};
static int side_fields_ref = LUA_NOREF;
enum vertex_e {
vertex_valid = 0,
vertex_x,
@ -204,6 +224,8 @@ static const char *const vertex_opt[] = {
"ceilingzset",
NULL};
static int vertex_fields_ref = LUA_NOREF;
enum ffloor_e {
ffloor_valid = 0,
ffloor_topheight,
@ -256,6 +278,8 @@ static const char *const ffloor_opt[] = {
"bouncestrength",
NULL};
static int ffloor_fields_ref = LUA_NOREF;
#ifdef HAVE_LUA_SEGS
enum seg_e {
seg_valid = 0,
@ -285,6 +309,8 @@ static const char *const seg_opt[] = {
"polyseg",
NULL};
static int seg_fields_ref = LUA_NOREF;
enum node_e {
node_valid = 0,
node_x,
@ -305,6 +331,8 @@ static const char *const node_opt[] = {
"children",
NULL};
static int node_fields_ref = LUA_NOREF;
enum nodechild_e {
nodechild_valid = 0,
nodechild_right,
@ -356,6 +384,8 @@ static const char *const slope_opt[] = {
"flags",
NULL};
static int slope_fields_ref = LUA_NOREF;
// shared by both vector2_t and vector3_t
enum vector_e {
vector_x = 0,
@ -575,7 +605,7 @@ static int sectorlines_num(lua_State *L)
static int sector_get(lua_State *L)
{
sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt);
enum sector_e field = Lua_optoption(L, 2, sector_valid, sector_fields_ref);
INT16 i;
if (!sector)
@ -697,7 +727,7 @@ static int sector_get(lua_State *L)
static int sector_set(lua_State *L)
{
sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
enum sector_e field = luaL_checkoption(L, 2, sector_opt[0], sector_opt);
enum sector_e field = Lua_optoption(L, 2, sector_valid, sector_fields_ref);
if (!sector)
return luaL_error(L, "accessed sector_t doesn't exist anymore.");
@ -814,7 +844,7 @@ static int sector_num(lua_State *L)
static int subsector_get(lua_State *L)
{
subsector_t *subsector = *((subsector_t **)luaL_checkudata(L, 1, META_SUBSECTOR));
enum subsector_e field = luaL_checkoption(L, 2, subsector_opt[0], subsector_opt);
enum subsector_e field = Lua_optoption(L, 2, subsector_valid, subsector_fields_ref);
if (!subsector)
{
@ -898,7 +928,7 @@ static int linestringargs_len(lua_State *L)
static int line_get(lua_State *L)
{
line_t *line = *((line_t **)luaL_checkudata(L, 1, META_LINE));
enum line_e field = luaL_checkoption(L, 2, line_opt[0], line_opt);
enum line_e field = Lua_optoption(L, 2, line_valid, line_fields_ref);
if (!line)
{
@ -1057,7 +1087,7 @@ static int sidenum_get(lua_State *L)
static int side_get(lua_State *L)
{
side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
enum side_e field = luaL_checkoption(L, 2, side_opt[0], side_opt);
enum side_e field = Lua_optoption(L, 2, side_valid, side_fields_ref);
if (!side)
{
@ -1079,6 +1109,24 @@ static int side_get(lua_State *L)
case side_rowoffset:
lua_pushfixed(L, side->rowoffset);
return 1;
case side_offsetx_top:
lua_pushfixed(L, side->offsetx_top);
return 1;
case side_offsety_top:
lua_pushfixed(L, side->offsety_top);
return 1;
case side_offsetx_mid:
lua_pushfixed(L, side->offsetx_mid);
return 1;
case side_offsety_mid:
lua_pushfixed(L, side->offsety_mid);
return 1;
case side_offsetx_bot:
lua_pushfixed(L, side->offsetx_bot);
return 1;
case side_offsety_bot:
lua_pushfixed(L, side->offsety_bot);
return 1;
case side_toptexture:
lua_pushinteger(L, side->toptexture);
return 1;
@ -1110,7 +1158,7 @@ static int side_get(lua_State *L)
static int side_set(lua_State *L)
{
side_t *side = *((side_t **)luaL_checkudata(L, 1, META_SIDE));
enum side_e field = luaL_checkoption(L, 2, side_opt[0], side_opt);
enum side_e field = Lua_optoption(L, 2, side_valid, side_fields_ref);
if (!side)
{
@ -1136,6 +1184,24 @@ static int side_set(lua_State *L)
case side_rowoffset:
side->rowoffset = luaL_checkfixed(L, 3);
break;
case side_offsetx_top:
side->offsetx_top = luaL_checkfixed(L, 3);
break;
case side_offsety_top:
side->offsety_top = luaL_checkfixed(L, 3);
break;
case side_offsetx_mid:
side->offsetx_mid = luaL_checkfixed(L, 3);
break;
case side_offsety_mid:
side->offsety_mid = luaL_checkfixed(L, 3);
break;
case side_offsetx_bot:
side->offsetx_bot = luaL_checkfixed(L, 3);
break;
case side_offsety_bot:
side->offsety_bot = luaL_checkfixed(L, 3);
break;
case side_toptexture:
side->toptexture = luaL_checkinteger(L, 3);
break;
@ -1166,7 +1232,7 @@ static int side_num(lua_State *L)
static int vertex_get(lua_State *L)
{
vertex_t *vertex = *((vertex_t **)luaL_checkudata(L, 1, META_VERTEX));
enum vertex_e field = luaL_checkoption(L, 2, vertex_opt[0], vertex_opt);
enum vertex_e field = Lua_optoption(L, 2, vertex_valid, vertex_fields_ref);
if (!vertex)
{
@ -1220,7 +1286,7 @@ static int vertex_num(lua_State *L)
static int seg_get(lua_State *L)
{
seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
enum seg_e field = luaL_checkoption(L, 2, seg_opt[0], seg_opt);
enum seg_e field = Lua_optoption(L, 2, seg_valid, seg_fields_ref);
if (!seg)
{
@ -1284,7 +1350,7 @@ static int seg_num(lua_State *L)
static int node_get(lua_State *L)
{
node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
enum node_e field = luaL_checkoption(L, 2, node_opt[0], node_opt);
enum node_e field = Lua_optoption(L, 2, node_valid, node_fields_ref);
if (!node)
{
@ -1920,7 +1986,7 @@ static INT32 P_GetOldFOFFlags(ffloor_t *fflr)
static int ffloor_get(lua_State *L)
{
ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
enum ffloor_e field = luaL_checkoption(L, 2, ffloor_opt[0], ffloor_opt);
enum ffloor_e field = Lua_optoption(L, 2, ffloor_valid, ffloor_fields_ref);
INT16 i;
if (!ffloor)
@ -2102,7 +2168,7 @@ static void P_SetOldFOFFlags(ffloor_t *fflr, oldffloortype_e oldflags)
static int ffloor_set(lua_State *L)
{
ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
enum ffloor_e field = luaL_checkoption(L, 2, ffloor_opt[0], ffloor_opt);
enum ffloor_e field = Lua_optoption(L, 2, ffloor_valid, ffloor_fields_ref);
if (!ffloor)
return luaL_error(L, "accessed ffloor_t doesn't exist anymore.");
@ -2197,7 +2263,7 @@ static int ffloor_set(lua_State *L)
static int slope_get(lua_State *L)
{
pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt);
enum slope_e field = Lua_optoption(L, 2, slope_valid, slope_fields_ref);
if (!slope)
{
@ -2241,7 +2307,7 @@ static int slope_get(lua_State *L)
static int slope_set(lua_State *L)
{
pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE));
enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt);
enum slope_e field = Lua_optoption(L, 2, slope_valid, slope_fields_ref);
if (!slope)
return luaL_error(L, "accessed pslope_t doesn't exist anymore.");
@ -2405,110 +2471,258 @@ static int lib_nummapheaders(lua_State *L)
// mapheader_t //
/////////////////
enum mapheaderinfo_e
{
mapheaderinfo_lvlttl,
mapheaderinfo_subttl,
mapheaderinfo_actnum,
mapheaderinfo_typeoflevel,
mapheaderinfo_nextlevel,
mapheaderinfo_marathonnext,
mapheaderinfo_keywords,
mapheaderinfo_musname,
mapheaderinfo_mustrack,
mapheaderinfo_muspos,
mapheaderinfo_musinterfadeout,
mapheaderinfo_musintername,
mapheaderinfo_muspostbossname,
mapheaderinfo_muspostbosstrack,
mapheaderinfo_muspostbosspos,
mapheaderinfo_muspostbossfadein,
mapheaderinfo_musforcereset,
mapheaderinfo_forcecharacter,
mapheaderinfo_weather,
mapheaderinfo_skynum,
mapheaderinfo_skybox_scalex,
mapheaderinfo_skybox_scaley,
mapheaderinfo_skybox_scalez,
mapheaderinfo_interscreen,
mapheaderinfo_runsoc,
mapheaderinfo_scriptname,
mapheaderinfo_precutscenenum,
mapheaderinfo_cutscenenum,
mapheaderinfo_countdown,
mapheaderinfo_palette,
mapheaderinfo_numlaps,
mapheaderinfo_unlockrequired,
mapheaderinfo_levelselect,
mapheaderinfo_bonustype,
mapheaderinfo_ltzzpatch,
mapheaderinfo_ltzztext,
mapheaderinfo_ltactdiamond,
mapheaderinfo_maxbonuslives,
mapheaderinfo_levelflags,
mapheaderinfo_menuflags,
mapheaderinfo_selectheading,
mapheaderinfo_startrings,
mapheaderinfo_sstimer,
mapheaderinfo_ssspheres,
mapheaderinfo_gravity,
};
static const char *const mapheaderinfo_opt[] = {
"lvlttl",
"subttl",
"actnum",
"typeoflevel",
"nextlevel",
"marathonnext",
"keywords",
"musname",
"mustrack",
"muspos",
"musinterfadeout",
"musintername",
"muspostbossname",
"muspostbosstrack",
"muspostbosspos",
"muspostbossfadein",
"musforcereset",
"forcecharacter",
"weather",
"skynum",
"skybox_scalex",
"skybox_scaley",
"skybox_scalez",
"interscreen",
"runsoc",
"scriptname",
"precutscenenum",
"cutscenenum",
"countdown",
"palette",
"numlaps",
"unlockrequired",
"levelselect",
"bonustype",
"ltzzpatch",
"ltzztext",
"ltactdiamond",
"maxbonuslives",
"levelflags",
"menuflags",
"selectheading",
"startrings",
"sstimer",
"ssspheres",
"gravity",
NULL,
};
static int mapheaderinfo_fields_ref = LUA_NOREF;
static int mapheaderinfo_get(lua_State *L)
{
mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER));
const char *field = luaL_checkstring(L, 2);
enum mapheaderinfo_e field = Lua_optoption(L, 2, -1, mapheaderinfo_fields_ref);
INT16 i;
if (fastcmp(field,"lvlttl"))
switch (field)
{
case mapheaderinfo_lvlttl:
lua_pushstring(L, header->lvlttl);
else if (fastcmp(field,"subttl"))
break;
case mapheaderinfo_subttl:
lua_pushstring(L, header->subttl);
else if (fastcmp(field,"actnum"))
break;
case mapheaderinfo_actnum:
lua_pushinteger(L, header->actnum);
else if (fastcmp(field,"typeoflevel"))
break;
case mapheaderinfo_typeoflevel:
lua_pushinteger(L, header->typeoflevel);
else if (fastcmp(field,"nextlevel"))
break;
case mapheaderinfo_nextlevel:
lua_pushinteger(L, header->nextlevel);
else if (fastcmp(field,"marathonnext"))
break;
case mapheaderinfo_marathonnext:
lua_pushinteger(L, header->marathonnext);
else if (fastcmp(field,"keywords"))
break;
case mapheaderinfo_keywords:
lua_pushstring(L, header->keywords);
else if (fastcmp(field,"musname"))
break;
case mapheaderinfo_musname:
lua_pushstring(L, header->musname);
else if (fastcmp(field,"mustrack"))
break;
case mapheaderinfo_mustrack:
lua_pushinteger(L, header->mustrack);
else if (fastcmp(field,"muspos"))
break;
case mapheaderinfo_muspos:
lua_pushinteger(L, header->muspos);
else if (fastcmp(field,"musinterfadeout"))
break;
case mapheaderinfo_musinterfadeout:
lua_pushinteger(L, header->musinterfadeout);
else if (fastcmp(field,"musintername"))
break;
case mapheaderinfo_musintername:
lua_pushstring(L, header->musintername);
else if (fastcmp(field,"muspostbossname"))
break;
case mapheaderinfo_muspostbossname:
lua_pushstring(L, header->muspostbossname);
else if (fastcmp(field,"muspostbosstrack"))
break;
case mapheaderinfo_muspostbosstrack:
lua_pushinteger(L, header->muspostbosstrack);
else if (fastcmp(field,"muspostbosspos"))
break;
case mapheaderinfo_muspostbosspos:
lua_pushinteger(L, header->muspostbosspos);
else if (fastcmp(field,"muspostbossfadein"))
break;
case mapheaderinfo_muspostbossfadein:
lua_pushinteger(L, header->muspostbossfadein);
else if (fastcmp(field,"musforcereset"))
break;
case mapheaderinfo_musforcereset:
lua_pushinteger(L, header->musforcereset);
else if (fastcmp(field,"forcecharacter"))
break;
case mapheaderinfo_forcecharacter:
lua_pushstring(L, header->forcecharacter);
else if (fastcmp(field,"weather"))
break;
case mapheaderinfo_weather:
lua_pushinteger(L, header->weather);
else if (fastcmp(field,"skynum"))
break;
case mapheaderinfo_skynum:
lua_pushinteger(L, header->skynum);
else if (fastcmp(field,"skybox_scalex"))
break;
case mapheaderinfo_skybox_scalex:
lua_pushinteger(L, header->skybox_scalex);
else if (fastcmp(field,"skybox_scaley"))
break;
case mapheaderinfo_skybox_scaley:
lua_pushinteger(L, header->skybox_scaley);
else if (fastcmp(field,"skybox_scalez"))
break;
case mapheaderinfo_skybox_scalez:
lua_pushinteger(L, header->skybox_scalez);
else if (fastcmp(field,"interscreen")) {
break;
case mapheaderinfo_interscreen:
for (i = 0; i < 8; i++)
if (!header->interscreen[i])
break;
lua_pushlstring(L, header->interscreen, i);
} else if (fastcmp(field,"runsoc"))
break;
case mapheaderinfo_runsoc:
lua_pushstring(L, header->runsoc);
else if (fastcmp(field,"scriptname"))
break;
case mapheaderinfo_scriptname:
lua_pushstring(L, header->scriptname);
else if (fastcmp(field,"precutscenenum"))
break;
case mapheaderinfo_precutscenenum:
lua_pushinteger(L, header->precutscenenum);
else if (fastcmp(field,"cutscenenum"))
break;
case mapheaderinfo_cutscenenum:
lua_pushinteger(L, header->cutscenenum);
else if (fastcmp(field,"countdown"))
break;
case mapheaderinfo_countdown:
lua_pushinteger(L, header->countdown);
else if (fastcmp(field,"palette"))
break;
case mapheaderinfo_palette:
lua_pushinteger(L, header->palette);
else if (fastcmp(field,"numlaps"))
break;
case mapheaderinfo_numlaps:
lua_pushinteger(L, header->numlaps);
else if (fastcmp(field,"unlockrequired"))
break;
case mapheaderinfo_unlockrequired:
lua_pushinteger(L, header->unlockrequired);
else if (fastcmp(field,"levelselect"))
break;
case mapheaderinfo_levelselect:
lua_pushinteger(L, header->levelselect);
else if (fastcmp(field,"bonustype"))
break;
case mapheaderinfo_bonustype:
lua_pushinteger(L, header->bonustype);
else if (fastcmp(field,"ltzzpatch"))
break;
case mapheaderinfo_ltzzpatch:
lua_pushstring(L, header->ltzzpatch);
else if (fastcmp(field,"ltzztext"))
break;
case mapheaderinfo_ltzztext:
lua_pushstring(L, header->ltzztext);
else if (fastcmp(field,"ltactdiamond"))
break;
case mapheaderinfo_ltactdiamond:
lua_pushstring(L, header->ltactdiamond);
else if (fastcmp(field,"maxbonuslives"))
break;
case mapheaderinfo_maxbonuslives:
lua_pushinteger(L, header->maxbonuslives);
else if (fastcmp(field,"levelflags"))
break;
case mapheaderinfo_levelflags:
lua_pushinteger(L, header->levelflags);
else if (fastcmp(field,"menuflags"))
break;
case mapheaderinfo_menuflags:
lua_pushinteger(L, header->menuflags);
else if (fastcmp(field,"selectheading"))
break;
case mapheaderinfo_selectheading:
lua_pushstring(L, header->selectheading);
else if (fastcmp(field,"startrings"))
break;
case mapheaderinfo_startrings:
lua_pushinteger(L, header->startrings);
else if (fastcmp(field, "sstimer"))
break;
case mapheaderinfo_sstimer:
lua_pushinteger(L, header->sstimer);
else if (fastcmp(field, "ssspheres"))
break;
case mapheaderinfo_ssspheres:
lua_pushinteger(L, header->ssspheres);
else if (fastcmp(field, "gravity"))
break;
case mapheaderinfo_gravity:
lua_pushfixed(L, header->gravity);
break;
// TODO add support for reading numGradedMares and grades
else {
default:
// Read custom vars now
// (note: don't include the "LUA." in your lua scripts!)
UINT8 j = 0;
for (;j < header->numCustomOptions && !fastcmp(field, header->customopts[j].option); ++j);
for (;j < header->numCustomOptions && !fastcmp(lua_tostring(L, 2), header->customopts[j].option); ++j);
if(j < header->numCustomOptions)
lua_pushstring(L, header->customopts[j].value);
@ -2539,6 +2753,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
sector_fields_ref = Lua_CreateFieldTable(L, sector_opt);
luaL_newmetatable(L, META_SUBSECTOR);
lua_pushcfunction(L, subsector_get);
lua_setfield(L, -2, "__index");
@ -2547,6 +2763,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
subsector_fields_ref = Lua_CreateFieldTable(L, subsector_opt);
luaL_newmetatable(L, META_LINE);
lua_pushcfunction(L, line_get);
lua_setfield(L, -2, "__index");
@ -2555,6 +2773,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
line_fields_ref = Lua_CreateFieldTable(L, line_opt);
luaL_newmetatable(L, META_LINEARGS);
lua_pushcfunction(L, lineargs_get);
lua_setfield(L, -2, "__index");
@ -2587,6 +2807,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
side_fields_ref = Lua_CreateFieldTable(L, side_opt);
luaL_newmetatable(L, META_VERTEX);
lua_pushcfunction(L, vertex_get);
lua_setfield(L, -2, "__index");
@ -2595,6 +2817,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
vertex_fields_ref = Lua_CreateFieldTable(L, vertex_opt);
luaL_newmetatable(L, META_FFLOOR);
lua_pushcfunction(L, ffloor_get);
lua_setfield(L, -2, "__index");
@ -2603,6 +2827,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
ffloor_fields_ref = Lua_CreateFieldTable(L, ffloor_opt);
#ifdef HAVE_LUA_SEGS
luaL_newmetatable(L, META_SEG);
lua_pushcfunction(L, seg_get);
@ -2612,6 +2838,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
seg_fields_ref = Lua_CreateFieldTable(L, seg_opt);
luaL_newmetatable(L, META_NODE);
lua_pushcfunction(L, node_get);
lua_setfield(L, -2, "__index");
@ -2620,6 +2848,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
node_fields_ref = Lua_CreateFieldTable(L, node_opt);
luaL_newmetatable(L, META_NODEBBOX);
//lua_pushcfunction(L, nodebbox_get);
//lua_setfield(L, -2, "__index");
@ -2646,6 +2876,8 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
slope_fields_ref = Lua_CreateFieldTable(L, slope_opt);
luaL_newmetatable(L, META_VECTOR2);
lua_pushcfunction(L, vector2_get);
lua_setfield(L, -2, "__index");
@ -2664,6 +2896,8 @@ int LUA_MapLib(lua_State *L)
//lua_setfield(L, -2, "__len");
lua_pop(L, 1);
mapheaderinfo_fields_ref = Lua_CreateFieldTable(L, mapheaderinfo_opt);
LUA_PushTaggableObjectArray(L, "sectors",
lib_iterateSectors,
lib_getSector,

View file

@ -179,10 +179,12 @@ static const char *const mobj_opt[] = {
#define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field])
static int mobj_fields_ref = LUA_NOREF;
static int mobj_get(lua_State *L)
{
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
enum mobj_e field = Lua_optoption(L, 2, NULL, mobj_opt);
enum mobj_e field = Lua_optoption(L, 2, -1, mobj_fields_ref);
lua_settop(L, 2);
if (!mo || !ISINLEVEL) {
@ -467,7 +469,7 @@ static int mobj_get(lua_State *L)
static int mobj_set(lua_State *L)
{
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
enum mobj_e field = Lua_optoption(L, 2, mobj_opt[0], mobj_opt);
enum mobj_e field = Lua_optoption(L, 2, mobj_valid, mobj_fields_ref);
lua_settop(L, 3);
INLEVEL
@ -876,14 +878,55 @@ static int thingstringargs_len(lua_State *L)
return 1;
}
enum mapthing_e {
mapthing_valid = 0,
mapthing_x,
mapthing_y,
mapthing_angle,
mapthing_pitch,
mapthing_roll,
mapthing_type,
mapthing_options,
mapthing_scale,
mapthing_z,
mapthing_extrainfo,
mapthing_tag,
mapthing_taglist,
mapthing_args,
mapthing_stringargs,
mapthing_mobj,
};
const char *const mapthing_opt[] = {
"valid",
"x",
"y",
"angle",
"pitch",
"roll",
"type",
"options",
"scale",
"z",
"extrainfo",
"tag",
"taglist",
"args",
"stringargs",
"mobj",
NULL,
};
static int mapthing_fields_ref = LUA_NOREF;
static int mapthing_get(lua_State *L)
{
mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
const char *field = luaL_checkstring(L, 2);
lua_Integer number;
enum mapthing_e field = Lua_optoption(L, 2, -1, mapthing_fields_ref);
lua_settop(L, 2);
if (!mt) {
if (fastcmp(field,"valid")) {
if (field == mapthing_valid) {
lua_pushboolean(L, false);
return 1;
}
@ -892,62 +935,71 @@ static int mapthing_get(lua_State *L)
return 0;
}
if (fastcmp(field,"valid")) {
lua_pushboolean(L, true);
return 1;
} else if(fastcmp(field,"x"))
number = mt->x;
else if(fastcmp(field,"y"))
number = mt->y;
else if(fastcmp(field,"angle"))
number = mt->angle;
else if(fastcmp(field,"pitch"))
number = mt->pitch;
else if(fastcmp(field,"roll"))
number = mt->roll;
else if(fastcmp(field,"type"))
number = mt->type;
else if(fastcmp(field,"options"))
number = mt->options;
else if(fastcmp(field,"scale"))
number = mt->scale;
else if(fastcmp(field,"z"))
number = mt->z;
else if(fastcmp(field,"extrainfo"))
number = mt->extrainfo;
else if(fastcmp(field,"tag"))
number = Tag_FGet(&mt->tags);
else if(fastcmp(field,"taglist"))
switch (field)
{
LUA_PushUserdata(L, &mt->tags, META_TAGLIST);
return 1;
case mapthing_valid:
lua_pushboolean(L, true);
break;
case mapthing_x:
lua_pushinteger(L, mt->x);
break;
case mapthing_y:
lua_pushinteger(L, mt->y);
break;
case mapthing_angle:
lua_pushinteger(L, mt->angle);
break;
case mapthing_pitch:
lua_pushinteger(L, mt->pitch);
break;
case mapthing_roll:
lua_pushinteger(L, mt->roll);
break;
case mapthing_type:
lua_pushinteger(L, mt->type);
break;
case mapthing_options:
lua_pushinteger(L, mt->options);
break;
case mapthing_scale:
lua_pushinteger(L, mt->scale);
break;
case mapthing_z:
lua_pushinteger(L, mt->z);
break;
case mapthing_extrainfo:
lua_pushinteger(L, mt->extrainfo);
break;
case mapthing_tag:
lua_pushinteger(L, Tag_FGet(&mt->tags));
break;
case mapthing_taglist:
LUA_PushUserdata(L, &mt->tags, META_TAGLIST);
break;
case mapthing_args:
LUA_PushUserdata(L, mt->args, META_THINGARGS);
break;
case mapthing_stringargs:
LUA_PushUserdata(L, mt->stringargs, META_THINGSTRINGARGS);
break;
case mapthing_mobj:
LUA_PushUserdata(L, mt->mobj, META_MOBJ);
break;
default:
if (devparm)
return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
else
return 0;
}
else if(fastcmp(field,"args"))
{
LUA_PushUserdata(L, mt->args, META_THINGARGS);
return 1;
}
else if(fastcmp(field,"stringargs"))
{
LUA_PushUserdata(L, mt->stringargs, META_THINGSTRINGARGS);
return 1;
}
else if(fastcmp(field,"mobj")) {
LUA_PushUserdata(L, mt->mobj, META_MOBJ);
return 1;
} else if (devparm)
return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
else
return 0;
lua_pushinteger(L, number);
return 1;
}
static int mapthing_set(lua_State *L)
{
mapthing_t *mt = *((mapthing_t **)luaL_checkudata(L, 1, META_MAPTHING));
const char *field = luaL_checkstring(L, 2);
enum mapthing_e field = Lua_optoption(L, 2, -1, mapthing_fields_ref);
lua_settop(L, 3);
if (!mt)
return luaL_error(L, "accessed mapthing_t doesn't exist anymore.");
@ -957,39 +1009,52 @@ static int mapthing_set(lua_State *L)
if (hook_cmd_running)
return luaL_error(L, "Do not alter mapthing_t in CMD building code!");
if(fastcmp(field,"x"))
mt->x = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"y"))
mt->y = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"angle"))
mt->angle = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"pitch"))
mt->pitch = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"roll"))
mt->roll = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"type"))
mt->type = (UINT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"options"))
mt->options = (UINT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"scale"))
mt->scale = luaL_checkfixed(L, 3);
else if(fastcmp(field,"z"))
mt->z = (INT16)luaL_checkinteger(L, 3);
else if(fastcmp(field,"extrainfo"))
switch (field)
{
INT32 extrainfo = luaL_checkinteger(L, 3);
if (extrainfo & ~15)
return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15);
mt->extrainfo = (UINT8)extrainfo;
case mapthing_x:
mt->x = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_y:
mt->y = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_angle:
mt->angle = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_pitch:
mt->pitch = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_roll:
mt->roll = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_type:
mt->type = (UINT16)luaL_checkinteger(L, 3);
break;
case mapthing_options:
mt->options = (UINT16)luaL_checkinteger(L, 3);
break;
case mapthing_scale:
mt->scale = luaL_checkfixed(L, 3);
break;
case mapthing_z:
mt->z = (INT16)luaL_checkinteger(L, 3);
break;
case mapthing_extrainfo:
INT32 extrainfo = luaL_checkinteger(L, 3);
if (extrainfo & ~15)
return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15);
mt->extrainfo = (UINT8)extrainfo;
break;
case mapthing_tag:
Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3));
break;
case mapthing_taglist:
return LUA_ErrSetDirectly(L, "mapthing_t", "taglist");
case mapthing_mobj:
mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
break;
default:
return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
}
else if (fastcmp(field,"tag"))
Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3));
else if (fastcmp(field,"taglist"))
return LUA_ErrSetDirectly(L, "mapthing_t", "taglist");
else if(fastcmp(field,"mobj"))
mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
else
return luaL_error(L, LUA_QL("mapthing_t") " has no field named " LUA_QS, field);
return 0;
}
@ -1051,6 +1116,8 @@ int LUA_MobjLib(lua_State *L)
lua_setfield(L, -2, "__newindex");
lua_pop(L,1);
mobj_fields_ref = Lua_CreateFieldTable(L, mobj_opt);
luaL_newmetatable(L, META_THINGARGS);
lua_pushcfunction(L, thingargs_get);
lua_setfield(L, -2, "__index");
@ -1078,6 +1145,8 @@ int LUA_MobjLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L,1);
mapthing_fields_ref = Lua_CreateFieldTable(L, mapthing_opt);
LUA_PushTaggableObjectArray(L, "mapthings",
lib_iterateMapthings,
lib_getMapthing,

File diff suppressed because it is too large Load diff

View file

@ -306,6 +306,18 @@ int LUA_PushGlobals(lua_State *L, const char *word)
lua_pushinteger(L, ammoremovaltics);
return 1;
// end timers
} else if (fastcmp(word,"use1upSound")) {
lua_pushinteger(L, use1upSound);
return 1;
} else if (fastcmp(word,"maxXtraLife")) {
lua_pushinteger(L, maxXtraLife);
return 1;
} else if (fastcmp(word,"useContinues")) {
lua_pushinteger(L, useContinues);
return 1;
} else if (fastcmp(word,"shareEmblems")) {
lua_pushinteger(L, shareEmblems);
return 1;
} else if (fastcmp(word,"gametype")) {
lua_pushinteger(L, gametype);
return 1;
@ -698,20 +710,23 @@ void LUA_DumpFile(const char *filename)
fixed_t LUA_EvalMath(const char *word)
{
lua_State *L = NULL;
static lua_State *L = NULL;
char buf[1024], *b;
const char *p;
fixed_t res = 0;
// make a new state so SOC can't interefere with scripts
// allocate state
L = lua_newstate(LUA_Alloc, NULL);
lua_atpanic(L, LUA_Panic);
if (!L)
{
// make a new state so SOC can't interefere with scripts
// allocate state
L = lua_newstate(LUA_Alloc, NULL);
lua_atpanic(L, LUA_Panic);
// open only enum lib
lua_pushcfunction(L, LUA_EnumLib);
lua_pushboolean(L, true);
lua_call(L, 1, 0);
// open only enum lib
lua_pushcfunction(L, LUA_EnumLib);
lua_pushboolean(L, true);
lua_call(L, 1, 0);
}
// change ^ into ^^ for Lua.
strcpy(buf, "return ");
@ -736,8 +751,6 @@ fixed_t LUA_EvalMath(const char *word)
else
res = lua_tointeger(L, -1);
// clean up and return.
lua_close(L);
return res;
}
@ -1716,17 +1729,39 @@ void LUA_UnArchive(void)
}
// For mobj_t, player_t, etc. to take custom variables.
int Lua_optoption(lua_State *L, int narg,
const char *def, const char *const lst[])
int Lua_optoption(lua_State *L, int narg, int def, int list_ref)
{
const char *name = (def) ? luaL_optstring(L, narg, def) : luaL_checkstring(L, narg);
int i;
for (i=0; lst[i]; i++)
if (fastcmp(lst[i], name))
return i;
if (lua_isnoneornil(L, narg))
return def;
I_Assert(lua_checkstack(L, 2));
luaL_checkstring(L, narg);
lua_rawgeti(L, LUA_REGISTRYINDEX, list_ref);
I_Assert(lua_istable(L, -1));
lua_pushvalue(L, narg);
lua_rawget(L, -2);
if (lua_isnumber(L, -1))
return lua_tointeger(L, -1);
return -1;
}
int Lua_CreateFieldTable(lua_State *L, const char *const lst[])
{
int i;
lua_newtable(L);
for (i = 0; lst[i] != NULL; i++)
{
lua_pushstring(L, lst[i]);
lua_pushinteger(L, i);
lua_settable(L, -3);
}
return luaL_ref(L, LUA_REGISTRYINDEX);
}
void LUA_PushTaggableObjectArray
( lua_State *L,
const char *field,

View file

@ -57,8 +57,8 @@ int LUA_PushGlobals(lua_State *L, const char *word);
int LUA_CheckGlobals(lua_State *L, const char *word);
void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c
void LUA_CVarChanged(void *cvar); // lua_consolelib.c
int Lua_optoption(lua_State *L, int narg,
const char *def, const char *const lst[]);
int Lua_optoption(lua_State *L, int narg, int def, int list_ref);
int Lua_CreateFieldTable(lua_State *L, const char *const lst[]);
void LUA_HookNetArchive(lua_CFunction archFunc);
void LUA_PushTaggableObjectArray

View file

@ -55,6 +55,7 @@ enum skin {
skin_soundsid,
skin_sprites
};
static const char *const skin_opt[] = {
"valid",
"name",
@ -95,10 +96,12 @@ static const char *const skin_opt[] = {
#define UNIMPLEMENTED luaL_error(L, LUA_QL("skin_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", skin_opt[field])
static int skin_fields_ref = LUA_NOREF;
static int skin_get(lua_State *L)
{
skin_t *skin = *((skin_t **)luaL_checkudata(L, 1, META_SKIN));
enum skin field = luaL_checkoption(L, 2, NULL, skin_opt);
enum skin field = Lua_optoption(L, 2, -1, skin_fields_ref);
// skins are always valid, only added, never removed
I_Assert(skin != NULL);
@ -376,6 +379,8 @@ int LUA_SkinLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L,1);
skin_fields_ref = Lua_CreateFieldTable(L, skin_opt);
luaL_newmetatable(L, META_SOUNDSID);
lua_pushcfunction(L, soundsid_get);
lua_setfield(L, -2, "__index");

View file

@ -79,9 +79,9 @@ static UINT8 cheatf_warp(void)
// Temporarily unlock stuff.
G_SetUsedCheats(false);
unlockables[31].unlocked = true; // credits
unlockables[30].unlocked = true; // sound test
unlockables[28].unlocked = true; // level select
clientGamedata->unlocked[31] = true; // credits
clientGamedata->unlocked[30] = true; // sound test
clientGamedata->unlocked[28] = true; // level select
// Refresh secrets menu existing.
M_ClearMenus(true);
@ -102,7 +102,7 @@ static UINT8 cheatf_devmode(void)
// Just unlock all the things and turn on -debug and console devmode.
G_SetUsedCheats(false);
for (i = 0; i < MAXUNLOCKABLES; i++)
unlockables[i].unlocked = true;
clientGamedata->unlocked[i] = true;
devparm = true;
cv_debug |= 0x8000;
@ -238,7 +238,7 @@ boolean cht_Responder(event_t *ev)
}
// Console cheat commands rely on these a lot...
#define REQUIRE_PANDORA if (!M_SecretUnlocked(SECRET_PANDORA) && !cv_debug)\
#define REQUIRE_PANDORA if (!M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !cv_debug)\
{ CONS_Printf(M_GetText("You haven't earned this yet.\n")); return; }
#define REQUIRE_DEVMODE if (!cv_debug)\

View file

@ -21,6 +21,9 @@
#include "r_skins.h" // numskins
#include "r_draw.h" // R_GetColorByName
gamedata_t *clientGamedata; // Our gamedata
gamedata_t *serverGamedata; // Server's gamedata
// Map triggers for linedef executors
// 32 triggers, one bit each
UINT32 unlocktriggers;
@ -41,6 +44,70 @@ unlockable_t unlockables[MAXUNLOCKABLES];
INT32 numemblems = 0;
INT32 numextraemblems = 0;
// Temporary holding place for nights data for the current map
nightsdata_t ntemprecords;
// Create a new gamedata_t, for start-up
gamedata_t *M_NewGameDataStruct(void)
{
gamedata_t *data = Z_Calloc(sizeof (*data), PU_STATIC, NULL);
M_ClearSecrets(data);
G_ClearRecords(data);
return data;
}
void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
{
INT32 i, j;
M_ClearSecrets(dest);
G_ClearRecords(dest);
dest->loaded = src->loaded;
dest->totalplaytime = src->totalplaytime;
dest->timesBeaten = src->timesBeaten;
dest->timesBeatenWithEmeralds = src->timesBeatenWithEmeralds;
dest->timesBeatenUltimate = src->timesBeatenUltimate;
memcpy(dest->achieved, src->achieved, sizeof(dest->achieved));
memcpy(dest->collected, src->collected, sizeof(dest->collected));
memcpy(dest->extraCollected, src->extraCollected, sizeof(dest->extraCollected));
memcpy(dest->unlocked, src->unlocked, sizeof(dest->unlocked));
memcpy(dest->mapvisited, src->mapvisited, sizeof(dest->mapvisited));
// Main records
for (i = 0; i < NUMMAPS; ++i)
{
if (!src->mainrecords[i])
continue;
G_AllocMainRecordData((INT16)i, dest);
dest->mainrecords[i]->score = src->mainrecords[i]->score;
dest->mainrecords[i]->time = src->mainrecords[i]->time;
dest->mainrecords[i]->rings = src->mainrecords[i]->rings;
}
// Nights records
for (i = 0; i < NUMMAPS; ++i)
{
if (!src->nightsrecords[i] || !src->nightsrecords[i]->nummares)
continue;
G_AllocNightsRecordData((INT16)i, dest);
for (j = 0; j < (src->nightsrecords[i]->nummares + 1); j++)
{
dest->nightsrecords[i]->score[j] = src->nightsrecords[i]->score[j];
dest->nightsrecords[i]->grade[j] = src->nightsrecords[i]->grade[j];
dest->nightsrecords[i]->time[j] = src->nightsrecords[i]->time[j];
}
dest->nightsrecords[i]->nummares = src->nightsrecords[i]->nummares;
}
}
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2)
{
condition_t *cond;
@ -70,89 +137,90 @@ void M_ClearConditionSet(UINT8 set)
conditionSets[set - 1].condition = NULL;
conditionSets[set - 1].numconditions = 0;
}
conditionSets[set - 1].achieved = false;
clientGamedata->achieved[set - 1] = serverGamedata->achieved[set - 1] = false;
}
// Clear ALL secrets.
void M_ClearSecrets(void)
void M_ClearSecrets(gamedata_t *data)
{
INT32 i;
memset(mapvisited, 0, sizeof(mapvisited));
memset(data->mapvisited, 0, sizeof(data->mapvisited));
for (i = 0; i < MAXEMBLEMS; ++i)
emblemlocations[i].collected = false;
data->collected[i] = false;
for (i = 0; i < MAXEXTRAEMBLEMS; ++i)
extraemblems[i].collected = false;
data->extraCollected[i] = false;
for (i = 0; i < MAXUNLOCKABLES; ++i)
unlockables[i].unlocked = false;
data->unlocked[i] = false;
for (i = 0; i < MAXCONDITIONSETS; ++i)
conditionSets[i].achieved = false;
data->achieved[i] = false;
timesBeaten = timesBeatenWithEmeralds = timesBeatenUltimate = 0;
data->timesBeaten = data->timesBeatenWithEmeralds = data->timesBeatenUltimate = 0;
// Re-unlock any always unlocked things
M_SilentUpdateUnlockablesAndEmblems();
M_SilentUpdateUnlockablesAndEmblems(data);
M_SilentUpdateSkinAvailabilites();
}
// ----------------------
// Condition set checking
// ----------------------
static UINT8 M_CheckCondition(condition_t *cn)
static UINT8 M_CheckCondition(condition_t *cn, gamedata_t *data)
{
switch (cn->type)
{
case UC_PLAYTIME: // Requires total playing time >= x
return (totalplaytime >= (unsigned)cn->requirement);
return (data->totalplaytime >= (unsigned)cn->requirement);
case UC_GAMECLEAR: // Requires game beaten >= x times
return (timesBeaten >= (unsigned)cn->requirement);
return (data->timesBeaten >= (unsigned)cn->requirement);
case UC_ALLEMERALDS: // Requires game beaten with all 7 emeralds >= x times
return (timesBeatenWithEmeralds >= (unsigned)cn->requirement);
return (data->timesBeatenWithEmeralds >= (unsigned)cn->requirement);
case UC_ULTIMATECLEAR: // Requires game beaten on ultimate >= x times (in other words, never)
return (timesBeatenUltimate >= (unsigned)cn->requirement);
return (data->timesBeatenUltimate >= (unsigned)cn->requirement);
case UC_OVERALLSCORE: // Requires overall score >= x
return (M_GotHighEnoughScore(cn->requirement));
return (M_GotHighEnoughScore(cn->requirement, data));
case UC_OVERALLTIME: // Requires overall time <= x
return (M_GotLowEnoughTime(cn->requirement));
return (M_GotLowEnoughTime(cn->requirement, data));
case UC_OVERALLRINGS: // Requires overall rings >= x
return (M_GotHighEnoughRings(cn->requirement));
return (M_GotHighEnoughRings(cn->requirement, data));
case UC_MAPVISITED: // Requires map x to be visited
return ((mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED);
return ((data->mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED);
case UC_MAPBEATEN: // Requires map x to be beaten
return ((mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN);
return ((data->mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN);
case UC_MAPALLEMERALDS: // Requires map x to be beaten with all emeralds in possession
return ((mapvisited[cn->requirement - 1] & MV_ALLEMERALDS) == MV_ALLEMERALDS);
return ((data->mapvisited[cn->requirement - 1] & MV_ALLEMERALDS) == MV_ALLEMERALDS);
case UC_MAPULTIMATE: // Requires map x to be beaten on ultimate
return ((mapvisited[cn->requirement - 1] & MV_ULTIMATE) == MV_ULTIMATE);
return ((data->mapvisited[cn->requirement - 1] & MV_ULTIMATE) == MV_ULTIMATE);
case UC_MAPPERFECT: // Requires map x to be beaten with a perfect bonus
return ((mapvisited[cn->requirement - 1] & MV_PERFECT) == MV_PERFECT);
return ((data->mapvisited[cn->requirement - 1] & MV_PERFECT) == MV_PERFECT);
case UC_MAPSCORE: // Requires score on map >= x
return (G_GetBestScore(cn->extrainfo1) >= (unsigned)cn->requirement);
return (G_GetBestScore(cn->extrainfo1, data) >= (unsigned)cn->requirement);
case UC_MAPTIME: // Requires time on map <= x
return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement);
return (G_GetBestTime(cn->extrainfo1, data) <= (unsigned)cn->requirement);
case UC_MAPRINGS: // Requires rings on map >= x
return (G_GetBestRings(cn->extrainfo1) >= cn->requirement);
return (G_GetBestRings(cn->extrainfo1, data) >= cn->requirement);
case UC_NIGHTSSCORE:
return (G_GetBestNightsScore(cn->extrainfo1, (UINT8)cn->extrainfo2) >= (unsigned)cn->requirement);
return (G_GetBestNightsScore(cn->extrainfo1, (UINT8)cn->extrainfo2, data) >= (unsigned)cn->requirement);
case UC_NIGHTSTIME:
return (G_GetBestNightsTime(cn->extrainfo1, (UINT8)cn->extrainfo2) <= (unsigned)cn->requirement);
return (G_GetBestNightsTime(cn->extrainfo1, (UINT8)cn->extrainfo2, data) <= (unsigned)cn->requirement);
case UC_NIGHTSGRADE:
return (G_GetBestNightsGrade(cn->extrainfo1, (UINT8)cn->extrainfo2) >= cn->requirement);
return (G_GetBestNightsGrade(cn->extrainfo1, (UINT8)cn->extrainfo2, data) >= cn->requirement);
case UC_TRIGGER: // requires map trigger set
return !!(unlocktriggers & (1 << cn->requirement));
case UC_TOTALEMBLEMS: // Requires number of emblems >= x
return (M_GotEnoughEmblems(cn->requirement));
return (M_GotEnoughEmblems(cn->requirement, data));
case UC_EMBLEM: // Requires emblem x to be obtained
return emblemlocations[cn->requirement-1].collected;
return data->collected[cn->requirement-1];
case UC_EXTRAEMBLEM: // Requires extra emblem x to be obtained
return extraemblems[cn->requirement-1].collected;
return data->extraCollected[cn->requirement-1];
case UC_CONDITIONSET: // requires condition set x to already be achieved
return M_Achieved(cn->requirement-1);
return M_Achieved(cn->requirement-1, data);
}
return false;
}
static UINT8 M_CheckConditionSet(conditionset_t *c)
static UINT8 M_CheckConditionSet(conditionset_t *c, gamedata_t *data)
{
UINT32 i;
UINT32 lastID = 0;
@ -173,13 +241,13 @@ static UINT8 M_CheckConditionSet(conditionset_t *c)
continue;
lastID = cn->id;
achievedSoFar = M_CheckCondition(cn);
achievedSoFar = M_CheckCondition(cn, data);
}
return achievedSoFar;
}
void M_CheckUnlockConditions(void)
void M_CheckUnlockConditions(gamedata_t *data)
{
INT32 i;
conditionset_t *c;
@ -187,27 +255,27 @@ void M_CheckUnlockConditions(void)
for (i = 0; i < MAXCONDITIONSETS; ++i)
{
c = &conditionSets[i];
if (!c->numconditions || c->achieved)
if (!c->numconditions || data->achieved[i])
continue;
c->achieved = (M_CheckConditionSet(c));
data->achieved[i] = (M_CheckConditionSet(c, data));
}
}
UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
UINT8 M_UpdateUnlockablesAndExtraEmblems(gamedata_t *data)
{
INT32 i;
char cechoText[992] = "";
UINT8 cechoLines = 0;
M_CheckUnlockConditions();
M_CheckUnlockConditions(data);
// Go through extra emblems
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected || !extraemblems[i].conditionset)
if (data->extraCollected[i] || !extraemblems[i].conditionset)
continue;
if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
if ((data->extraCollected[i] = M_Achieved(extraemblems[i].conditionset - 1, data)) != false)
{
strcat(cechoText, va(M_GetText("Got \"%s\" emblem!\\"), extraemblems[i].name));
++cechoLines;
@ -217,14 +285,14 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
// Fun part: if any of those unlocked we need to go through the
// unlock conditions AGAIN just in case an emblem reward was reached
if (cechoLines)
M_CheckUnlockConditions();
M_CheckUnlockConditions(data);
// Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].unlocked || !unlockables[i].conditionset)
if (data->unlocked[i] || !unlockables[i].conditionset)
continue;
if ((unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1)) != false)
if ((data->unlocked[i] = M_Achieved(unlockables[i].conditionset - 1, data)) != false)
{
if (unlockables[i].nocecho)
continue;
@ -248,45 +316,50 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void)
HU_DoCEcho(slashed);
return true;
}
return false;
}
// Used when loading gamedata to make sure all unlocks are synched with conditions
void M_SilentUpdateUnlockablesAndEmblems(void)
void M_SilentUpdateUnlockablesAndEmblems(gamedata_t *data)
{
INT32 i;
boolean checkAgain = false;
// Just in case they aren't to sync
M_CheckUnlockConditions();
M_CheckLevelEmblems();
M_CheckUnlockConditions(data);
M_CheckLevelEmblems(data);
M_CompletionEmblems(data);
// Go through extra emblems
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected || !extraemblems[i].conditionset)
if (data->extraCollected[i] || !extraemblems[i].conditionset)
continue;
if ((extraemblems[i].collected = M_Achieved(extraemblems[i].conditionset - 1)) != false)
if ((data->extraCollected[i] = M_Achieved(extraemblems[i].conditionset - 1, data)) != false)
checkAgain = true;
}
// check again if extra emblems unlocked, blah blah, etc
if (checkAgain)
M_CheckUnlockConditions();
M_CheckUnlockConditions(data);
// Go through unlockables
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].unlocked || !unlockables[i].conditionset)
if (data->unlocked[i] || !unlockables[i].conditionset)
continue;
unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
data->unlocked[i] = M_Achieved(unlockables[i].conditionset - 1, data);
}
}
void M_SilentUpdateSkinAvailabilites(void)
{
players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
}
// Emblem unlocking shit
UINT8 M_CheckLevelEmblems(void)
UINT8 M_CheckLevelEmblems(gamedata_t *data)
{
INT32 i;
INT32 valToReach;
@ -297,7 +370,7 @@ UINT8 M_CheckLevelEmblems(void)
// Update Score, Time, Rings emblems
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || emblemlocations[i].collected)
if (emblemlocations[i].type <= ET_SKIN || emblemlocations[i].type == ET_MAP || data->collected[i])
continue;
levelnum = emblemlocations[i].level;
@ -306,32 +379,32 @@ UINT8 M_CheckLevelEmblems(void)
switch (emblemlocations[i].type)
{
case ET_SCORE: // Requires score on map >= x
res = (G_GetBestScore(levelnum) >= (unsigned)valToReach);
res = (G_GetBestScore(levelnum, data) >= (unsigned)valToReach);
break;
case ET_TIME: // Requires time on map <= x
res = (G_GetBestTime(levelnum) <= (unsigned)valToReach);
res = (G_GetBestTime(levelnum, data) <= (unsigned)valToReach);
break;
case ET_RINGS: // Requires rings on map >= x
res = (G_GetBestRings(levelnum) >= valToReach);
res = (G_GetBestRings(levelnum, data) >= valToReach);
break;
case ET_NGRADE: // Requires NiGHTS grade on map >= x
res = (G_GetBestNightsGrade(levelnum, 0) >= valToReach);
res = (G_GetBestNightsGrade(levelnum, 0, data) >= valToReach);
break;
case ET_NTIME: // Requires NiGHTS time on map <= x
res = (G_GetBestNightsTime(levelnum, 0) <= (unsigned)valToReach);
res = (G_GetBestNightsTime(levelnum, 0, data) <= (unsigned)valToReach);
break;
default: // unreachable but shuts the compiler up.
continue;
}
emblemlocations[i].collected = res;
data->collected[i] = res;
if (res)
++somethingUnlocked;
}
return somethingUnlocked;
}
UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
UINT8 M_CompletionEmblems(gamedata_t *data) // Bah! Duplication sucks, but it's for a separate print when awarding emblems and it's sorta different enough.
{
INT32 i;
INT32 embtype;
@ -342,7 +415,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].type != ET_MAP || emblemlocations[i].collected)
if (emblemlocations[i].type != ET_MAP || data->collected[i])
continue;
levelnum = emblemlocations[i].level;
@ -358,9 +431,9 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
if (embtype & ME_PERFECT)
flags |= MV_PERFECT;
res = ((mapvisited[levelnum - 1] & flags) == flags);
res = ((data->mapvisited[levelnum - 1] & flags) == flags);
emblemlocations[i].collected = res;
data->collected[i] = res;
if (res)
++somethingUnlocked;
}
@ -370,48 +443,54 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
// -------------------
// Quick unlock checks
// -------------------
UINT8 M_AnySecretUnlocked(void)
UINT8 M_AnySecretUnlocked(gamedata_t *data)
{
INT32 i;
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (!unlockables[i].nocecho && unlockables[i].unlocked)
if (!unlockables[i].nocecho && data->unlocked[i])
return true;
}
return false;
}
UINT8 M_SecretUnlocked(INT32 type)
UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data)
{
INT32 i;
for (i = 0; i < MAXUNLOCKABLES; ++i)
{
if (unlockables[i].type == type && unlockables[i].unlocked)
if (unlockables[i].type == type && data->unlocked[i])
return true;
}
return false;
}
UINT8 M_MapLocked(INT32 mapnum)
UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data)
{
if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0)
{
return false;
if (!unlockables[mapheaderinfo[mapnum-1]->unlockrequired].unlocked)
}
if (!data->unlocked[mapheaderinfo[mapnum-1]->unlockrequired])
{
return true;
}
return false;
}
INT32 M_CountEmblems(void)
INT32 M_CountEmblems(gamedata_t *data)
{
INT32 found = 0, i;
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].collected)
if (data->collected[i])
found++;
}
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected)
if (data->extraCollected[i])
found++;
}
return found;
@ -423,23 +502,23 @@ INT32 M_CountEmblems(void)
// Theoretically faster than using M_CountEmblems()
// Stops when it reaches the target number of emblems.
UINT8 M_GotEnoughEmblems(INT32 number)
UINT8 M_GotEnoughEmblems(INT32 number, gamedata_t *data)
{
INT32 i, gottenemblems = 0;
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].collected)
if (data->collected[i])
if (++gottenemblems >= number) return true;
}
for (i = 0; i < numextraemblems; ++i)
{
if (extraemblems[i].collected)
if (data->extraCollected[i])
if (++gottenemblems >= number) return true;
}
return false;
}
UINT8 M_GotHighEnoughScore(INT32 tscore)
UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data)
{
INT32 mscore = 0;
INT32 i;
@ -448,16 +527,16 @@ UINT8 M_GotHighEnoughScore(INT32 tscore)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
if (!mainrecords[i])
if (!data->mainrecords[i])
continue;
if ((mscore += mainrecords[i]->score) > tscore)
if ((mscore += data->mainrecords[i]->score) > tscore)
return true;
}
return false;
}
UINT8 M_GotLowEnoughTime(INT32 tictime)
UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data)
{
INT32 curtics = 0;
INT32 i;
@ -467,15 +546,15 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
if (!mainrecords[i] || !mainrecords[i]->time)
if (!data->mainrecords[i] || !data->mainrecords[i]->time)
return false;
else if ((curtics += mainrecords[i]->time) > tictime)
else if ((curtics += data->mainrecords[i]->time) > tictime)
return false;
}
return true;
}
UINT8 M_GotHighEnoughRings(INT32 trings)
UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data)
{
INT32 mrings = 0;
INT32 i;
@ -484,10 +563,10 @@ UINT8 M_GotHighEnoughRings(INT32 trings)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
if (!mainrecords[i])
if (!data->mainrecords[i])
continue;
if ((mrings += mainrecords[i]->rings) > trings)
if ((mrings += data->mainrecords[i]->rings) > trings)
return true;
}
return false;

View file

@ -10,7 +10,11 @@
/// \file m_cond.h
/// \brief Unlockable condition system for SRB2 version 2.1
#ifndef __M_COND__
#define __M_COND__
#include "doomdef.h"
#include "doomdata.h"
// --------
// Typedefs
@ -61,8 +65,6 @@ typedef struct
{
UINT32 numconditions; /// <- number of conditions.
condition_t *condition; /// <- All conditionals to be checked.
UINT8 achieved; /// <- Whether this conditional has been achieved already or not.
/// (Conditional checking is skipped if true -- it's assumed you can't relock an unlockable)
} conditionset_t;
// Emblem information
@ -94,7 +96,6 @@ typedef struct
INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin)
char *stringVar; ///< String version
char hint[110]; ///< Hint for emblem hints menu
UINT8 collected; ///< Do you have this emblem?
} emblem_t;
typedef struct
{
@ -104,7 +105,6 @@ typedef struct
UINT8 showconditionset; ///< Condition set that shows this emblem.
UINT8 sprite; ///< emblem sprite to use, 0 - 25
UINT16 color; ///< skincolor to use
UINT8 collected; ///< Do you have this emblem?
} extraemblem_t;
// Unlockable information
@ -120,7 +120,6 @@ typedef struct
char *stringVar;
UINT8 nocecho;
UINT8 nochecklist;
UINT8 unlocked;
} unlockable_t;
#define SECRET_NONE -6 // Does nil. Use with levels locked by UnlockRequired
@ -143,6 +142,83 @@ typedef struct
#define MAXEXTRAEMBLEMS 16
#define MAXUNLOCKABLES 32
/** Time attack information, currently a very small structure.
*/
typedef struct
{
tic_t time; ///< Time in which the level was finished.
UINT32 score; ///< Score when the level was finished.
UINT16 rings; ///< Rings when the level was finished.
} recorddata_t;
/** Setup for one NiGHTS map.
* These are dynamically allocated because I am insane
*/
#define GRADE_F 0
#define GRADE_E 1
#define GRADE_D 2
#define GRADE_C 3
#define GRADE_B 4
#define GRADE_A 5
#define GRADE_S 6
typedef struct
{
// 8 mares, 1 overall (0)
UINT8 nummares;
UINT32 score[9];
UINT8 grade[9];
tic_t time[9];
} nightsdata_t;
// mapvisited is now a set of flags that says what we've done in the map.
#define MV_VISITED 1
#define MV_BEATEN 2
#define MV_ALLEMERALDS 4
#define MV_ULTIMATE 8
#define MV_PERFECT 16
#define MV_PERFECTRA 32
#define MV_MAX 63 // used in gamedata check, update whenever MV's are added
// Temporary holding place for nights data for the current map
extern nightsdata_t ntemprecords;
// GAMEDATA STRUCTURE
// Everything that would get saved in gamedata.dat
typedef struct
{
// WHENEVER OR NOT WE'RE READY TO SAVE
boolean loaded;
// CONDITION SETS ACHIEVED
boolean achieved[MAXCONDITIONSETS];
// EMBLEMS COLLECTED
boolean collected[MAXEMBLEMS];
// EXTRA EMBLEMS COLLECTED
boolean extraCollected[MAXEXTRAEMBLEMS];
// UNLOCKABLES UNLOCKED
boolean unlocked[MAXUNLOCKABLES];
// TIME ATTACK DATA
recorddata_t *mainrecords[NUMMAPS];
nightsdata_t *nightsrecords[NUMMAPS];
UINT8 mapvisited[NUMMAPS];
// # OF TIMES THE GAME HAS BEEN BEATEN
UINT32 timesBeaten;
UINT32 timesBeatenWithEmeralds;
UINT32 timesBeatenUltimate;
// PLAY TIME
UINT32 totalplaytime;
} gamedata_t;
extern gamedata_t *clientGamedata;
extern gamedata_t *serverGamedata;
extern conditionset_t conditionSets[MAXCONDITIONSETS];
extern emblem_t emblemlocations[MAXEMBLEMS];
extern extraemblem_t extraemblems[MAXEXTRAEMBLEMS];
@ -153,25 +229,30 @@ extern INT32 numextraemblems;
extern UINT32 unlocktriggers;
gamedata_t *M_NewGameDataStruct(void);
void M_CopyGameData(gamedata_t *dest, gamedata_t *src);
// Condition set setup
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);
// Clearing secrets
void M_ClearConditionSet(UINT8 set);
void M_ClearSecrets(void);
void M_ClearSecrets(gamedata_t *data);
// Updating conditions and unlockables
void M_CheckUnlockConditions(void);
UINT8 M_UpdateUnlockablesAndExtraEmblems(void);
void M_SilentUpdateUnlockablesAndEmblems(void);
UINT8 M_CheckLevelEmblems(void);
UINT8 M_CompletionEmblems(void);
void M_CheckUnlockConditions(gamedata_t *data);
UINT8 M_UpdateUnlockablesAndExtraEmblems(gamedata_t *data);
void M_SilentUpdateUnlockablesAndEmblems(gamedata_t *data);
UINT8 M_CheckLevelEmblems(gamedata_t *data);
UINT8 M_CompletionEmblems(gamedata_t *data);
void M_SilentUpdateSkinAvailabilites(void);
// Checking unlockable status
UINT8 M_AnySecretUnlocked(void);
UINT8 M_SecretUnlocked(INT32 type);
UINT8 M_MapLocked(INT32 mapnum);
INT32 M_CountEmblems(void);
UINT8 M_AnySecretUnlocked(gamedata_t *data);
UINT8 M_SecretUnlocked(INT32 type, gamedata_t *data);
UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data);
INT32 M_CountEmblems(gamedata_t *data);
// Emblem shit
emblem_t *M_GetLevelEmblems(INT32 mapnum);
@ -183,12 +264,14 @@ const char *M_GetExtraEmblemPatch(extraemblem_t *em, boolean big);
// If you're looking to compare stats for unlocks or what not, use these
// They stop checking upon reaching the target number so they
// should be (theoretically?) slightly faster.
UINT8 M_GotEnoughEmblems(INT32 number);
UINT8 M_GotHighEnoughScore(INT32 tscore);
UINT8 M_GotLowEnoughTime(INT32 tictime);
UINT8 M_GotHighEnoughRings(INT32 trings);
UINT8 M_GotEnoughEmblems(INT32 number, gamedata_t *data);
UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data);
UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data);
UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data);
INT32 M_UnlockableSkinNum(unlockable_t *unlock);
INT32 M_EmblemSkinNum(emblem_t *emblem);
#define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved)
#define M_Achieved(a, data) ((a) >= MAXCONDITIONSETS || data->achieved[a])
#endif

View file

@ -241,6 +241,7 @@ static void M_EmblemHints(INT32 choice);
static void M_HandleEmblemHints(INT32 choice);
UINT32 hintpage = 1;
static void M_HandleChecklist(INT32 choice);
static void M_PauseLevelSelect(INT32 choice);
menu_t SR_MainDef, SR_UnlockChecklistDef;
static UINT8 check_on;
@ -554,26 +555,30 @@ static menuitem_t MPauseMenu[] =
{
{IT_STRING | IT_CALL, NULL, "Add-ons...", M_Addons, 8},
{IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16},
{IT_STRING | IT_CALL, NULL, "Switch Gametype/Level...", M_MapChange, 24},
{IT_STRING | IT_CALL, NULL, "Emblem Hints...", M_EmblemHints, 24},
{IT_STRING | IT_CALL, NULL, "Switch Gametype/Level...", M_MapChange, 32},
{IT_STRING | IT_CALL, NULL, "Continue", M_SelectableClearMenus,40},
{IT_STRING | IT_CALL, NULL, "Player 1 Setup", M_SetupMultiPlayer, 48}, // splitscreen
{IT_STRING | IT_CALL, NULL, "Player 2 Setup", M_SetupMultiPlayer2, 56}, // splitscreen
{IT_STRING | IT_CALL, NULL, "Continue", M_SelectableClearMenus,48},
{IT_STRING | IT_CALL, NULL, "Spectate", M_ConfirmSpectate, 48},
{IT_STRING | IT_CALL, NULL, "Enter Game", M_ConfirmEnterGame, 48},
{IT_STRING | IT_SUBMENU, NULL, "Switch Team...", &MISC_ChangeTeamDef, 48},
{IT_STRING | IT_CALL, NULL, "Player Setup", M_SetupMultiPlayer, 56}, // alone
{IT_STRING | IT_CALL, NULL, "Options", M_Options, 64},
{IT_STRING | IT_CALL, NULL, "Player 1 Setup", M_SetupMultiPlayer, 56}, // splitscreen
{IT_STRING | IT_CALL, NULL, "Player 2 Setup", M_SetupMultiPlayer2, 64},
{IT_STRING | IT_CALL, NULL, "Return to Title", M_EndGame, 80},
{IT_STRING | IT_CALL, NULL, "Quit Game", M_QuitSRB2, 88},
{IT_STRING | IT_CALL, NULL, "Spectate", M_ConfirmSpectate, 56}, // alone
{IT_STRING | IT_CALL, NULL, "Enter Game", M_ConfirmEnterGame, 56},
{IT_STRING | IT_SUBMENU, NULL, "Switch Team...", &MISC_ChangeTeamDef, 56},
{IT_STRING | IT_CALL, NULL, "Player Setup", M_SetupMultiPlayer, 64},
{IT_STRING | IT_CALL, NULL, "Options", M_Options, 72},
{IT_STRING | IT_CALL, NULL, "Return to Title", M_EndGame, 88},
{IT_STRING | IT_CALL, NULL, "Quit Game", M_QuitSRB2, 96},
};
typedef enum
{
mpause_addons = 0,
mpause_scramble,
mpause_hints,
mpause_switchmap,
mpause_continue,
@ -597,7 +602,7 @@ static menuitem_t SPauseMenu[] =
// Pandora's Box will be shifted up if both options are available
{IT_CALL | IT_STRING, NULL, "Pandora's Box...", M_PandorasBox, 16},
{IT_CALL | IT_STRING, NULL, "Emblem Hints...", M_EmblemHints, 24},
{IT_CALL | IT_STRING, NULL, "Level Select...", M_LoadGameLevelSelect, 32},
{IT_CALL | IT_STRING, NULL, "Level Select...", M_PauseLevelSelect, 32},
{IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus,48},
{IT_CALL | IT_STRING, NULL, "Retry", M_Retry, 56},
@ -970,7 +975,7 @@ static menuitem_t MP_MainMenu[] =
{
{IT_HEADER, NULL, "Join a game", NULL, 0},
{IT_STRING|IT_CALL, NULL, "Server browser...", M_ConnectMenuModChecks, 12},
{IT_STRING|IT_KEYHANDLER, NULL, "Specify IPv4 address:", M_HandleConnectIP, 22},
{IT_STRING|IT_KEYHANDLER, NULL, "Specify server address:", M_HandleConnectIP, 22},
{IT_HEADER, NULL, "Host a game", NULL, 54},
{IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 66},
{IT_STRING|IT_CALL, NULL, "Splitscreen...", M_StartSplitServerMenu, 76},
@ -1824,6 +1829,10 @@ menu_t SP_LevelSelectDef = MAPPLATTERMENUSTYLE(
MTREE4(MN_SP_MAIN, MN_SP_LOAD, MN_SP_PLAYER, MN_SP_LEVELSELECT),
NULL, SP_LevelSelectMenu);
menu_t SP_PauseLevelSelectDef = MAPPLATTERMENUSTYLE(
MTREE4(MN_SP_MAIN, MN_SP_LOAD, MN_SP_PLAYER, MN_SP_LEVELSELECT),
NULL, SP_LevelSelectMenu);
menu_t SP_LevelStatsDef =
{
MTREE2(MN_SP_MAIN, MN_SP_LEVELSTATS),
@ -2284,6 +2293,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt);
// Nextmap. Used for Level select.
void Nextmap_OnChange(void)
{
gamedata_t *data = clientGamedata;
char *leveltitle;
char tabase[256];
#ifdef OLDNREPLAYNAME
@ -2301,7 +2311,7 @@ void Nextmap_OnChange(void)
{
CV_StealthSetValue(&cv_dummymares, 0);
// Hide the record changing CVAR if only one mare is available.
if (!nightsrecords[cv_nextmap.value-1] || nightsrecords[cv_nextmap.value-1]->nummares < 2)
if (!data->nightsrecords[cv_nextmap.value-1] || data->nightsrecords[cv_nextmap.value-1]->nummares < 2)
SP_NightsAttackMenu[narecords].status = IT_DISABLED;
else
SP_NightsAttackMenu[narecords].status = IT_STRING|IT_CVAR;
@ -2432,14 +2442,15 @@ void Nextmap_OnChange(void)
static void Dummymares_OnChange(void)
{
if (!nightsrecords[cv_nextmap.value-1])
gamedata_t *data = clientGamedata;
if (!data->nightsrecords[cv_nextmap.value-1])
{
CV_StealthSetValue(&cv_dummymares, 0);
return;
}
else
{
UINT8 mares = nightsrecords[cv_nextmap.value-1]->nummares;
UINT8 mares = data->nightsrecords[cv_nextmap.value-1]->nummares;
if (cv_dummymares.value < 0)
CV_StealthSetValue(&cv_dummymares, mares);
@ -3670,9 +3681,9 @@ void M_StartControlPanel(void)
if (!Playing())
{
// Secret menu!
MainMenu[singleplr].alphaKey = (M_AnySecretUnlocked()) ? 76 : 84;
MainMenu[multiplr].alphaKey = (M_AnySecretUnlocked()) ? 84 : 92;
MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
MainMenu[singleplr].alphaKey = (M_AnySecretUnlocked(clientGamedata)) ? 76 : 84;
MainMenu[multiplr].alphaKey = (M_AnySecretUnlocked(clientGamedata)) ? 84 : 92;
MainMenu[secrets].status = (M_AnySecretUnlocked(clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
currentMenu = &MainDef;
itemOn = singleplr;
@ -3680,14 +3691,14 @@ void M_StartControlPanel(void)
else if (modeattacking)
{
currentMenu = &MAPauseDef;
MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
MAPauseMenu[mapause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
itemOn = mapause_continue;
}
else if (!(netgame || multiplayer)) // Single Player
{
if (gamestate != GS_LEVEL || ultimatemode) // intermission, so gray out stuff.
{
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA)) ? (IT_GRAYEDOUT) : (IT_DISABLED);
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata)) ? (IT_GRAYEDOUT) : (IT_DISABLED);
SPauseMenu[spause_retry].status = IT_GRAYEDOUT;
}
else
@ -3696,7 +3707,7 @@ void M_StartControlPanel(void)
if (players[consoleplayer].playerstate != PST_LIVE)
++numlives;
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
// The list of things that can disable retrying is (was?) a little too complex
// for me to want to use the short if statement syntax
@ -3707,10 +3718,10 @@ void M_StartControlPanel(void)
}
// We can always use level select though. :33
SPauseMenu[spause_levelselect].status = (gamecomplete == 1) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
SPauseMenu[spause_levelselect].status = (maplistoption != 0) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
// And emblem hints.
SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
// Shift up Pandora's Box if both pandora and levelselect are active
/*if (SPauseMenu[spause_pandora].status != (IT_DISABLED)
@ -3745,12 +3756,10 @@ void M_StartControlPanel(void)
if (splitscreen)
{
MPauseMenu[mpause_psetupsplit].status = MPauseMenu[mpause_psetupsplit2].status = IT_STRING | IT_CALL;
MPauseMenu[mpause_psetup].text = "Player 1 Setup";
}
else
{
MPauseMenu[mpause_psetup].status = IT_STRING | IT_CALL;
MPauseMenu[mpause_psetup].text = "Player Setup";
if (G_GametypeHasTeams())
MPauseMenu[mpause_switchteam].status = IT_STRING | IT_SUBMENU;
@ -3760,6 +3769,8 @@ void M_StartControlPanel(void)
MPauseMenu[mpause_spectate].status = IT_GRAYEDOUT;
}
MPauseMenu[mpause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && G_CoopGametype()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
currentMenu = &MPauseDef;
itemOn = mpause_continue;
}
@ -4259,7 +4270,7 @@ static void M_DrawMapEmblems(INT32 mapnum, INT32 x, INT32 y, boolean norecordatt
x -= 4;
lasttype = curtype;
if (emblem->collected)
if (clientGamedata->collected[emblem - emblemlocations])
V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
else
@ -4692,7 +4703,9 @@ static void M_DrawGenericScrollMenu(void)
static void M_DrawPauseMenu(void)
{
if (!netgame && !multiplayer && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
gamedata_t *data = clientGamedata;
if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
{
emblem_t *emblem_detail[3] = {NULL, NULL, NULL};
char emblem_text[3][20];
@ -4720,7 +4733,7 @@ static void M_DrawPauseMenu(void)
{
case ET_SCORE:
snprintf(targettext, 9, "%d", emblem->var);
snprintf(currenttext, 9, "%u", G_GetBestScore(gamemap));
snprintf(currenttext, 9, "%u", G_GetBestScore(gamemap, data));
targettext[8] = 0;
currenttext[8] = 0;
@ -4734,7 +4747,7 @@ static void M_DrawPauseMenu(void)
G_TicsToSeconds((tic_t)emblemslot),
G_TicsToCentiseconds((tic_t)emblemslot));
emblemslot = (INT32)G_GetBestTime(gamemap); // dumb hack pt ii
emblemslot = (INT32)G_GetBestTime(gamemap, data); // dumb hack pt ii
if ((tic_t)emblemslot == UINT32_MAX)
snprintf(currenttext, 9, "-:--.--");
else
@ -4750,7 +4763,7 @@ static void M_DrawPauseMenu(void)
break;
case ET_RINGS:
snprintf(targettext, 9, "%d", emblem->var);
snprintf(currenttext, 9, "%u", G_GetBestRings(gamemap));
snprintf(currenttext, 9, "%u", G_GetBestRings(gamemap, data));
targettext[8] = 0;
currenttext[8] = 0;
@ -4758,8 +4771,8 @@ static void M_DrawPauseMenu(void)
emblemslot = 2;
break;
case ET_NGRADE:
snprintf(targettext, 9, "%u", P_GetScoreForGradeOverall(gamemap, emblem->var));
snprintf(currenttext, 9, "%u", G_GetBestNightsScore(gamemap, 0));
snprintf(targettext, 9, "%u", P_GetScoreForGrade(gamemap, 0, emblem->var));
snprintf(currenttext, 9, "%u", G_GetBestNightsScore(gamemap, 0, data));
targettext[8] = 0;
currenttext[8] = 0;
@ -4773,7 +4786,7 @@ static void M_DrawPauseMenu(void)
G_TicsToSeconds((tic_t)emblemslot),
G_TicsToCentiseconds((tic_t)emblemslot));
emblemslot = (INT32)G_GetBestNightsTime(gamemap, 0); // dumb hack pt iv
emblemslot = (INT32)G_GetBestNightsTime(gamemap, 0, data); // dumb hack pt iv
if ((tic_t)emblemslot == UINT32_MAX)
snprintf(currenttext, 9, "-:--.--");
else
@ -4807,7 +4820,7 @@ static void M_DrawPauseMenu(void)
if (!emblem)
continue;
if (emblem->collected)
if (data->collected[emblem - emblemlocations])
V_DrawSmallMappedPatch(40, 44 + (i*8), 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
else
@ -5019,7 +5032,9 @@ static void M_PatchSkinNameTable(void)
//
static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
{
if (M_MapLocked(mapnum+1))
gamedata_t *data = serverGamedata;
if (M_MapLocked(mapnum+1, data))
return false; // not unlocked
switch (levellistmode)
@ -5032,7 +5047,7 @@ static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
return true;
#ifndef DEVELOP
if (mapvisited[mapnum]) // MV_MP
if (data->mapvisited[mapnum])
#endif
return true;
@ -5040,7 +5055,7 @@ static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
case LLM_RECORDATTACK:
case LLM_NIGHTSATTACK:
#ifndef DEVELOP
if (mapvisited[mapnum] & MV_MAX)
if (data->mapvisited[mapnum])
return true;
if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED)
@ -5071,7 +5086,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt)
if (!mapheaderinfo[mapnum]->lvlttl[0])
return false;
/*if (M_MapLocked(mapnum+1))
/*if (M_MapLocked(mapnum+1, serverGamedata))
return false; // not unlocked*/
switch (levellistmode)
@ -5966,7 +5981,7 @@ static void M_DrawLevelPlatterMenu(void)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
else if (!curbghide || !titlemapinaction)
{
F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
F_SkyScroll(curbgname);
// Draw and animate foreground
if (!strncmp("RECATKBG", curbgname, 8))
M_DrawRecordAttackForeground();
@ -6228,7 +6243,7 @@ static void M_DrawMessageMenu(void)
}
else
{
F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
F_SkyScroll(curbgname);
if (!strncmp("RECATKBG", curbgname, 8))
M_DrawRecordAttackForeground();
}
@ -6904,7 +6919,7 @@ static void M_HandleAddons(INT32 choice)
closefilemenu(true);
// secrets disabled by addfile...
MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
MainMenu[secrets].status = (M_AnySecretUnlocked(clientGamedata)) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
if (currentMenu->prevMenu)
M_SetupNextMenu(currentMenu->prevMenu);
@ -7118,6 +7133,7 @@ static void M_DestroyRobots(INT32 choice)
static void M_LevelSelectWarp(INT32 choice)
{
boolean fromloadgame = (currentMenu == &SP_LevelSelectDef);
boolean frompause = (currentMenu == &SP_PauseLevelSelectDef);
(void)choice;
@ -7128,7 +7144,6 @@ static void M_LevelSelectWarp(INT32 choice)
}
startmap = (INT16)(cv_nextmap.value);
fromlevelselect = true;
if (fromloadgame)
@ -7136,7 +7151,20 @@ static void M_LevelSelectWarp(INT32 choice)
else
{
cursaveslot = 0;
M_SetupChoosePlayer(0);
if (frompause)
{
M_ClearMenus(true);
G_DeferedInitNew(false, G_BuildMapName(startmap), cv_skin.value, false, fromlevelselect); // Not sure about using cv_skin here, but it seems fine in testing.
COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
if (levelselect.rows)
Z_Free(levelselect.rows);
levelselect.rows = NULL;
}
else
M_SetupChoosePlayer(0);
}
}
@ -7150,7 +7178,9 @@ static boolean checklist_cangodown; // uuuueeerggghhhh HACK
static void M_HandleChecklist(INT32 choice)
{
gamedata_t *data = clientGamedata;
INT32 j;
switch (choice)
{
case KEY_DOWNARROW:
@ -7167,7 +7197,7 @@ static void M_HandleChecklist(INT32 choice)
continue;
if (unlockables[j].conditionset > MAXCONDITIONSETS)
continue;
if (!unlockables[j].unlocked && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset))
if (!data->unlocked[j] && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset, data))
continue;
if (unlockables[j].conditionset == unlockables[check_on].conditionset)
continue;
@ -7192,7 +7222,7 @@ static void M_HandleChecklist(INT32 choice)
continue;
if (unlockables[j].conditionset > MAXCONDITIONSETS)
continue;
if (!unlockables[j].unlocked && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset))
if (!data->unlocked[j] && unlockables[j].showconditionset && !M_Achieved(unlockables[j].showconditionset, data))
continue;
if (j && unlockables[j].conditionset == unlockables[j-1].conditionset)
continue;
@ -7218,6 +7248,9 @@ static void M_HandleChecklist(INT32 choice)
static void M_DrawChecklist(void)
{
gamedata_t *data = clientGamedata;
INT32 emblemCount = M_CountEmblems(data);
INT32 i = check_on, j = 0, y = currentMenu->y, emblems = numemblems+numextraemblems;
UINT32 condnum, previd, maxcond;
condition_t *cond;
@ -7228,7 +7261,7 @@ static void M_DrawChecklist(void)
// draw emblem counter
if (emblems > 0)
{
V_DrawString(42, 20, (emblems == M_CountEmblems()) ? V_GREENMAP : 0, va("%d/%d", M_CountEmblems(), emblems));
V_DrawString(42, 20, (emblems == emblemCount) ? V_GREENMAP : 0, va("%d/%d", emblemCount, emblems));
V_DrawSmallScaledPatch(28, 20, 0, W_CachePatchName("EMBLICON", PU_PATCH));
}
@ -7239,13 +7272,13 @@ static void M_DrawChecklist(void)
{
if (unlockables[i].name[0] == 0 //|| unlockables[i].nochecklist
|| !unlockables[i].conditionset || unlockables[i].conditionset > MAXCONDITIONSETS
|| (!unlockables[i].unlocked && unlockables[i].showconditionset && !M_Achieved(unlockables[i].showconditionset)))
|| (!data->unlocked[i] && unlockables[i].showconditionset && !M_Achieved(unlockables[i].showconditionset, data)))
{
i += 1;
continue;
}
V_DrawString(currentMenu->x, y, ((unlockables[i].unlocked) ? V_GREENMAP : V_TRANSLUCENT)|V_ALLOWLOWERCASE, ((unlockables[i].unlocked || !unlockables[i].nochecklist) ? unlockables[i].name : M_CreateSecretMenuOption(unlockables[i].name)));
V_DrawString(currentMenu->x, y, ((data->unlocked[i]) ? V_GREENMAP : V_TRANSLUCENT)|V_ALLOWLOWERCASE, ((data->unlocked[i] || !unlockables[i].nochecklist) ? unlockables[i].name : M_CreateSecretMenuOption(unlockables[i].name)));
for (j = i+1; j < MAXUNLOCKABLES; j++)
{
@ -7323,7 +7356,7 @@ static void M_DrawChecklist(void)
if (title)
{
const char *level = ((M_MapLocked(cond[condnum].requirement) || !((mapheaderinfo[cond[condnum].requirement-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].requirement-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
const char *level = ((M_MapLocked(cond[condnum].requirement, data) || !((mapheaderinfo[cond[condnum].requirement-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].requirement-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
switch (cond[condnum].type)
{
@ -7356,7 +7389,7 @@ static void M_DrawChecklist(void)
if (title)
{
const char *level = ((M_MapLocked(cond[condnum].extrainfo1) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
const char *level = ((M_MapLocked(cond[condnum].extrainfo1, data) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
switch (cond[condnum].type)
{
@ -7425,7 +7458,7 @@ static void M_DrawChecklist(void)
if (title)
{
const char *level = ((M_MapLocked(cond[condnum].extrainfo1) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
const char *level = ((M_MapLocked(cond[condnum].extrainfo1, data) || !((mapheaderinfo[cond[condnum].extrainfo1-1]->menuflags & LF2_NOVISITNEEDED) || (data->mapvisited[cond[condnum].extrainfo1-1] & MV_MAX))) ? M_CreateSecretMenuOption(title) : title);
switch (cond[condnum].type)
{
@ -7488,7 +7521,7 @@ static void M_DrawChecklist(void)
/*V_DrawString(160, 8+(24*j), V_RETURN8, V_WordWrap(160, 292, 0, unlockables[i].objective));
if (unlockables[i].unlocked)
if (data->unlocked[i])
V_DrawString(308, 8+(24*j), V_YELLOWMAP, "Y");
else
V_DrawString(308, 8+(24*j), V_YELLOWMAP, "N");*/
@ -7517,7 +7550,7 @@ static void M_EmblemHints(INT32 choice)
(void)choice;
SR_EmblemHintMenu[0].status = (local > NUMHINTS*2) ? (IT_STRING | IT_ARROWS) : (IT_DISABLED);
SR_EmblemHintMenu[1].status = (M_SecretUnlocked(SECRET_ITEMFINDER)) ? (IT_CVAR|IT_STRING) : (IT_SECRET);
SR_EmblemHintMenu[1].status = (M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata)) ? (IT_CVAR|IT_STRING) : (IT_SECRET);
hintpage = 1;
SR_EmblemHintDef.prevMenu = currentMenu;
M_SetupNextMenu(&SR_EmblemHintDef);
@ -7577,7 +7610,7 @@ static void M_DrawEmblemHints(void)
if (totalemblems >= ((hintpage-1)*(NUMHINTS*2) + 1) && totalemblems < (hintpage*NUMHINTS*2)+1){
if (emblem->collected)
if (clientGamedata->collected[i])
{
collected = V_GREENMAP;
V_DrawMappedPatch(x, y+4, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
@ -7647,6 +7680,26 @@ static void M_HandleEmblemHints(INT32 choice)
}
static void M_PauseLevelSelect(INT32 choice)
{
(void)choice;
SP_PauseLevelSelectDef.prevMenu = currentMenu;
levellistmode = LLM_LEVELSELECT;
// maplistoption is NOT specified, so that this
// transfers the level select list from the menu
// used to enter the game to the pause menu.
if (!M_PrepareLevelPlatter(-1, true))
{
M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING);
return;
}
M_SetupNextMenu(&SP_PauseLevelSelectDef);
}
/*static void M_DrawSkyRoom(void)
{
INT32 i, y = 0;
@ -8117,7 +8170,7 @@ static void M_SecretsMenu(INT32 choice)
SR_MainMenu[i].status = IT_SECRET;
if (unlockables[ul].unlocked)
if (clientGamedata->unlocked[ul])
{
switch (unlockables[ul].type)
{
@ -8154,6 +8207,7 @@ INT32 ultimate_selectable = false;
static void M_NewGame(void)
{
fromlevelselect = false;
maplistoption = 0;
startmap = spstage_start;
CV_SetValue(&cv_newgametype, GT_COOP); // Graue 09-08-2004
@ -8165,6 +8219,7 @@ static void M_CustomWarp(INT32 choice)
{
INT32 ul = skyRoomMenuTranslations[choice-1];
maplistoption = 0;
startmap = (INT16)(unlockables[ul].variable);
M_SetupChoosePlayer(0);
@ -8216,7 +8271,7 @@ static void M_SinglePlayerMenu(INT32 choice)
levellistmode = LLM_RECORDATTACK;
if (M_GametypeHasLevels(-1))
SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET;
SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK, clientGamedata)) ? IT_CALL|IT_STRING : IT_SECRET;
else // If Record Attack is nonexistent in the current add-on...
{
SP_MainMenu[sprecordattack].status = IT_NOTHING|IT_DISABLED; // ...hide and disable the Record Attack option...
@ -8226,7 +8281,7 @@ static void M_SinglePlayerMenu(INT32 choice)
levellistmode = LLM_NIGHTSATTACK;
if (M_GametypeHasLevels(-1))
SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET;
SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE, clientGamedata)) ? IT_CALL|IT_STRING : IT_SECRET;
else // If NiGHTS Mode is nonexistent in the current add-on...
{
SP_MainMenu[spnightsmode].status = IT_NOTHING|IT_DISABLED; // ...hide and disable the NiGHTS Mode option...
@ -8249,7 +8304,7 @@ static void M_SinglePlayerMenu(INT32 choice)
SP_MainMenu[spnightsmode] .alphaKey += 8;
}
else // Otherwise, if Marathon Run is allowed and Record Attack is unlocked, unlock Marathon Run!
SP_MainMenu[spmarathon].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED : IT_SECRET;
SP_MainMenu[spmarathon].status = (M_SecretUnlocked(SECRET_RECORDATTACK, clientGamedata)) ? IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED : IT_SECRET;
if (tutorialmap) // If there's a tutorial available in the current add-on...
@ -8357,6 +8412,7 @@ static void M_StartTutorial(INT32 choice)
M_ClearMenus(true);
gamecomplete = 0;
cursaveslot = 0;
maplistoption = 0;
G_DeferedInitNew(false, G_BuildMapName(tutorialmap), 0, false, false);
}
@ -8719,6 +8775,10 @@ static void M_LoadSelect(INT32 choice)
{
(void)choice;
// Reset here, if we want a level select
// M_LoadGameLevelSelect will set it for us.
maplistoption = 0;
if (saveSlotSelected == NOSAVESLOT) //last slot is play without saving
{
M_NewGame();
@ -9626,7 +9686,7 @@ static void M_Statistics(INT32 choice)
if (!(mapheaderinfo[i]->typeoflevel & TOL_SP) || (mapheaderinfo[i]->menuflags & LF2_HIDEINSTATS))
continue;
if (!(mapvisited[i] & MV_MAX))
if (!(clientGamedata->mapvisited[i] & MV_MAX))
continue;
statsMapList[j++] = i;
@ -9643,6 +9703,7 @@ static void M_Statistics(INT32 choice)
static void M_DrawStatsMaps(int location)
{
gamedata_t *data = clientGamedata;
INT32 y = 80, i = -1;
INT16 mnum;
extraemblem_t *exemblem;
@ -9710,14 +9771,14 @@ static void M_DrawStatsMaps(int location)
{
exemblem = &extraemblems[i];
if (exemblem->collected)
if (data->extraCollected[i])
V_DrawSmallMappedPatch(292, y, 0, W_CachePatchName(M_GetExtraEmblemPatch(exemblem, false), PU_PATCH),
R_GetTranslationColormap(TC_DEFAULT, M_GetExtraEmblemColor(exemblem), GTC_CACHE));
else
V_DrawSmallScaledPatch(292, y, 0, W_CachePatchName("NEEDIT", PU_PATCH));
V_DrawString(20, y, V_YELLOWMAP|V_ALLOWLOWERCASE,
(!exemblem->collected && exemblem->showconditionset && !M_Achieved(exemblem->showconditionset))
(!data->extraCollected[i] && exemblem->showconditionset && !M_Achieved(exemblem->showconditionset, data))
? M_CreateSecretMenuOption(exemblem->description)
: exemblem->description);
}
@ -9734,6 +9795,7 @@ bottomarrow:
static void M_DrawLevelStats(void)
{
gamedata_t *data = clientGamedata;
char beststr[40];
tic_t besttime = 0;
@ -9748,9 +9810,9 @@ static void M_DrawLevelStats(void)
V_DrawString(20, 24, V_YELLOWMAP, "Total Play Time:");
V_DrawCenteredString(BASEVIDWIDTH/2, 32, 0, va("%i hours, %i minutes, %i seconds",
G_TicsToHours(totalplaytime),
G_TicsToMinutes(totalplaytime, false),
G_TicsToSeconds(totalplaytime)));
G_TicsToHours(data->totalplaytime),
G_TicsToMinutes(data->totalplaytime, false),
G_TicsToSeconds(data->totalplaytime)));
for (i = 0; i < NUMMAPS; i++)
{
@ -9759,25 +9821,25 @@ static void M_DrawLevelStats(void)
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
if (!mainrecords[i])
if (!data->mainrecords[i])
{
mapsunfinished++;
bestunfinished[0] = bestunfinished[1] = bestunfinished[2] = true;
continue;
}
if (mainrecords[i]->score > 0)
bestscore += mainrecords[i]->score;
if (data->mainrecords[i]->score > 0)
bestscore += data->mainrecords[i]->score;
else
mapunfinished = bestunfinished[0] = true;
if (mainrecords[i]->time > 0)
besttime += mainrecords[i]->time;
if (data->mainrecords[i]->time > 0)
besttime += data->mainrecords[i]->time;
else
mapunfinished = bestunfinished[1] = true;
if (mainrecords[i]->rings > 0)
bestrings += mainrecords[i]->rings;
if (data->mainrecords[i]->rings > 0)
bestrings += data->mainrecords[i]->rings;
else
mapunfinished = bestunfinished[2] = true;
@ -9792,7 +9854,7 @@ static void M_DrawLevelStats(void)
else
V_DrawString(20, 56, V_GREENMAP, "(complete)");
V_DrawString(36, 64, 0, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems));
V_DrawString(36, 64, 0, va("x %d/%d", M_CountEmblems(data), numemblems+numextraemblems));
V_DrawSmallScaledPatch(20, 64, 0, W_CachePatchName("EMBLICON", PU_PATCH));
sprintf(beststr, "%u", bestscore);
@ -9859,6 +9921,7 @@ static void M_HandleLevelStats(INT32 choice)
// Drawing function for Time Attack
void M_DrawTimeAttackMenu(void)
{
gamedata_t *data = clientGamedata;
INT32 i, x, y, empatx, empaty, cursory = 0;
UINT16 dispstatus;
patch_t *PictureOfUrFace; // my WHAT
@ -9875,7 +9938,7 @@ void M_DrawTimeAttackMenu(void)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
else if (!curbghide || !titlemapinaction)
{
F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
F_SkyScroll(curbgname);
// Draw and animate foreground
if (!strncmp("RECATKBG", curbgname, 8))
M_DrawRecordAttackForeground();
@ -10017,7 +10080,7 @@ void M_DrawTimeAttackMenu(void)
empatx = empatch->leftoffset / 2;
empaty = empatch->topoffset / 2;
if (em->collected)
if (data->collected[em - emblemlocations])
V_DrawSmallMappedPatch(104+76+empatx, yHeight+lsheadingheight/2+empaty, 0, empatch,
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
else
@ -10030,34 +10093,34 @@ void M_DrawTimeAttackMenu(void)
// Draw in-level emblems.
M_DrawMapEmblems(cv_nextmap.value, 288, 28, true);
if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->score)
if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->score)
sprintf(beststr, "(none)");
else
sprintf(beststr, "%u", mainrecords[cv_nextmap.value-1]->score);
sprintf(beststr, "%u", data->mainrecords[cv_nextmap.value-1]->score);
V_DrawString(104-72, 33+lsheadingheight/2, V_YELLOWMAP, "SCORE:");
V_DrawRightAlignedString(104+64, 33+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
V_DrawRightAlignedString(104+72, 43+lsheadingheight/2, V_ALLOWLOWERCASE, reqscore);
if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->time)
if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->time)
sprintf(beststr, "(none)");
else
sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(mainrecords[cv_nextmap.value-1]->time, true),
G_TicsToSeconds(mainrecords[cv_nextmap.value-1]->time),
G_TicsToCentiseconds(mainrecords[cv_nextmap.value-1]->time));
sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(data->mainrecords[cv_nextmap.value-1]->time, true),
G_TicsToSeconds(data->mainrecords[cv_nextmap.value-1]->time),
G_TicsToCentiseconds(data->mainrecords[cv_nextmap.value-1]->time));
V_DrawString(104-72, 53+lsheadingheight/2, V_YELLOWMAP, "TIME:");
V_DrawRightAlignedString(104+64, 53+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
V_DrawRightAlignedString(104+72, 63+lsheadingheight/2, V_ALLOWLOWERCASE, reqtime);
if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->rings)
if (!data->mainrecords[cv_nextmap.value-1] || !data->mainrecords[cv_nextmap.value-1]->rings)
sprintf(beststr, "(none)");
else
sprintf(beststr, "%hu", mainrecords[cv_nextmap.value-1]->rings);
sprintf(beststr, "%hu", data->mainrecords[cv_nextmap.value-1]->rings);
V_DrawString(104-72, 73+lsheadingheight/2, V_YELLOWMAP, "RINGS:");
V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((mapvisited[cv_nextmap.value-1] & MV_PERFECTRA) ? V_YELLOWMAP : 0), beststr);
V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((data->mapvisited[cv_nextmap.value-1] & MV_PERFECTRA) ? V_YELLOWMAP : 0), beststr);
V_DrawRightAlignedString(104+72, 83+lsheadingheight/2, V_ALLOWLOWERCASE, reqrings);
}
@ -10151,6 +10214,7 @@ static void M_TimeAttack(INT32 choice)
// Drawing function for Nights Attack
void M_DrawNightsAttackMenu(void)
{
gamedata_t *data = clientGamedata;
INT32 i, x, y, cursory = 0;
UINT16 dispstatus;
@ -10217,10 +10281,10 @@ void M_DrawNightsAttackMenu(void)
lumpnum_t lumpnum;
char beststr[40];
//UINT8 bestoverall = G_GetBestNightsGrade(cv_nextmap.value, 0);
UINT8 bestgrade = G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value);
UINT32 bestscore = G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value);
tic_t besttime = G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value);
//UINT8 bestoverall = G_GetBestNightsGrade(cv_nextmap.value, 0, data);
UINT8 bestgrade = G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value, data);
UINT32 bestscore = G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value, data);
tic_t besttime = G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value, data);
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
@ -10301,7 +10365,7 @@ void M_DrawNightsAttackMenu(void)
goto skipThisOne;
}
if (em->collected)
if (data->collected[em - emblemlocations])
V_DrawSmallMappedPatch(xpos, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em, false), PU_PATCH),
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
else
@ -10655,6 +10719,7 @@ static void M_Marathon(INT32 choice)
}
fromlevelselect = false;
maplistoption = 0;
startmap = spmarathon_start;
CV_SetValue(&cv_newgametype, GT_COOP); // Graue 09-08-2004
@ -11673,35 +11738,19 @@ static void M_StartServerMenu(INT32 choice)
#define CONNIP_LEN 128
static char setupm_ip[CONNIP_LEN];
#define DOTS "... "
// Draw the funky Connect IP menu. Tails 11-19-2002
// So much work for such a little thing!
static void M_DrawMPMainMenu(void)
static void M_DrawConnectIP(void)
{
INT32 x = currentMenu->x;
INT32 y = currentMenu->y;
INT32 y = currentMenu->y + 22;
const INT32 boxwidth = /*16*8 + 6*/ (BASEVIDWIDTH - 2*(x+5));
const INT32 maxstrwidth = boxwidth - 5;
char *drawnstr = malloc(sizeof(setupm_ip));
char *drawnstr_orig = drawnstr;
boolean drawthin, shorten = false;
// use generic drawer for cursor, items and title
M_DrawGenericMenu();
V_DrawRightAlignedString(BASEVIDWIDTH-x, y+66,
((itemOn == 4) ? V_YELLOWMAP : 0), va("(2-%d players)", MAXPLAYERS));
V_DrawRightAlignedString(BASEVIDWIDTH-x, y+76,
((itemOn == 5) ? V_YELLOWMAP : 0), "(2 players)");
V_DrawRightAlignedString(BASEVIDWIDTH-x, y+116,
((itemOn == 8) ? V_YELLOWMAP : 0), "(splitscreen)");
y += 22;
V_DrawFill(x+5, y+4+5, boxwidth, 8+6, 159);
strcpy(drawnstr, setupm_ip);
@ -11749,6 +11798,28 @@ static void M_DrawMPMainMenu(void)
free(drawnstr_orig);
}
// Draw the funky Connect IP menu. Tails 11-19-2002
// So much work for such a little thing!
static void M_DrawMPMainMenu(void)
{
INT32 x = currentMenu->x;
INT32 y = currentMenu->y;
// use generic drawer for cursor, items and title
M_DrawGenericMenu();
V_DrawRightAlignedString(BASEVIDWIDTH-x, y+66,
((itemOn == 4) ? V_YELLOWMAP : 0), va("(2-%d players)", MAXPLAYERS));
V_DrawRightAlignedString(BASEVIDWIDTH-x, y+76,
((itemOn == 5) ? V_YELLOWMAP : 0), "(2 players)");
V_DrawRightAlignedString(BASEVIDWIDTH-x, y+116,
((itemOn == 8) ? V_YELLOWMAP : 0), "(splitscreen)");
M_DrawConnectIP();
}
#undef DOTS
// Tails 11-19-2002
@ -11886,7 +11957,11 @@ static void M_HandleConnectIP(INT32 choice)
break;
// Rudimentary number and period enforcing - also allows letters so hostnames can be used instead
if ((choice >= '-' && choice <= ':') || (choice >= 'A' && choice <= 'Z') || (choice >= 'a' && choice <= 'z'))
// and square brackets for RFC 2732 IPv6 addresses
if ((choice >= '-' && choice <= ':') ||
(choice == '[' || choice == ']') ||
(choice >= 'A' && choice <= 'Z') ||
(choice >= 'a' && choice <= 'z'))
{
S_StartSound(NULL,sfx_menu1); // Tails
setupm_ip[l] = (char)choice;
@ -12544,12 +12619,12 @@ static void M_EraseDataResponse(INT32 ch)
// Delete the data
if (erasecontext != 1)
G_ClearRecords();
G_ClearRecords(clientGamedata);
if (erasecontext != 0)
M_ClearSecrets();
M_ClearSecrets(clientGamedata);
if (erasecontext == 2)
{
totalplaytime = 0;
clientGamedata->totalplaytime = 0;
F_StartIntro();
}
BwehHehHe();

View file

@ -14,12 +14,11 @@
#include "doomdef.h"
#include "doomtype.h"
#include "doomstat.h" // totalplaytime
#include "m_random.h"
#include "m_fixed.h"
#include "m_cond.h" // totalplaytime
// ---------------------------
// RNG functions (not synched)
@ -252,5 +251,5 @@ void P_SetRandSeedD(const char *rfile, INT32 rline, UINT32 seed)
*/
UINT32 M_RandomizedSeed(void)
{
return ((totalplaytime & 0xFFFF) << 16)|M_RandomFixed();
return ((serverGamedata->totalplaytime & 0xFFFF) << 16) | M_RandomFixed();
}

View file

@ -33,7 +33,7 @@ typedef union
typedef struct
{
msg_header_t header;
char ip[16];
char ip[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
char port[8];
char name[32];
INT32 room;

View file

@ -164,6 +164,62 @@ boolean P_CanPickupItem(player_t *player, boolean weapon)
return true;
}
boolean P_CanPickupEmblem(player_t *player, INT32 emblemID)
{
emblem_t *emblem = NULL;
if (emblemID < 0 || emblemID >= numemblems)
{
// Invalid emblem ID, can't pickup.
return false;
}
emblem = &emblemlocations[emblemID];
if (demoplayback)
{
// Never collect emblems in replays.
return false;
}
if (player->bot && player->bot != BOT_MPAI)
{
// Your little lap-dog can't grab these for you.
return false;
}
if (emblem->type == ET_SKIN)
{
INT32 skinnum = M_EmblemSkinNum(emblem);
if (player->skin != skinnum)
{
// Incorrect skin to pick up this emblem.
return false;
}
}
return true;
}
boolean P_EmblemWasCollected(INT32 emblemID)
{
if (emblemID < 0 || emblemID >= numemblems)
{
// Invalid emblem ID, can't pickup.
return true;
}
if (shareEmblems && !serverGamedata->collected[emblemID])
{
// It can be worth collecting again if we're sharing emblems
// and the server doesn't have it.
return false;
}
return clientGamedata->collected[emblemID];
}
//
// P_DoNightsScore
//
@ -563,7 +619,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
special->momx = special->momy = special->momz = 0;
P_GivePlayerSpheres(player, 1);
if (special->type == MT_BLUESPHERE)
if (special->type == MT_BLUESPHERE || special->type == MT_FLINGBLUESPHERE)
{
special->destscale = ((player->powers[pw_carry] == CR_NIGHTSMODE) ? 4 : 2)*special->scale;
if (states[special->info->deathstate].tics > 0)
@ -738,13 +794,70 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// Secret emblem thingy
case MT_EMBLEM:
{
if (demoplayback || (player->bot && player->bot != BOT_MPAI) || special->health <= 0 || special->health > MAXEMBLEMS)
return;
emblemlocations[special->health-1].collected = true;
const boolean toucherIsServer = ((player - players) == serverplayer);
const boolean consoleIsServer = (consoleplayer == serverplayer);
boolean prevCollected = false;
M_UpdateUnlockablesAndExtraEmblems();
G_SaveGameData();
break;
if ((special->flags2 & MF2_NIGHTSPULL)
&& (toucher == special->tracer))
{
// Since collecting may not remove the object,
// we need to manually stop it from chasing.
P_SetTarget(&special->tracer, NULL);
special->flags2 &= ~MF2_NIGHTSPULL;
special->movefactor = 0;
special->momx = special->momy = special->momz = 0;
}
if (!P_CanPickupEmblem(player, special->health - 1))
{
return;
}
prevCollected = P_EmblemWasCollected(special->health - 1);
if (toucherIsServer || shareEmblems)
{
serverGamedata->collected[special->health-1] = true;
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
}
if (P_IsLocalPlayer(player) || (consoleIsServer && shareEmblems))
{
clientGamedata->collected[special->health-1] = true;
M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
G_SaveGameData(clientGamedata);
}
if (netgame)
{
// This always spawns the object to prevent mobjnum issues,
// but makes the effect invisible to whoever it doesn't matter to.
mobj_t *spark = P_SpawnMobjFromMobj(special, 0, 0, 0, MT_SPARK);
if (prevCollected == false && P_EmblemWasCollected(special->health - 1) == true)
{
// Play the sound if it was collected.
S_StartSound((shareEmblems ? NULL : special), special->info->deathsound);
}
else
{
// We didn't collect it, make it invisible to us.
spark->flags2 |= MF2_DONTDRAW;
}
return;
}
else
{
if (prevCollected == false && P_EmblemWasCollected(special->health - 1) == true)
{
// Disappear when collecting for local games.
break;
}
return;
}
}
// CTF Flags

View file

@ -510,6 +510,8 @@ void P_ClearStarPost(INT32 postnum);
void P_ResetStarposts(void);
boolean P_CanPickupItem(player_t *player, boolean weapon);
boolean P_CanPickupEmblem(player_t *player, INT32 emblemID);
boolean P_EmblemWasCollected(INT32 emblemID);
void P_DoNightsScore(player_t *player);
void P_DoMatchSuper(player_t *player);

View file

@ -2728,8 +2728,22 @@ increment_move
fixed_t thingtop;
floatok = false;
if (radius < MAXRADIUS/2)
radius = MAXRADIUS/2;
// This makes sure that there are no freezes from computing extremely small movements.
// Originally was MAXRADIUS/2, but that can cause some bad inconsistencies for small players.
radius = max(radius, thing->scale);
// And we also have to prevent Big Large (tm) movements, as those can skip too far
// across slopes and cause us to fail step up checks on them when we otherwise shouldn't.
radius = min(radius, 16 * thing->scale);
// (This whole "step" system is flawed; it was OK before, but the addition of slopes has
// exposed the problems with doing it like this. The right thing to do would be to use
// raycasting for physics to fix colliding in weird order, double-checking collisions,
// randomly colliding with slopes instead of going up them, etc. I don't feel like porting
// that from RR, as its both a huge sweeping change and still incomplete at the time of
// writing. Clamping radius to make our steps more precise will work just fine as long
// as you keep all of your crazy intentions to poke any of the other deep-rooted movement
// code to yourself. -- Sal 6/5/2023)
do {
if (thing->flags & MF_NOCLIP) {
@ -4242,13 +4256,11 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
// the way it was and call P_CheckSector (? was P_ChangeSector - Graue) again
// to undo the changes.
//
static boolean crushchange;
static boolean nofit;
//
// PIT_ChangeSector
//
static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush, boolean crunch)
{
mobj_t *killer = NULL;
//If a thing is both pushable and vulnerable, it doesn't block the crusher because it gets killed.
@ -4272,11 +4284,7 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
if (thing->z + thing->height > thing->ceilingz && thing->z <= thing->ceilingz)
{
if (immunepushable && thing->z + thing->height > thing->subsector->sector->ceilingheight)
{
//Thing is a pushable and blocks the moving ceiling
nofit = true;
return false;
}
return false; //Thing is a pushable and blocks the moving ceiling
//Check FOFs in the sector
if (thing->subsector->sector->ffloors && (realcrush || immunepushable))
@ -4288,47 +4296,54 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
for (rover = thing->subsector->sector->ffloors; rover; rover = rover->next)
{
if (!(((rover->fofflags & FOF_BLOCKPLAYER) && thing->player)
|| ((rover->fofflags & FOF_BLOCKOTHERS) && !thing->player)) || !(rover->fofflags & FOF_EXISTS))
thinker_t *think;
if (!(rover->fofflags & FOF_EXISTS))
continue;
if (thing->player && !(rover->fofflags & FOF_BLOCKPLAYER))
continue;
if (!thing->player && !(rover->fofflags & FOF_BLOCKOTHERS))
continue;
topheight = *rover->topheight;
bottomheight = *rover->bottomheight;
//topheight = P_GetFFloorTopZAt (rover, thing->x, thing->y);
//bottomheight = P_GetFFloorBottomZAt(rover, thing->x, thing->y);
if (bottomheight > thing->ceilingz)
continue;
delta1 = thing->z - (bottomheight + topheight)/2;
delta2 = thingtop - (bottomheight + topheight)/2;
if (bottomheight <= thing->ceilingz && abs(delta1) >= abs(delta2))
if (abs(delta1) < abs(delta2))
continue;
if (immunepushable)
return false; //FOF is blocked by pushable
if (!realcrush)
continue;
//If the thing was crushed by a crumbling FOF, reward the player who made it crumble!
for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
{
if (immunepushable)
{
//FOF is blocked by pushable
nofit = true;
return false;
}
else
{
//If the thing was crushed by a crumbling FOF, reward the player who made it crumble!
thinker_t *think;
crumble_t *crumbler;
crumble_t *crumbler;
for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)T_StartCrumble)
continue;
if (think->function.acp1 != (actionf_p1)T_StartCrumble)
continue;
crumbler = (crumble_t *)think;
crumbler = (crumble_t *)think;
if (crumbler->player && crumbler->player->mo
&& crumbler->player->mo != thing
&& crumbler->actionsector == thing->subsector->sector
&& crumbler->sector == rover->master->frontsector)
{
killer = crumbler->player->mo;
}
}
}
if (!crumbler->player)
continue;
if (!crumbler->player->mo)
continue;
if (crumbler->player->mo == thing)
continue;
if (crumbler->actionsector != thing->subsector->sector)
continue;
if (crumbler->sector != rover->master->frontsector)
continue;
killer = crumbler->player->mo;
}
}
}
@ -4344,24 +4359,139 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
}
}
if (realcrush && crushchange)
if (realcrush && crunch)
P_DamageMobj(thing, NULL, NULL, 1, 0);
// keep checking (crush other things)
return true;
}
static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boolean crunch)
{
size_t i;
// Sal: This stupid function chain is required to fix polyobjects not being able to crush.
// Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead
validcount++;
for (i = 0; i < sector->linecount; i++)
{
INT32 x, y;
polyobj_t *po = sector->lines[i]->polyobj;
if (!po)
continue;
if (po->validcount == validcount)
continue; // skip if already checked
if (!(po->flags & POF_SOLID))
continue;
if (po->lines[0]->backsector != sector) // Make sure you're currently checking the control sector
continue;
po->validcount = validcount;
for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y)
{
for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
{
mobj_t *mo;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue;
mo = blocklinks[y * bmapwidth + x];
for (; mo; mo = mo->bnext)
{
// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
if (!P_MobjInsidePolyobj(po, mo))
continue;
if (!PIT_ChangeSector(mo, realcrush, crunch) && !realcrush)
return false;
}
}
}
}
return true;
}
static boolean P_CheckTouchingThinglist(sector_t *sector, boolean realcrush, boolean crunch)
{
msecnode_t *n;
for (n = sector->touching_thinglist; n; n = n->m_thinglist_next)
n->visited = false;
do
{
for (n = sector->touching_thinglist; n; n = n->m_thinglist_next) // go through list
{
if (n->visited)
continue;
n->visited = true; // mark thing as processed
if (n->m_thing->flags & MF_NOBLOCKMAP) //jff 4/7/98 don't do these
continue;
if (!PIT_ChangeSector(n->m_thing, realcrush, crunch) && !realcrush) // process it
return false;
break; // exit and start over
}
} while (n); // repeat from scratch until all things left are marked valid
return true;
}
static boolean P_CheckSectorFFloors(sector_t *sector, boolean realcrush, boolean crunch)
{
sector_t *sec;
size_t i;
if (!sector->numattached)
return true;
for (i = 0; i < sector->numattached; i++)
{
sec = &sectors[sector->attached[i]];
sec->moved = true;
P_RecalcPrecipInSector(sec);
if (!sector->attachedsolid[i])
continue;
if (!P_CheckTouchingThinglist(sec, realcrush, crunch))
return false;
}
return true;
}
static boolean P_CheckSectorHelper(sector_t *sector, boolean realcrush, boolean crunch)
{
if (!P_CheckSectorPolyObjects(sector, realcrush, crunch))
return false;
if (!P_CheckSectorFFloors(sector, realcrush, crunch))
return false;
// Mark all things invalid
sector->moved = true;
return P_CheckTouchingThinglist(sector, realcrush, crunch);
}
//
// P_CheckSector
//
boolean P_CheckSector(sector_t *sector, boolean crunch)
{
msecnode_t *n;
size_t i;
nofit = false;
crushchange = crunch;
// killough 4/4/98: scan list front-to-back until empty or exhausted,
// restarting from beginning after each thing is processed. Avoids
// crashes, and is sure to examine all things in the sector, and only
@ -4370,218 +4500,14 @@ boolean P_CheckSector(sector_t *sector, boolean crunch)
//
// killough 4/7/98: simplified to avoid using complicated counter
// First, let's see if anything will keep it from crushing.
// Sal: This stupid function chain is required to fix polyobjects not being able to crush.
// Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead
validcount++;
for (i = 0; i < sector->linecount; i++)
{
if (sector->lines[i]->polyobj)
{
polyobj_t *po = sector->lines[i]->polyobj;
if (po->validcount == validcount)
continue; // skip if already checked
if (!(po->flags & POF_SOLID))
continue;
if (po->lines[0]->backsector == sector) // Make sure you're currently checking the control sector
{
INT32 x, y;
po->validcount = validcount;
for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y)
{
for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
{
mobj_t *mo;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue;
mo = blocklinks[y * bmapwidth + x];
for (; mo; mo = mo->bnext)
{
// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
if (!P_MobjInsidePolyobj(po, mo))
continue;
if (!PIT_ChangeSector(mo, false))
{
nofit = true;
return nofit;
}
}
}
}
}
}
}
if (sector->numattached)
{
sector_t *sec;
for (i = 0; i < sector->numattached; i++)
{
sec = &sectors[sector->attached[i]];
for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
n->visited = false;
sec->moved = true;
P_RecalcPrecipInSector(sec);
if (!sector->attachedsolid[i])
continue;
do
{
for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
if (!n->visited)
{
n->visited = true;
if (!(n->m_thing->flags & MF_NOBLOCKMAP))
{
if (!PIT_ChangeSector(n->m_thing, false))
{
nofit = true;
return nofit;
}
}
break;
}
} while (n);
}
}
// Mark all things invalid
sector->moved = true;
for (n = sector->touching_thinglist; n; n = n->m_thinglist_next)
n->visited = false;
do
{
for (n = sector->touching_thinglist; n; n = n->m_thinglist_next) // go through list
if (!n->visited) // unprocessed thing found
{
n->visited = true; // mark thing as processed
if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
{
if (!PIT_ChangeSector(n->m_thing, false)) // process it
{
nofit = true;
return nofit;
}
}
break; // exit and start over
}
} while (n); // repeat from scratch until all things left are marked valid
if (!P_CheckSectorHelper(sector, false, crunch))
return true;
// Nothing blocked us, so lets crush for real!
P_CheckSectorHelper(sector, true, crunch);
// Sal: This stupid function chain is required to fix polyobjects not being able to crush.
// Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead
validcount++;
for (i = 0; i < sector->linecount; i++)
{
if (sector->lines[i]->polyobj)
{
polyobj_t *po = sector->lines[i]->polyobj;
if (po->validcount == validcount)
continue; // skip if already checked
if (!(po->flags & POF_SOLID))
continue;
if (po->lines[0]->backsector == sector) // Make sure you're currently checking the control sector
{
INT32 x, y;
po->validcount = validcount;
for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y)
{
for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x)
{
mobj_t *mo;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue;
mo = blocklinks[y * bmapwidth + x];
for (; mo; mo = mo->bnext)
{
// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
if (!P_MobjInsidePolyobj(po, mo))
continue;
PIT_ChangeSector(mo, true);
return nofit;
}
}
}
}
}
}
if (sector->numattached)
{
sector_t *sec;
for (i = 0; i < sector->numattached; i++)
{
sec = &sectors[sector->attached[i]];
for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
n->visited = false;
sec->moved = true;
P_RecalcPrecipInSector(sec);
if (!sector->attachedsolid[i])
continue;
do
{
for (n = sec->touching_thinglist; n; n = n->m_thinglist_next)
if (!n->visited)
{
n->visited = true;
if (!(n->m_thing->flags & MF_NOBLOCKMAP))
{
PIT_ChangeSector(n->m_thing, true);
return nofit;
}
break;
}
} while (n);
}
}
// Mark all things invalid
sector->moved = true;
for (n = sector->touching_thinglist; n; n = n->m_thinglist_next)
n->visited = false;
do
{
for (n = sector->touching_thinglist; n; n = n->m_thinglist_next) // go through list
if (!n->visited) // unprocessed thing found
{
n->visited = true; // mark thing as processed
if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these
{
PIT_ChangeSector(n->m_thing, true); // process it
return nofit;
}
break; // exit and start over
}
} while (n); // repeat from scratch until all things left are marked valid
return nofit;
return false;
}
/*

View file

@ -509,26 +509,26 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
// on non-solid polyobjects should NEVER happen in the future
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat
texbottom = back->floorheight + side->rowoffset;
textop = back->ceilingheight + side->rowoffset;
texbottom = back->floorheight + side->rowoffset + side->offsety_mid;
textop = back->ceilingheight + side->rowoffset + side->offsety_mid;
} else if (linedef->flags & ML_MIDTEX) {
texbottom = back->floorheight + side->rowoffset;
texbottom = back->floorheight + side->rowoffset + side->offsety_mid;
textop = texbottom + texheight*(side->repeatcnt+1);
} else {
textop = back->ceilingheight + side->rowoffset;
textop = back->ceilingheight + side->rowoffset + side->offsety_mid;
texbottom = textop - texheight*(side->repeatcnt+1);
}
} else
#endif
{
if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat
texbottom = openbottom + side->rowoffset;
textop = opentop + side->rowoffset;
texbottom = openbottom + side->rowoffset + side->offsety_mid;
textop = opentop + side->rowoffset + side->offsety_mid;
} else if (linedef->flags & ML_MIDPEG) {
texbottom = openbottom + side->rowoffset;
texbottom = openbottom + side->rowoffset + side->offsety_mid;
textop = texbottom + texheight*(side->repeatcnt+1);
} else {
textop = opentop + side->rowoffset;
textop = opentop + side->rowoffset + side->offsety_mid;
texbottom = textop - texheight*(side->repeatcnt+1);
}
}

View file

@ -3139,7 +3139,8 @@ boolean P_SceneryZMovement(mobj_t *mo)
if (P_CheckDeathPitCollide(mo))
{
P_RemoveMobj(mo);
if (mo->type != MT_GHOST) // ghosts play death animations instead, so don't remove them
P_RemoveMobj(mo);
return false;
}
@ -9716,6 +9717,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
A_AttractChase(mobj);
break;
case MT_EMBLEM:
if (P_EmblemWasCollected(mobj->health - 1) || !P_CanPickupEmblem(&players[consoleplayer], mobj->health - 1))
mobj->frame |= (tr_trans50 << FF_TRANSSHIFT);
else
mobj->frame &= ~FF_TRANSMASK;
if (mobj->flags2 & MF2_NIGHTSPULL)
P_NightsItemChase(mobj);
break;
@ -12005,8 +12011,8 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
break;
case MT_EMBLEM:
if (netgame || multiplayer)
return false; // Single player (You're next on my shit list)
if (!G_CoopGametype())
return false; // Gametype's not right
break;
default:
break;
@ -12150,7 +12156,6 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
INT32 j;
emblem_t* emblem = M_GetLevelEmblems(gamemap);
skincolornum_t emcolor;
boolean validEmblem = true;
while (emblem)
{
@ -12175,42 +12180,19 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting
mobj->color = (UINT16)emcolor;
validEmblem = !emblemlocations[j].collected;
mobj->frame &= ~FF_TRANSMASK;
if (emblemlocations[j].type == ET_SKIN)
if (emblemlocations[j].type == ET_GLOBAL)
{
INT32 skinnum = M_EmblemSkinNum(&emblemlocations[j]);
if (players[0].skin != skinnum)
mobj->reactiontime = emblemlocations[j].var;
if (emblemlocations[j].var & GE_NIGHTSITEM)
{
validEmblem = false;
mobj->flags |= MF_NIGHTSITEM;
mobj->flags &= ~MF_SPECIAL;
mobj->flags2 |= MF2_DONTDRAW;
}
}
if (validEmblem == false)
{
P_UnsetThingPosition(mobj);
mobj->flags |= MF_NOCLIP;
mobj->flags &= ~MF_SPECIAL;
mobj->flags |= MF_NOBLOCKMAP;
mobj->frame |= (tr_trans50 << FF_TRANSSHIFT);
P_SetThingPosition(mobj);
}
else
{
mobj->frame &= ~FF_TRANSMASK;
if (emblemlocations[j].type == ET_GLOBAL)
{
mobj->reactiontime = emblemlocations[j].var;
if (emblemlocations[j].var & GE_NIGHTSITEM)
{
mobj->flags |= MF_NIGHTSITEM;
mobj->flags &= ~MF_SPECIAL;
mobj->flags2 |= MF2_DONTDRAW;
}
}
}
return true;
}
@ -13706,7 +13688,6 @@ void P_SpawnItemPattern(mapthing_t *mthing, boolean bonustime)
UINT8 numitemtypes;
if (!udmf)
return;
CONS_Printf("Itemstring: %s\n", mthing->stringargs[0]);
P_ParseItemTypes(mthing->stringargs[0], itemtypes, &numitemtypes);
P_SpawnItemCircle(mthing, itemtypes, numitemtypes, mthing->args[0], mthing->args[1] << FRACBITS, bonustime);
return;

View file

@ -47,6 +47,7 @@ UINT8 *save_p;
#define ARCHIVEBLOCK_POBJS 0x7F928546
#define ARCHIVEBLOCK_THINKERS 0x7F37037C
#define ARCHIVEBLOCK_SPECIALS 0x7F228378
#define ARCHIVEBLOCK_EMBLEMS 0x7F4A5445
// Note: This cannot be bigger
// than an UINT16
@ -4339,6 +4340,8 @@ static void P_NetArchiveMisc(boolean resending)
WRITEUINT32(save_p, hidetime);
WRITEUINT32(save_p, unlocktriggers);
// Is it paused?
if (paused)
WRITEUINT8(save_p, 0x2f);
@ -4437,6 +4440,8 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
hidetime = READUINT32(save_p);
unlocktriggers = READUINT32(save_p);
// Is it paused?
if (READUINT8(save_p) == 0x2f)
paused = true;
@ -4444,6 +4449,224 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
return true;
}
static inline void P_NetArchiveEmblems(void)
{
gamedata_t *data = serverGamedata;
INT32 i, j;
UINT8 btemp;
INT32 curmare;
WRITEUINT32(save_p, ARCHIVEBLOCK_EMBLEMS);
// These should be synchronized before savegame loading by the wad files being the same anyway,
// but just in case, for now, we'll leave them here for testing. It would be very bad if they mismatch.
WRITEUINT8(save_p, (UINT8)savemoddata);
WRITEINT32(save_p, numemblems);
WRITEINT32(save_p, numextraemblems);
// The rest of this is lifted straight from G_SaveGameData in g_game.c
// TODO: Optimize this to only send information about emblems, unlocks, etc. which actually exist
// There is no need to go all the way up to MAXEMBLEMS when wads are guaranteed to be the same.
WRITEUINT32(save_p, data->totalplaytime);
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
WRITEUINT8(save_p, (data->mapvisited[i] & MV_MAX));
// To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < MAXEMBLEMS;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
btemp |= (data->collected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
for (i = 0; i < MAXEXTRAEMBLEMS;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
btemp |= (data->extraCollected[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
for (i = 0; i < MAXUNLOCKABLES;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
btemp |= (data->unlocked[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
for (i = 0; i < MAXCONDITIONSETS;)
{
btemp = 0;
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
btemp |= (data->achieved[j+i] << j);
WRITEUINT8(save_p, btemp);
i += j;
}
WRITEUINT32(save_p, data->timesBeaten);
WRITEUINT32(save_p, data->timesBeatenWithEmeralds);
WRITEUINT32(save_p, data->timesBeatenUltimate);
// Main records
for (i = 0; i < NUMMAPS; i++)
{
if (data->mainrecords[i])
{
WRITEUINT32(save_p, data->mainrecords[i]->score);
WRITEUINT32(save_p, data->mainrecords[i]->time);
WRITEUINT16(save_p, data->mainrecords[i]->rings);
}
else
{
WRITEUINT32(save_p, 0);
WRITEUINT32(save_p, 0);
WRITEUINT16(save_p, 0);
}
}
// NiGHTS records
for (i = 0; i < NUMMAPS; i++)
{
if (!data->nightsrecords[i] || !data->nightsrecords[i]->nummares)
{
WRITEUINT8(save_p, 0);
continue;
}
WRITEUINT8(save_p, data->nightsrecords[i]->nummares);
for (curmare = 0; curmare < (data->nightsrecords[i]->nummares + 1); ++curmare)
{
WRITEUINT32(save_p, data->nightsrecords[i]->score[curmare]);
WRITEUINT8(save_p, data->nightsrecords[i]->grade[curmare]);
WRITEUINT32(save_p, data->nightsrecords[i]->time[curmare]);
}
}
}
static inline void P_NetUnArchiveEmblems(void)
{
gamedata_t *data = serverGamedata;
INT32 i, j;
UINT8 rtemp;
UINT32 recscore;
tic_t rectime;
UINT16 recrings;
UINT8 recmares;
INT32 curmare;
if (READUINT32(save_p) != ARCHIVEBLOCK_EMBLEMS)
I_Error("Bad $$$.sav at archive block Emblems");
savemoddata = (boolean)READUINT8(save_p); // this one is actually necessary because savemoddata stays false otherwise for some reason.
if (numemblems != READINT32(save_p))
I_Error("numemblems mismatch");
if (numextraemblems != READINT32(save_p))
I_Error("numextraemblems mismatch");
// This shouldn't happen, but if something really fucked up happens and you transfer
// the SERVER player's gamedata over your own CLIENT gamedata,
// then this prevents it from being saved over yours.
data->loaded = false;
M_ClearSecrets(data);
G_ClearRecords(data);
// The rest of this is lifted straight from G_LoadGameData in g_game.c
// TODO: Optimize this to only read information about emblems, unlocks, etc. which actually exist
// There is no need to go all the way up to MAXEMBLEMS when wads are guaranteed to be the same.
data->totalplaytime = READUINT32(save_p);
// TODO put another cipher on these things? meh, I don't care...
for (i = 0; i < NUMMAPS; i++)
if ((data->mapvisited[i] = READUINT8(save_p)) > MV_MAX)
I_Error("Bad $$$.sav dearchiving Emblems");
// To save space, use one bit per collected/achieved/unlocked flag
for (i = 0; i < MAXEMBLEMS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
data->collected[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXEXTRAEMBLEMS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
data->extraCollected[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXUNLOCKABLES;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
data->unlocked[j+i] = ((rtemp >> j) & 1);
i += j;
}
for (i = 0; i < MAXCONDITIONSETS;)
{
rtemp = READUINT8(save_p);
for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
data->achieved[j+i] = ((rtemp >> j) & 1);
i += j;
}
data->timesBeaten = READUINT32(save_p);
data->timesBeatenWithEmeralds = READUINT32(save_p);
data->timesBeatenUltimate = READUINT32(save_p);
// Main records
for (i = 0; i < NUMMAPS; ++i)
{
recscore = READUINT32(save_p);
rectime = (tic_t)READUINT32(save_p);
recrings = READUINT16(save_p);
if (recrings > 10000 || recscore > MAXSCORE)
I_Error("Bad $$$.sav dearchiving Emblems");
if (recscore || rectime || recrings)
{
G_AllocMainRecordData((INT16)i, data);
data->mainrecords[i]->score = recscore;
data->mainrecords[i]->time = rectime;
data->mainrecords[i]->rings = recrings;
}
}
// Nights records
for (i = 0; i < NUMMAPS; ++i)
{
if ((recmares = READUINT8(save_p)) == 0)
continue;
G_AllocNightsRecordData((INT16)i, data);
for (curmare = 0; curmare < (recmares+1); ++curmare)
{
data->nightsrecords[i]->score[curmare] = READUINT32(save_p);
data->nightsrecords[i]->grade[curmare] = READUINT8(save_p);
data->nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
if (data->nightsrecords[i]->grade[curmare] > GRADE_S)
{
I_Error("Bad $$$.sav dearchiving Emblems");
}
}
data->nightsrecords[i]->nummares = recmares;
}
}
static inline void P_ArchiveLuabanksAndConsistency(void)
{
UINT8 i, banksinuse = NUM_LUABANKS;
@ -4507,6 +4730,7 @@ void P_SaveNetGame(boolean resending)
CV_SaveNetVars(&save_p);
P_NetArchiveMisc(resending);
P_NetArchiveEmblems();
// Assign the mobjnumber for pointer tracking
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
@ -4559,6 +4783,7 @@ boolean P_LoadNetGame(boolean reloading)
CV_LoadNetVars(&save_p);
if (!P_NetUnArchiveMisc(reloading))
return false;
P_NetUnArchiveEmblems();
P_NetUnArchivePlayers();
if (gamestate == GS_LEVEL)
{

View file

@ -876,7 +876,7 @@ static void P_SpawnMapThings(boolean spawnemblems)
size_t i;
mapthing_t *mt;
// Spawn axis points first so they are at the front of the list for fast searching.
// Spawn axis points first so they are at the front of the list for fast searching.
for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
{
switch (mt->type)
@ -1240,6 +1240,9 @@ static void P_LoadSidedefs(UINT8 *data)
}
sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
sd->offsetx_top = sd->offsetx_mid = sd->offsetx_bot = 0;
sd->offsety_top = sd->offsety_mid = sd->offsety_bot = 0;
P_SetSidedefSector(i, SHORT(msd->sector));
// Special info stored in texture fields!
@ -1777,6 +1780,18 @@ static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char
sides[i].textureoffset = atol(val)<<FRACBITS;
else if (fastcmp(param, "offsety"))
sides[i].rowoffset = atol(val)<<FRACBITS;
else if (fastcmp(param, "offsetx_top"))
sides[i].offsetx_top = atol(val) << FRACBITS;
else if (fastcmp(param, "offsetx_mid"))
sides[i].offsetx_mid = atol(val) << FRACBITS;
else if (fastcmp(param, "offsetx_bottom"))
sides[i].offsetx_bot = atol(val) << FRACBITS;
else if (fastcmp(param, "offsety_top"))
sides[i].offsety_top = atol(val) << FRACBITS;
else if (fastcmp(param, "offsety_mid"))
sides[i].offsety_mid = atol(val) << FRACBITS;
else if (fastcmp(param, "offsety_bottom"))
sides[i].offsety_bot = atol(val) << FRACBITS;
else if (fastcmp(param, "texturetop"))
sides[i].toptexture = R_TextureNumForName(val);
else if (fastcmp(param, "texturebottom"))
@ -2461,6 +2476,18 @@ static void P_WriteTextmap(void)
fprintf(f, "offsetx = %d;\n", wsides[i].textureoffset >> FRACBITS);
if (wsides[i].rowoffset != 0)
fprintf(f, "offsety = %d;\n", wsides[i].rowoffset >> FRACBITS);
if (wsides[i].offsetx_top != 0)
fprintf(f, "offsetx_top = %d;\n", wsides[i].offsetx_top >> FRACBITS);
if (wsides[i].offsety_top != 0)
fprintf(f, "offsety_top = %d;\n", wsides[i].offsety_top >> FRACBITS);
if (wsides[i].offsetx_mid != 0)
fprintf(f, "offsetx_mid = %d;\n", wsides[i].offsetx_mid >> FRACBITS);
if (wsides[i].offsety_mid != 0)
fprintf(f, "offsety_mid = %d;\n", wsides[i].offsety_mid >> FRACBITS);
if (wsides[i].offsetx_bot != 0)
fprintf(f, "offsetx_bottom = %d;\n", wsides[i].offsetx_bot >> FRACBITS);
if (wsides[i].offsety_bot != 0)
fprintf(f, "offsety_bottom = %d;\n", wsides[i].offsety_bot >> FRACBITS);
if (wsides[i].toptexture > 0 && wsides[i].toptexture < numtextures)
fprintf(f, "texturetop = \"%.*s\";\n", 8, textures[wsides[i].toptexture]->name);
if (wsides[i].bottomtexture > 0 && wsides[i].bottomtexture < numtextures)
@ -2828,6 +2855,8 @@ static void P_LoadTextmap(void)
// Defaults.
sd->textureoffset = 0;
sd->rowoffset = 0;
sd->offsetx_top = sd->offsetx_mid = sd->offsetx_bot = 0;
sd->offsety_top = sd->offsety_mid = sd->offsety_bot = 0;
sd->toptexture = R_TextureNumForName("-");
sd->midtexture = R_TextureNumForName("-");
sd->bottomtexture = R_TextureNumForName("-");
@ -7460,7 +7489,7 @@ static void P_WriteLetter(void)
{
char *buf, *b;
if (!unlockables[28].unlocked) // pandora's box
if (!serverGamedata->unlocked[28]) // pandora's box
return;
if (modeattacking)
@ -7804,10 +7833,11 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
nextmapoverride = 0;
skipstats = 0;
if (!(netgame || multiplayer || demoplayback))
mapvisited[gamemap-1] |= MV_VISITED;
else if (netgame || multiplayer)
mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently
if (!demoplayback)
{
clientGamedata->mapvisited[gamemap-1] |= MV_VISITED;
serverGamedata->mapvisited[gamemap-1] |= MV_VISITED;
}
levelloading = false;

View file

@ -1795,9 +1795,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
{ // Unlockable triggers required
INT32 trigid = triggerline->args[1];
if (netgame || multiplayer)
return false;
else if (trigid < 0 || trigid > 31) // limited by 32 bit variable
if (trigid < 0 || trigid > 31) // limited by 32 bit variable
{
CONS_Debug(DBG_GAMELOGIC, "Unlockable trigger (sidedef %hu): bad trigger ID %d\n", triggerline->sidenum[0], trigid);
return false;
@ -1810,14 +1808,12 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
{ // An unlockable itself must be unlocked!
INT32 unlockid = triggerline->args[1];
if (netgame || multiplayer)
return false;
else if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
if (unlockid < 0 || unlockid >= MAXUNLOCKABLES) // limited by unlockable count
{
CONS_Debug(DBG_GAMELOGIC, "Unlockable check (sidedef %hu): bad unlockable ID %d\n", triggerline->sidenum[0], unlockid);
return false;
}
else if (!(unlockables[unlockid-1].unlocked))
else if (!(serverGamedata->unlocked[unlockid-1]))
return false;
}
break;
@ -2942,7 +2938,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
break;
case 441: // Trigger unlockable
if (!(netgame || multiplayer))
{
INT32 trigid = line->args[0];
@ -2953,10 +2948,12 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
unlocktriggers |= 1 << trigid;
// Unlocked something?
if (M_UpdateUnlockablesAndExtraEmblems())
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
{
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData(); // only save if unlocked something
G_SaveGameData(clientGamedata); // only save if unlocked something
}
}
}

View file

@ -675,7 +675,10 @@ void P_Ticker(boolean run)
// Keep track of how long they've been playing!
if (!demoplayback) // Don't increment if a demo is playing.
totalplaytime++;
{
clientGamedata->totalplaytime++;
serverGamedata->totalplaytime++;
}
if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))
P_DoSpecialStageStuff();

View file

@ -559,6 +559,10 @@ typedef struct
// add this to the calculated texture top
fixed_t rowoffset;
// per-texture offsets for UDMF
fixed_t offsetx_top, offsetx_mid, offsetx_bot;
fixed_t offsety_top, offsety_mid, offsety_bot;
// Texture indices.
// We do not maintain names here.
INT32 toptexture, bottomtexture, midtexture;

View file

@ -1092,34 +1092,12 @@ subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y)
void R_SetupFrame(player_t *player)
{
camera_t *thiscam;
boolean chasecam = false;
if (splitscreen && player == &players[secondarydisplayplayer]
&& player != &players[consoleplayer])
{
boolean chasecam = R_ViewpointHasChasecam(player);
if (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer])
thiscam = &camera2;
chasecam = (cv_chasecam2.value != 0);
R_SetViewContext(VIEWCONTEXT_PLAYER2);
}
else
{
thiscam = &camera;
chasecam = (cv_chasecam.value != 0);
R_SetViewContext(VIEWCONTEXT_PLAYER1);
}
if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode)
chasecam = true; // force chasecam on
else if (player->spectator) // no spectator chasecam
chasecam = false; // force chasecam off
if (chasecam && !thiscam->chase)
{
P_ResetCamera(player, thiscam);
thiscam->chase = true;
}
else if (!chasecam)
thiscam->chase = false;
newview->sky = false;
@ -1348,11 +1326,37 @@ boolean R_ViewpointHasChasecam(player_t *player)
{
camera_t *thiscam;
boolean chasecam = false;
boolean isplayer2 = (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer]);
if (splitscreen && player == &players[secondarydisplayplayer] && player != &players[consoleplayer])
if (isplayer2)
{
thiscam = &camera2;
chasecam = (cv_chasecam2.value != 0);
}
else
{
thiscam = &camera;
chasecam = (cv_chasecam.value != 0);
}
if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode)
chasecam = true; // force chasecam on
else if (player->spectator) // no spectator chasecam
chasecam = false; // force chasecam off
if (chasecam && !thiscam->chase)
{
P_ResetCamera(player, thiscam);
thiscam->chase = true;
}
else if (!chasecam && thiscam->chase)
{
P_ResetCamera(player, thiscam);
thiscam->chase = false;
}
if (isplayer2)
{
R_SetViewContext(VIEWCONTEXT_PLAYER2);
if (thiscam->reset)
{
@ -1362,8 +1366,6 @@ boolean R_ViewpointHasChasecam(player_t *player)
}
else
{
thiscam = &camera;
chasecam = (cv_chasecam.value != 0);
R_SetViewContext(VIEWCONTEXT_PLAYER1);
if (thiscam->reset)
{
@ -1372,11 +1374,6 @@ boolean R_ViewpointHasChasecam(player_t *player)
}
}
if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode)
chasecam = true; // force chasecam on
else if (player->spectator) // no spectator chasecam
chasecam = false; // force chasecam off
return chasecam;
}

View file

@ -49,6 +49,7 @@ fixed_t rw_distance;
static INT32 rw_x, rw_stopx;
static angle_t rw_centerangle;
static fixed_t rw_offset;
static fixed_t rw_offset_top, rw_offset_mid, rw_offset_bot;
static fixed_t rw_offset2; // for splats
static fixed_t rw_scale, rw_scalestep;
static fixed_t rw_midtexturemid, rw_toptexturemid, rw_bottomtexturemid;
@ -778,7 +779,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
if (newline)
{
offsetvalue = sides[newline->sidenum[0]].rowoffset;
offsetvalue = sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid;
if (newline->flags & ML_DONTPEGBOTTOM)
{
skewslope = *pfloor->b_slope; // skew using bottom slope
@ -790,7 +791,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
}
else
{
offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset;
offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset + sides[pfloor->master->sidenum[0]].offsety_mid;
if (curline->linedef->flags & ML_DONTPEGBOTTOM)
{
skewslope = *pfloor->b_slope; // skew using bottom slope
@ -1335,7 +1336,7 @@ static void R_RenderSegLoop (void)
dc_yl = yl;
dc_yh = yh;
dc_texturemid = rw_midtexturemid;
dc_source = R_GetColumn(midtexture,texturecolumn);
dc_source = R_GetColumn(midtexture,texturecolumn + (rw_offset_mid>>FRACBITS));
dc_texheight = textureheight[midtexture]>>FRACBITS;
//profile stuff ---------------------------------------------------------
@ -1396,7 +1397,7 @@ static void R_RenderSegLoop (void)
dc_yl = yl;
dc_yh = mid;
dc_texturemid = rw_toptexturemid;
dc_source = R_GetColumn(toptexture,texturecolumn);
dc_source = R_GetColumn(toptexture,texturecolumn + (rw_offset_top>>FRACBITS));
dc_texheight = textureheight[toptexture]>>FRACBITS;
colfunc();
ceilingclip[rw_x] = (INT16)mid;
@ -1433,7 +1434,7 @@ static void R_RenderSegLoop (void)
dc_yh = yh;
dc_texturemid = rw_bottomtexturemid;
dc_source = R_GetColumn(bottomtexture,
texturecolumn);
texturecolumn + (rw_offset_bot>>FRACBITS));
dc_texheight = textureheight[bottomtexture]>>FRACBITS;
colfunc();
floorclip[rw_x] = (INT16)mid;
@ -1452,7 +1453,7 @@ static void R_RenderSegLoop (void)
{
// save texturecol
// for backdrawing of masked mid texture
maskedtexturecol[rw_x] = (INT16)texturecolumn;
maskedtexturecol[rw_x] = (INT16)(texturecolumn + (rw_offset_mid>>FRACBITS));
if (maskedtextureheight != NULL) {
maskedtextureheight[rw_x] = (curline->linedef->flags & ML_MIDPEG) ?
@ -1783,7 +1784,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
rw_midtexturemid = worldtop;
rw_midtextureslide = ceilingfrontslide;
}
rw_midtexturemid += sidedef->rowoffset;
rw_midtexturemid += sidedef->rowoffset + sidedef->offsety_mid;
ds_p->silhouette = SIL_BOTH;
ds_p->sprtopclip = screenheightarray;
@ -2022,8 +2023,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
}
}
rw_toptexturemid += sidedef->rowoffset;
rw_bottomtexturemid += sidedef->rowoffset;
rw_toptexturemid += sidedef->rowoffset + sidedef->offsety_top;
rw_bottomtexturemid += sidedef->rowoffset + sidedef->offsety_bot;
// allocate space for masked texture tables
if (frontsector && backsector && !Tag_Compare(&frontsector->tags, &backsector->tags) && (backsector->ffloors || frontsector->ffloors))
@ -2266,8 +2267,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
rw_midtexturebackslide = ceilingbackslide;
}
}
rw_midtexturemid += sidedef->rowoffset;
rw_midtextureback += sidedef->rowoffset;
rw_midtexturemid += sidedef->rowoffset + sidedef->offsety_mid;
rw_midtextureback += sidedef->rowoffset + sidedef->offsety_mid;
maskedtexture = true;
}
@ -2305,6 +2306,9 @@ void R_StoreWallRange(INT32 start, INT32 stop)
/// don't use texture offset for splats
rw_offset2 = rw_offset + curline->offset;
rw_offset += sidedef->textureoffset + curline->offset;
rw_offset_top = sidedef->offsetx_top;
rw_offset_mid = sidedef->offsetx_mid;
rw_offset_bot = sidedef->offsetx_bot;
rw_centerangle = ANGLE_90 + viewangle - rw_normalangle;
// calculate light table

View file

@ -194,7 +194,7 @@ UINT32 R_GetSkinAvailabilities(void)
return 0;
}
if (unlockables[i].unlocked)
if (clientGamedata->unlocked[i])
{
response |= (1 << unlockShift);
}
@ -242,11 +242,12 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
// Force 3.
return true;
}
if (playernum != -1 && players[playernum].bot)
{
//Force 4.
return true;
}
{
// Force 4.
return true;
}
// We will now check if this skin is supposed to be locked or not.
@ -284,7 +285,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
else
{
// We want to check our global unlockables.
return (unlockables[unlockID].unlocked);
return (clientGamedata->unlocked[unlockID]);
}
}

View file

@ -1324,6 +1324,8 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale,
if (trans >= 9) return;
scalemul = FixedMul(FRACUNIT - floordiff/640, scale);
if ((thing->scale != thing->old_scale) && (thing->scale >= FRACUNIT/1024)) // Interpolate shadows when scaling mobjs
scalemul = FixedMul(scalemul, FixedDiv(interp.scale, thing->scale));
patch = W_CachePatchName("DSHADOW", PU_SPRITE);
xscale = FixedDiv(projection, tz);

View file

@ -1692,6 +1692,7 @@ UINT8 soundtestpage = 1;
//
boolean S_PrepareSoundTest(void)
{
gamedata_t *data = clientGamedata;
musicdef_t *def;
INT32 pos = numsoundtestdefs = 0;
@ -1717,9 +1718,9 @@ boolean S_PrepareSoundTest(void)
if (!(def->soundtestpage & soundtestpage))
continue;
soundtestdefs[pos++] = def;
if (def->soundtestcond > 0 && !(mapvisited[def->soundtestcond-1] & MV_BEATEN))
if (def->soundtestcond > 0 && !(data->mapvisited[def->soundtestcond-1] & MV_BEATEN))
continue;
if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond))
if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond, data))
continue;
def->allowed = true;
}

View file

@ -2352,7 +2352,7 @@ void I_Quit(void)
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
G_SaveGameData(); // Tails 12-08-2002
G_SaveGameData(clientGamedata); // Tails 12-08-2002
//added:16-02-98: when recording a demo, should exit using 'q' key,
// but sometimes we forget and use 'F10'.. so save here too.
@ -2436,7 +2436,7 @@ void I_Error(const char *error, ...)
if (errorcount == 8)
{
M_SaveConfig(NULL);
G_SaveGameData();
G_SaveGameData(clientGamedata);
}
if (errorcount > 20)
{
@ -2469,7 +2469,7 @@ void I_Error(const char *error, ...)
#ifndef NONET
D_SaveBan(); // save the ban list
#endif
G_SaveGameData(); // Tails 12-08-2002
G_SaveGameData(clientGamedata); // Tails 12-08-2002
// Shutdown. Here might be other errors.
if (demorecording)

View file

@ -108,6 +108,9 @@ static patch_t *sneakers;
static patch_t *gravboots;
static patch_t *nonicon;
static patch_t *nonicon2;
static patch_t *nightopianhelper;
static patch_t *linkfreeze;
static patch_t *superparaloop;
static patch_t *bluestat;
static patch_t *byelstat;
static patch_t *orngstat;
@ -313,6 +316,10 @@ void ST_LoadGraphics(void)
nonicon2 = W_CachePatchName("NONICON2", PU_HUDGFX);
// NiGHTS HUD things
nightopianhelper = W_CachePatchName("NHLPICON", PU_HUDGFX);
linkfreeze = W_CachePatchName("NLFZICON", PU_HUDGFX);
superparaloop = W_CachePatchName("NSPRICON", PU_HUDGFX);
bluestat = W_CachePatchName("BLUESTAT", PU_HUDGFX);
byelstat = W_CachePatchName("BYELSTAT", PU_HUDGFX);
orngstat = W_CachePatchName("ORNGSTAT", PU_HUDGFX);
@ -1448,6 +1455,21 @@ void ST_drawWipeTitleCard(void)
}
}
#define ICONSEP (16+4) // matches weapon rings HUD
static INT32 ST_powerupHUDoffset(UINT16 timer)
{
if (timer > 7)
return ICONSEP;
else
{
UINT8 a = ICONSEP, b = 7-timer;
while (b--)
a = 2*a/3;
return a;
}
}
static void ST_drawPowerupHUD(void)
{
patch_t *p = NULL;
@ -1455,7 +1477,6 @@ static void ST_drawPowerupHUD(void)
INT32 offs = hudinfo[HUD_POWERUPS].x;
const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
static INT32 flagoffs[2] = {0, 0}, shieldoffs[2] = {0, 0}, finishoffs[2] = {0, 0};
#define ICONSEP (16+4) // matches weapon rings HUD
if (F_GetPromptHideHud(hudinfo[HUD_POWERUPS].y))
return;
@ -1567,15 +1588,7 @@ static void ST_drawPowerupHUD(void)
DRAWTIMERICON(invincibility, invulntime)
}
if (invulntime > 7)
offs -= ICONSEP;
else
{
UINT8 a = ICONSEP, b = 7-invulntime;
while (b--)
a = 2*a/3;
offs -= a;
}
offs -= ST_powerupHUDoffset(invulntime);
// Super Sneakers
if (stplyr->powers[pw_sneakers] > 3*TICRATE || (stplyr->powers[pw_sneakers] && leveltime & 1))
@ -1583,15 +1596,7 @@ static void ST_drawPowerupHUD(void)
DRAWTIMERICON(sneakers, stplyr->powers[pw_sneakers])
}
if (stplyr->powers[pw_sneakers] > 7)
offs -= ICONSEP;
else
{
UINT8 a = ICONSEP, b = 7-stplyr->powers[pw_sneakers];
while (b--)
a = 2*a/3;
offs -= a;
}
offs -= ST_powerupHUDoffset(stplyr->powers[pw_sneakers]);
// Gravity Boots
if (stplyr->powers[pw_gravityboots] > 3*TICRATE || (stplyr->powers[pw_gravityboots] && leveltime & 1))
@ -1599,6 +1604,36 @@ static void ST_drawPowerupHUD(void)
DRAWTIMERICON(gravboots, stplyr->powers[pw_gravityboots])
}
offs -= ST_powerupHUDoffset(stplyr->powers[pw_gravityboots]);
// --------------------
// NiGHTS timer-based powerups
// --------------------
// Nightopian Helper
if (stplyr->powers[pw_nights_helper] > 3*TICRATE || (stplyr->powers[pw_nights_helper] && leveltime & 1))
{
DRAWTIMERICON(nightopianhelper, stplyr->powers[pw_nights_helper])
}
offs -= ST_powerupHUDoffset(stplyr->powers[pw_nights_helper]);
// Link Freeze
if (stplyr->powers[pw_nights_linkfreeze] > 3*TICRATE || (stplyr->powers[pw_nights_linkfreeze] && leveltime & 1))
{
DRAWTIMERICON(linkfreeze, stplyr->powers[pw_nights_linkfreeze])
}
offs -= ST_powerupHUDoffset(stplyr->powers[pw_nights_linkfreeze]);
// Super Paraloop
if (stplyr->powers[pw_nights_superloop] > 3*TICRATE || (stplyr->powers[pw_nights_superloop] && leveltime & 1))
{
DRAWTIMERICON(superparaloop, stplyr->powers[pw_nights_superloop])
}
//offs -= ST_powerupHUDoffset(stplyr->powers[pw_nights_superloop]);
#undef DRAWTIMERICON
#undef ICONSEP
}
@ -1697,7 +1732,7 @@ static void ST_drawNightsRecords(void)
ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<<FRACBITS, 160<<FRACBITS, FRACUNIT, aflag, stplyr->lastmarescore, nightsnum, SKINCOLOR_AZURE);
// If new record, say so!
if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1) <= stplyr->lastmarescore)
if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1, clientGamedata) <= stplyr->lastmarescore)
{
if (stplyr->texttimer & 16)
V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *");
@ -2545,7 +2580,7 @@ static void ST_doHuntIconsAndSound(void)
S_StartSound(NULL, sfx_emfind);
}
static void ST_doItemFinderIconsAndSound(void)
static boolean ST_doItemFinderIconsAndSound(void)
{
INT32 emblems[16];
thinker_t *th;
@ -2556,6 +2591,12 @@ static void ST_doItemFinderIconsAndSound(void)
INT32 interval = 0, newinterval = 0;
INT32 soffset;
if (!(cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER, clientGamedata)))
{
// Not unlocked, or not enabled. Use emerald hunt radar.
return false;
}
for (i = 0; i < numemblems; ++i)
{
if (emblemlocations[i].type > ET_SKIN || emblemlocations[i].level != gamemap)
@ -2563,15 +2604,21 @@ static void ST_doItemFinderIconsAndSound(void)
emblems[stemblems++] = i;
if (!emblemlocations[i].collected)
if (!P_EmblemWasCollected(i) && P_CanPickupEmblem(stplyr, i))
{
++stunfound;
}
if (stemblems >= 16)
break;
}
// Found all/none exist? Don't waste our time
if (!stunfound)
return;
{
// Allow emerald hunt radar to function after they're all collected.
return false;
}
// Scan thinkers to find emblem mobj with these ids
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
@ -2591,6 +2638,9 @@ static void ST_doItemFinderIconsAndSound(void)
{
if (mo2->health == emblems[i] + 1)
{
if (P_EmblemWasCollected(emblems[i]) || !P_CanPickupEmblem(stplyr, emblems[i]))
break;
soffset = (i * 20) - ((stemblems - 1) * 10);
newinterval = ST_drawEmeraldHuntIcon(mo2, itemhoming, soffset);
@ -2605,6 +2655,8 @@ static void ST_doItemFinderIconsAndSound(void)
if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0 && renderisnewtic)
S_StartSound(NULL, sfx_emfind);
return true;
}
//
@ -2723,9 +2775,7 @@ static void ST_overlayDrawer(void)
ST_drawRaceHUD();
// Emerald Hunt Indicators
if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER))
ST_doItemFinderIconsAndSound();
else
if (!ST_doItemFinderIconsAndSound())
ST_doHuntIconsAndSound();
if(!P_IsLocalPlayer(stplyr))
@ -2740,18 +2790,16 @@ static void ST_overlayDrawer(void)
}
// This is where we draw all the fun cheese if you have the chasecam off!
if (!(maptol & TOL_NIGHTS))
if ((stplyr == &players[displayplayer] && !camera.chase)
|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))
{
if ((stplyr == &players[displayplayer] && !camera.chase)
|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))
{
ST_drawFirstPersonHUD();
if (cv_powerupdisplay.value)
ST_drawPowerupHUD(); // same as it ever was...
}
else if (cv_powerupdisplay.value == 2)
ST_drawFirstPersonHUD();
if (cv_powerupdisplay.value)
ST_drawPowerupHUD(); // same as it ever was...
}
else if (cv_powerupdisplay.value == 2)
ST_drawPowerupHUD(); // same as it ever was...
}
else if (!(netgame || multiplayer) && cv_powerupdisplay.value == 2)
ST_drawPowerupHUD(); // same as it ever was...

View file

@ -1092,12 +1092,14 @@ void Y_Ticker(void)
S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
// Update when done with tally
if (!(netgame || multiplayer) && !demoplayback)
if (!demoplayback)
{
if (M_UpdateUnlockablesAndExtraEmblems())
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData();
G_SaveGameData(clientGamedata);
}
}
else if (!(intertic & 1))
@ -1228,12 +1230,14 @@ void Y_Ticker(void)
S_StartSound(NULL, (gottoken ? sfx_token : sfx_chchng)); // cha-ching!
// Update when done with tally
if (!(netgame || multiplayer) && !demoplayback)
if (!demoplayback)
{
if (M_UpdateUnlockablesAndExtraEmblems())
M_SilentUpdateUnlockablesAndEmblems(serverGamedata);
if (M_UpdateUnlockablesAndExtraEmblems(clientGamedata))
S_StartSound(NULL, sfx_s3k68);
G_SaveGameData();
G_SaveGameData(clientGamedata);
}
}
else if (!(intertic & 1))