Compare commits

...
67 Commits
Author SHA1 Message Date
Alug 36cb5624be Revert "gitignore"
This reverts commit 7c7d0ba9c3.
2024-03-05 06:43:53 +01:00
Ashnal ed1554de24 Disabled sim before race begin and after race end
May help with wierd intermission issues
2020-02-29 19:41:22 -05:00
Ashnal df4222e8dc relink up waypoint cap on local state loading? 2020-02-24 17:44:54 -05:00
Ashnal 2e4a11fc64 prevent crash until voting screen is fixed 2020-02-24 16:38:06 -05:00
Louis 70f4b20161 Renamed some commands 2020-02-21 19:45:22 -05:00
Ashnal 0be50629c4 Fixed netgame waypoint crash 2020-02-21 19:35:54 -05:00
Ashnal bee9aaee63 Game connects and somewhat plays ...
still many problems and crashes
2020-02-20 22:02:23 -05:00
Ashnal 0184bd2779 Fixes the server not taking player cmd 2020-02-20 21:44:55 -05:00
Ashnal 2f410e59c7 Gonna disable this cmd adjustment as it causes
desyncs when switching levels
2020-02-20 19:20:38 -05:00
Ashnal ff258858c8 This ...might ... fix joining this as a server
moved tc_end to where a vanilla client expects it
2020-02-20 17:15:22 -05:00
Ashnal 7bfb22d10d It compiles ....
And sort of works ...
2020-02-19 17:44:50 -05:00
Louis 47960e5c17 Fixed continuous falling thinker bug
Resynchs are more effective
2020-02-17 02:08:19 -05:00
Louis c5f569eedc Adding experimental distance culling
Also fix or partially MF_NOTHINK errors (crab enemy)
**MAY BE UNSTABLE** - fixed some things, probably broke some others, the save/load code is a nightmare!
2020-02-17 02:08:15 -05:00
Louis 586cb358ba * Snarky timefudge message should now only appear in the correct conditions ;) 2020-02-17 02:06:57 -05:00
Louis 65b157cac0 * Added recommended simulate tics, so it should be safe to keep simulatetics at MAX without spike lag teleporting you. 2020-02-17 02:06:57 -05:00
Louis 0c864503a3 * and better time fudges for Linux, hopefully 2020-02-17 02:06:54 -05:00
Louis 2f57137d11 * New time fudge
* Some timer fixes
2020-02-17 01:58:27 -05:00
Louis 13d4f9e7d7 * Fixed chasecam during simulations! 2020-02-17 01:48:23 -05:00
Louis c38d7c1672 HEY YOU MISSED A SPOT 2020-02-17 01:47:25 -05:00
Louis 0ddbaeb2a2 One more thing to unbreak 2020-02-17 01:47:25 -05:00
Louis 1e2ddebced Last couple of Linux fixes hopefully 2020-02-17 01:47:24 -05:00
Louis acf1254494 Lol trying to fix all the Linux issues without a Linux compiler 2020-02-17 01:47:24 -05:00
Louis 007094e6d2 More Linux fixes?? 2020-02-17 01:47:19 -05:00
Louis 9c0f9b0637 Fixed Linux compiler errors (hopefully lol) 2020-02-17 01:46:39 -05:00
Louis cf0475dd94 Refined steadyplayer delayed rings 2020-02-17 01:46:39 -05:00
Louis afe57f1d05 Local sound effects play during simulations, other sound effects play during gametic 2020-02-17 01:46:39 -05:00
Louis a7b5df6793 Stability fixes for steadyplayers
Rail rings can now be delayed
2020-02-17 01:46:35 -05:00
Louis a8eb9b2485 Polyobjects are now synced with gratuitous awkwardness
Fixed executor crash
During savestates, console vars will be stealthy
2020-02-17 01:46:07 -05:00
Louis 7fd123d604 Sounds now play instantly (even if they're incorrect)
Added ringsling delay for when steadyplayers are enabled
The simulation was always one tic behind! OOPS!
2020-02-17 01:44:20 -05:00
Louis 0644ecd0c3 Various stability fixes, trail improvements 2020-02-17 01:43:20 -05:00
Louis 37cd0a596e Fixed crash bug
Further improved player trails
2020-02-17 01:42:44 -05:00
Louis 499177c4b9 Improved ghost trails! 2020-02-17 01:42:40 -05:00
Louis 961e08ab75 Added quick and dirty auto time fudge finger!
Smooth gameplay here we come!
2020-02-17 01:42:10 -05:00
Louis 178a59d3a1 Patched together some new save-load code, very messy, much faster though 2020-02-17 01:41:15 -05:00
Louis 6a4dc84a1f Committing this change as I'm about to dive into save/load code
But I can't remember what this change did and whether it worked lol (probably an attempted bugfix for low-ping simulations breaking)

#usefulcommitmessages
2020-02-17 01:38:18 -05:00
Louis 9930675c93 Added experimental time fudge (for better sync with server tics)
+ Smoothened out simulations slightly
2020-02-17 01:38:17 -05:00
Louis 697f6994c9 Steadyplayers is now variable (now refers to how many tics behind the simulation the other players can be)
Also cleaned up a little
2020-02-17 01:38:14 -05:00
Louis 5284ba6935 Added steadyplayer aim compensation 2020-02-17 01:36:17 -05:00
Louis 55752f3d68 WIP new faster state save/load code - but locking it off for now to playtest 2020-02-17 01:35:05 -05:00
Louis 2a69fa8f45 Added some benchmarking for the save/loads. playtest time? 2020-02-17 01:32:11 -05:00
Louis 7c0c584c06 Added detachable sounds, not the cleanest solution to the sound problem, but sufficient for now 2020-02-17 01:32:05 -05:00
Louis f3fea02e77 Removed flag spawning message. next steps: fix sounds 2020-02-17 01:23:48 -05:00
Louis 47e3616b4a Hopefully? fixed CTF issues 2020-02-17 01:23:48 -05:00
Louis f8a0eefa1a New time-encoding-based synchronisation
(a LOT better!)
2020-02-17 01:23:44 -05:00
Louis 6bc4102dfd Added artificial lag spikes
Next step is to try and encode cool info into ticcmds so we can match them accurately
2020-02-17 01:22:53 -05:00
Louis 2cec2a5135 Fixed precipitation crash
Might be more useful to ignore precipitation altogether in loads. Will see to this.
2020-02-17 01:22:48 -05:00
Louis 8d3a9abda3 Only show point limit change when it's actually different 2020-02-17 00:53:35 -05:00
Louis f596bf07d5 Increased ping limit a bit
Time for playtesting <3
2020-02-17 00:53:35 -05:00
Louis 75a4614acc Console now only shows messages during real game tics
Fixed game tic buffer not holding other players' tics
2020-02-17 00:53:34 -05:00
Louis 4fb02a2175 Platform cfails fixed! (savegames now store platform momentum) 2020-02-17 00:53:29 -05:00
Louis 0b315a8364 Fixed sectors not always rewinding correctly
(save states only saved sectors that changed, so if a sector returned to its original point it would not save its state)
2020-02-17 00:51:48 -05:00
Louis a5a43ec7a9 FIXED mobj_t LOADING MEMORY LEAK - this may still be in the live version too.
Keeping some debugging stuff in here until everything's confirmed working
Next steps: syncing platforms so they don't cfail
2020-02-17 00:49:32 -05:00
Louis f783819278 Combined smoothing and simulation; almost ready to test it
Todo fix a memory leak in the level load and also fix level consistency issues.
2020-02-17 00:46:30 -05:00
Louis 8d6ca2646d Final modifications before removing recording stuff 2020-02-17 00:44:44 -05:00
Louis a6e48de9a3 Basic WIP tic rewind code with:
* artificial lag
* rewind command
...
profit!
2020-02-17 00:23:27 -05:00
Ashnal 7c7d0ba9c3 gitignore 2020-02-17 00:07:04 -05:00
Ashnal eb631e1b09 Fixes loading net savegames on 1.1 servers
Still need to fix server save code to align with this
2020-02-16 19:44:28 -05:00
Ashnal d0da83e5cb Remove 2.2 LoadSlope and SaveSlope 2020-02-16 15:59:41 -05:00
Ashnal 9f9c81696d Fixing slopes that got messed up during merge
Copy slope got changes in 2.2 and somehow made it into here
2020-02-16 13:45:02 -05:00
Ashnal 76f9907288 Fix debug build crash
This was tryign to =check the thinker list
on the titlescreen befoire any thinkers exist
2020-02-15 20:34:23 -05:00
Ashnal e3050c64bc Fix crash on level loading 2020-02-15 20:20:35 -05:00
MascaraSnake 607e0468ef Merge branch 'thinkerlists_oops' into 'master'
Fixed a netgame crash that was caused by a closing brace being in the wrong place :v

See merge request STJr/SRB2Internal!395
2020-02-13 23:07:15 -05:00
Ashnal f7fb8a1897 FIxed a couple missing th = 2020-02-13 23:06:28 -05:00
Ashnal 9ce1e0b0bc Fixed my bruh moment
Re-added a number of cuntion in p_enemy.c that I deleted
2020-02-13 22:17:19 -05:00
Monster Iestyn 3957c04a9b Merge branch 'thinkerlists-fix' into 'master'
Thinkerlists fix

Closes #161

See merge request STJr/SRB2Internal!253
2020-02-13 21:44:46 -05:00
Ashnal e11fec1408 Removed p_user.c include
I'm a C noob, moving on
2020-02-13 21:07:16 -05:00
MascaraSnake a2339b7a66 Merge branch 'thinkerlists' into 'master'
Separate thinkers into different lists

See merge request STJr/SRB2Internal!219

Ashnal: tons of compile errors to fix
2020-02-13 20:43:51 -05:00
53 changed files with 4336 additions and 1288 deletions

No files matched your search

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
@@ -37,51 +37,51 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}</ProjectGuid>
<RootNamespace>libpng</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+10 -10
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
@@ -37,51 +37,51 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{73A5729C-7323-41D4-AB48-8A03C9F81603}</ProjectGuid>
<RootNamespace>zlib</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v142</PlatformToolset>
<WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+14
View File
@@ -15,6 +15,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "s_openal", "src\hardware\s_
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Srb2Win", "src\sdl\Srb2SDL-vc10.vcxproj", "{61BA7D3C-F77D-4D31-B718-1177FE482CF2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Srb2DebugServer", "Srb2DebugServer\Srb2DebugServer.vcxproj", "{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@@ -115,6 +117,18 @@ Global
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|Win32.Build.0 = Release|Win32
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|x64.ActiveCfg = Release|x64
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|x64.Build.0 = Release|x64
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Debug|ARM.ActiveCfg = Debug|Win32
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Debug|ARM64.ActiveCfg = Debug|Win32
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Debug|Win32.ActiveCfg = Debug|Win32
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Debug|Win32.Build.0 = Debug|Win32
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Debug|x64.ActiveCfg = Debug|x64
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Debug|x64.Build.0 = Debug|x64
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Release|ARM.ActiveCfg = Release|Win32
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Release|ARM64.ActiveCfg = Release|Win32
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Release|Win32.ActiveCfg = Release|Win32
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Release|Win32.Build.0 = Release|Win32
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Release|x64.ActiveCfg = Release|x64
{2C3C45EC-A621-4845-AAEB-9CB82A6D32B4}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+4 -2
View File
@@ -1364,6 +1364,7 @@ badinput:
//
static boolean serverloading = false;
static boolean serverloadingstealth = false;
static void Got_NetVar(UINT8 **p, INT32 playernum)
{
@@ -1391,7 +1392,7 @@ static void Got_NetVar(UINT8 **p, INT32 playernum)
cvar = CV_FindNetVar(netid);
svalue = (char *)*p;
SKIPSTRING(*p);
stealth = READUINT8(*p);
stealth = READUINT8(*p) || (serverloading && serverloadingstealth);
if (!cvar)
{
@@ -1448,13 +1449,14 @@ void CV_SaveNetVars(UINT8 **p, boolean isdemorecording)
WRITEUINT16(count_p, count);
}
void CV_LoadNetVars(UINT8 **p)
void CV_LoadNetVars(UINT8 **p, boolean onlyIfChanged)
{
consvar_t *cvar;
UINT16 count;
// prevent "invalid command received"
serverloading = true;
serverloadingstealth = onlyIfChanged;
for (cvar = consvar_vars; cvar; cvar = cvar->next)
if (cvar->flags & CV_NETVAR)
+1 -1
View File
@@ -165,7 +165,7 @@ void CV_SaveVariables(FILE *f);
// load/save gamesate (load and save option and for network join in game)
void CV_SaveNetVars(UINT8 **p, boolean isdemorecording);
void CV_LoadNetVars(UINT8 **p);
void CV_LoadNetVars(UINT8 **p, boolean onlyIfChanged);
// reset cheat netvars after cheats is deactivated
void CV_ResetCheatNetVars(void);
+2 -1
View File
@@ -49,6 +49,7 @@ static boolean con_started = false; // console has been initialised
boolean con_startup = false; // true at game startup, screen need refreshing
static boolean con_forcepic = true; // at startup toggle console translucency when first off
boolean con_recalc; // set true when screen size has changed
boolean con_muted = false;
static tic_t con_tick; // console ticker for anim or blinking prompt cursor
// con_scrollup should use time (currenttime - lasttime)..
@@ -1163,7 +1164,7 @@ static void CON_Print(char *msg)
INT32 controlchars = 0; // for color changing
char color = '\x80'; // keep color across lines
if (msg == NULL)
if (msg == NULL || con_muted)
return;
if (*msg == '\3') // chat text, makes ding sound
+3
View File
@@ -26,6 +26,9 @@ extern boolean con_recalc;
extern boolean con_startup;
// no messages will be printed while the console is muted
extern boolean con_muted;
// top clip value for view render: do not draw part of view hidden by console
extern INT32 con_clipviewtop;
+777 -19
View File
@@ -82,6 +82,7 @@ boolean nodownload = false;
static boolean serverrunning = false;
INT32 serverplayer = 0;
char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)
char netDebugText[10000];
// Server specific vars
UINT8 playernode[MAXPLAYERS];
@@ -144,6 +145,27 @@ char connectedservername[MAXSERVERNAME];
/// \todo WORK!
boolean acceptnewnode = true;
// Net simulation stuff
savestate_t gameStateBuffer[BACKUPTICS];
boolean gameStateBufferIsValid[BACKUPTICS];
ticcmd_t gameTicBuffer[BACKUPTICS][MAXPLAYERS];
ticcmd_t localTicBuffer[BACKUPTICS];
boolean issimulation = false;
boolean rewindingWow = false;
int rewindingTarget = 0;
steadyplayer_t steadyplayers[MAXPLAYERS];
void EncodeTiccmdTime(ticcmd_t* ticcmd, tic_t time);
tic_t DecodeTiccmdTime(const ticcmd_t* ticcmd);
static void AdjustSimulatedTiccmdInputs(ticcmd_t* cmds);
static void RunSimulations();
// engine
// Must be a power of two
@@ -1711,7 +1733,7 @@ static void CL_LoadReceivedSavegame(void)
automapactive = false;
// load a base level
if (P_LoadNetGame())
if (P_LoadNetGame(false))
{
CON_LogMessage(va(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)));
if (strlen(mapheaderinfo[gamemap-1]->lvlttl) > 0)
@@ -3357,6 +3379,8 @@ static inline void SV_GenContext(void)
}
}
void SOCK_FlushDelayBuffers(boolean);
//
// D_QuitNetGame
// Called before quitting to leave a net game
@@ -3391,6 +3415,9 @@ void D_QuitNetGame(void)
HSendPacket(servernode, true, 0, 0);
}
// don't wait
SOCK_FlushDelayBuffers(true);
D_CloseConnection();
ClearAdminPlayers();
@@ -4125,7 +4152,7 @@ static void HandlePacketFromAwayNode(SINT8 node)
scp = netbuffer->u.servercfg.varlengthinputs;
CV_LoadPlayerNames(&scp);
CV_LoadNetVars(&scp);
CV_LoadNetVars(&scp, false);
#ifdef JOININGAME
/// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
/// Shouldn't them be downloaded even at intermission time?
@@ -4778,16 +4805,11 @@ static INT16 Consistancy(void)
ret += P_GetRandSeed();
#ifdef MOBJCONSISTANCY
if (!thinkercap.next)
{
DEBFILE(va("Consistancy = %u\n", ret));
return ret;
}
if (gamestate == GS_LEVEL)
{
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)th;
@@ -4891,6 +4913,7 @@ static INT16 Consistancy(void)
ret -= mo->sprite;
ret += mo->frame;
}
}
}
#endif
@@ -4935,8 +4958,11 @@ static void CL_SendClientCmd(void)
}
else if (gamestate != GS_NULL)
{
ticcmd_t adjustedCmd = localcmds;
packetsize = sizeof (clientcmd_pak);
G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
//AdjustSimulatedTiccmdInputs(&adjustedCmd); // adjust ticcmds for simulations
G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &adjustedCmd, 1);
netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
if (splitscreen || botingame) // Send a special packet with 2 cmd for splitscreen
@@ -5123,6 +5149,188 @@ static void SV_SendTics(void)
supposedtics[0] = maketic;
}
void EncodeTiccmdTime(ticcmd_t* ticcmd, tic_t time)
{
#ifdef ENCODE_TICCMD_TIMES
ticcmd->aiming = (ticcmd->aiming & ~TICCMD_TIMEMASK_AIMING) | (time & TICCMD_TIMEMASK_AIMING);
ticcmd->angleturn = (ticcmd->angleturn & ~(TICCMD_TIMEMASK_ANGLE<<1)) | (((time>>TICCMD_TIMEBITS_AIMING) & TICCMD_TIMEMASK_ANGLE) << 1); // <<1 due to TICCMD_RECEIVED :/
#endif
}
tic_t DecodeTiccmdTime(const ticcmd_t* ticcmd)
{
#ifdef ENCODE_TICCMD_TIMES
return (ticcmd->aiming & TICCMD_TIMEMASK_AIMING) + (((ticcmd->angleturn & (TICCMD_TIMEMASK_ANGLE<<1)) >> 1) << TICCMD_TIMEBITS_AIMING);
#else
return 0;
#endif
}
angle_t FixedAngleBetween(fixed_t srcX, fixed_t srcY, fixed_t destX, fixed_t destY)
{
return atan2(FIXED_TO_FLOAT(srcY - destY), FIXED_TO_FLOAT(srcX - destX)) * 0xFFFFFFFF / (2.0f*3.14159f) + 0x80000000;
}
fixed_t FixedLength(fixed_t x, fixed_t y, fixed_t z)
{
return FLOAT_TO_FIXED(sqrt(FIXED_TO_FLOAT(x) * FIXED_TO_FLOAT(x) + FIXED_TO_FLOAT(y) * FIXED_TO_FLOAT(y) + FIXED_TO_FLOAT(z) * FIXED_TO_FLOAT(z)));
}
fixed_t FixedLength2(fixed_t x, fixed_t y)
{
return FLOAT_TO_FIXED(sqrt(FIXED_TO_FLOAT(x) * FIXED_TO_FLOAT(x) + FIXED_TO_FLOAT(y) * FIXED_TO_FLOAT(y)));
}
fixed_t FixedDistance(fixed_t aX, fixed_t aY, fixed_t aZ, fixed_t bX, fixed_t bY, fixed_t bZ)
{
return FixedLength(bX - aX, bY - aY, bZ - aZ);
}
fixed_t FixedDistance2(fixed_t aX, fixed_t aY, fixed_t bX, fixed_t bY)
{
return FixedLength2(bX - aX, bY - aY);
}
static ticcmd_t lastCmds;
//Kart doesnt have FPS shootouts, so this is unneeded
/*
void CorrectPlayerTargeting(ticcmd_t* cmds)
{
if (!players[consoleplayer].mo || !cv_netsteadyplayers.value || simtic == gametic)
return;
boolean hasPressedAttack = (cmds->buttons & BT_ATTACK) && (!(lastCmds.buttons & BT_ATTACK)
|| (players[consoleplayer].currentweapon == WEP_AUTO && players[consoleplayer].rings > 0));
if (hasPressedAttack)
{
// find the most likely targeted player by measuring the angle between them
steadyplayer_t* mostLikelyPlayer = NULL;
INT16 smallestDifference = 0x8000;
INT16 smallestVDifference = 0x8000;
mobj_t* myself = players[consoleplayer].mo;
int gameHist = max((int)(simtic-gametic) - cv_netsteadyplayers.value, 0); // current hist time that the players have been positioned at on the screen
int simHist = simtic - gametic; // max simulated hist time
for (int i = 0; i < MAXPLAYERS; i++)
{
steadyplayer_t* player = &steadyplayers[i];
if (playeringame[i] && players[i].mo && i != consoleplayer)
{
fixed_t curX = player->histx[gameHist], curY = player->histy[gameHist], curZ = player->histz[gameHist];
INT16 difference = (INT16)(FixedAngleBetween(myself->x, myself->y, curX, curY)>>FRACBITS) - cmds->angleturn;
INT16 vDifference = -(INT16)(FixedAngleBetween(0, curZ, FixedDistance2(curX, curY, myself->x, myself->y), myself->z)>>FRACBITS) - cmds->aiming;
if (abs(difference) + abs(vDifference) < abs(smallestDifference) + abs(smallestVDifference))
{
smallestDifference = difference;
smallestVDifference = vDifference;
mostLikelyPlayer = &steadyplayers[i];
}
}
}
if (mostLikelyPlayer != NULL)
{
fixed_t curX = mostLikelyPlayer->histx[gameHist], curY = mostLikelyPlayer->histy[gameHist], curZ = mostLikelyPlayer->histz[gameHist];
fixed_t simX = mostLikelyPlayer->histx[simHist], simY = mostLikelyPlayer->histy[simHist], simZ = mostLikelyPlayer->histz[simHist];
if (players[consoleplayer].currentweapon == WEP_RAIL)
{
// rail rings are simple: offset the aim towards the player's new position compared to their old one
// distanceRatio increases/reduces the player's accuracy based on the distance change (e.g. big close Sonic to little faraway Sonic)
fixed_t distanceRatio = FixedDiv(FixedDistance(myself->x, myself->y, myself->z, simX, simY, simZ), FixedDistance(myself->x, myself->y, myself->z, curX, curY, curZ));
cmds->angleturn = (INT16)(FixedAngleBetween(myself->x, myself->y, simX, simY) >> FRACBITS) - FixedDiv(smallestDifference, distanceRatio);
cmds->aiming = -(INT16)(FixedAngleBetween(0, simZ, FixedDistance2(simX, simY, myself->x, myself->y), myself->z) >> FRACBITS) -
FixedDiv(smallestVDifference, distanceRatio);
}
else
{
// other rings are a bit more complex, we want to guess where the enemy will be and use that angle offset instead.
// calculate the player's new position and try to aim towards that
fixed_t ringSpeed = mobjinfo[MT_REDRING].speed;
fixed_t ringRadius = -(60<<FRACBITS) * (simtic - gametic); // if the ring was fired in any direction, this is the distance it would travel currently
fixed_t enemyX = curX, enemyY = curY, enemyZ = curZ;
tic_t wowtic = min(gametic, simtic - cv_netsteadyplayers.value);
fixed_t originalDistance = FixedDistance(myself->x, myself->y, myself->z, enemyX, enemyY, enemyZ);
fixed_t currentDistance = originalDistance;
INT16 difference = 0;
INT16 vDifference = 0;
while (ringRadius < currentDistance)
{
// move the enemy
if (wowtic < simtic)
{
enemyX = mostLikelyPlayer->histx[wowtic - gametic]; // we have a simulation for this!
enemyY = mostLikelyPlayer->histy[wowtic - gametic];
enemyZ = mostLikelyPlayer->histz[wowtic - gametic];
}
else
{
// we ran out of simulations so just extrapolate the player's previous position
enemyX += mostLikelyPlayer->histx[simtic - gametic] - mostLikelyPlayer->histx[simtic - gametic - 1];
enemyY += mostLikelyPlayer->histy[simtic - gametic] - mostLikelyPlayer->histy[simtic - gametic - 1];
enemyZ += mostLikelyPlayer->histz[simtic - gametic] - mostLikelyPlayer->histz[simtic - gametic - 1];
}
currentDistance = FixedDistance(myself->x, myself->y, myself->z, enemyX, enemyY, enemyZ);
// if we had no lag, would the ring have reached the enemy by now?
if (ringRadius + (60<<FRACBITS)*(int)(simtic - gametic) >= currentDistance && difference == 0 && vDifference == 0)
{
// capture the player's current angle offset/accuracy here
difference = (INT16)(FixedAngleBetween(myself->x, myself->y, enemyX, enemyY) >> FRACBITS) - cmds->angleturn;
vDifference = -(INT16)(FixedAngleBetween(0, enemyZ,
FixedDistance2(enemyX, enemyY, myself->x, myself->y), myself->z) >> FRACBITS) - cmds->aiming;
}
ringRadius += ringSpeed;
wowtic++;
}
// aim towards it I guess lol
fixed_t distanceRatio = FixedDiv(currentDistance, originalDistance);
cmds->angleturn = (INT16)(FixedAngleBetween(myself->x, myself->y, enemyX, enemyY)>>FRACBITS) - FixedDiv(difference, distanceRatio);
cmds->aiming = -(INT16)(FixedAngleBetween(0, enemyZ, FixedDistance2(enemyX, enemyY, myself->x, myself->y), myself->z) >> FRACBITS) - FixedDiv(vDifference, distanceRatio);
}
}
}
}
*/
static void AdjustSimulatedTiccmdInputs(ticcmd_t* cmds)
{
if (server || simtic == gametic)
return;
INT16 oldAngle = cmds->angleturn;
//Kart doesnt have match mode where this is relevant
//if (gamestate == GS_LEVEL && cv_netsteadyplayers.value && !cv_netslingdelay.value)
// CorrectPlayerTargeting(cmds);
if (cmds->angleturn != oldAngle)
{
// If the aiming angles are different, readjust movements to go towards the player's original intended direction
angle_t difference = (cmds->angleturn - oldAngle) << 16;
char oldSidemove = cmds->sidemove, oldForwardmove = cmds->forwardmove;
cmds->sidemove = (FixedMul((fixed_t)(oldSidemove<<FRACBITS), FINECOSINE(difference>>ANGLETOFINESHIFT))
+ FixedMul((fixed_t)(oldForwardmove<<FRACBITS), FINESINE(difference>>ANGLETOFINESHIFT))) >> FRACBITS;
cmds->forwardmove = (FixedMul((fixed_t)(oldSidemove<<FRACBITS), -FINESINE(difference>>ANGLETOFINESHIFT))
+ FixedMul((fixed_t)(oldForwardmove<<FRACBITS), FINECOSINE(difference>>ANGLETOFINESHIFT))) >> FRACBITS;
cmds->sidemove = min(max(cmds->sidemove, -50), 50);
cmds->forwardmove = min(max(cmds->forwardmove, -50), 50);
}
lastCmds = *cmds;
}
//
// TryRunTics
//
@@ -5146,6 +5354,8 @@ static void Local_Maketic(INT32 realtics)
}
}
EncodeTiccmdTime(&localcmds, I_GetTime());
localcmds.angleturn |= TICCMD_RECEIVED;
}
@@ -5213,6 +5423,41 @@ static void SV_Maketic(void)
maketic++;
}
// Overview for the new programmer and the programmer who probably isn't new, but doesn't have a clue what his code does becuase he didn't comment it ;)
// oh dayum did I just get called out by my old comment
/*
gametic is always the real tic as received from the server, plus one. the simulator does not change this!
smoothedTic is the time that we are using as a basis to cancel out jitter. this may be slightly <= gametic.
liveTic is the real time of the game from startup, used to record our commands
simTic is the gametic we've simulated to. if this == gametic, we haven't simulated any
gameStateBuffer[gametic] is the game state before 'gametic' executes
*/
tic_t liveTic;
int serverJitter;
int rttJitter;
int estimatedRTT;
int minRTT;
int maxRTT;
int maxLiveTicOffset;
int minLiveTicOffset;
int recommendedSimulateTics = 0; // simulateTics recommendation based on the last known 'stable' RTT (range <= 2). Used for avoiding spike lag future-past-teleports.
int smoothingDelay;
UINT64 saveStateBenchmark = 0;
UINT64 loadStateBenchmark = 0;
int netUpdateFudge; // our last net update fudge
tic_t lastSavestateClearedTic;
#define MAXOFFSETHISTORY 35
int ticTimeOffsetHistory[MAXOFFSETHISTORY];
void MakeNetDebugString();
void DetermineNetConditions();
static void PerformDebugRewinds();
boolean FindMatchingTics(int* liveTicOut, int* gameTicOut);
boolean CompareTiccmd(const ticcmd_t* a, const ticcmd_t* b);
void TryRunTics(tic_t realtics)
{
// the machine has lagged but it is not so bad
@@ -5234,6 +5479,10 @@ void TryRunTics(tic_t realtics)
D_MapChange(-1, 0, encoremode, false, 2, false, fromlevelselect); // finish the map change
}
// Get packets from the server
unsigned long long int frame = (I_GetTimeUs() * NEWTICRATE / 1000000);
netUpdateFudge = (I_GetTimeUs() - frame * 1000000 / NEWTICRATE) * 100 * NEWTICRATE / 1000000; // record the timefudge where the net update typically occurs
NetUpdate();
if (demo.playback)
@@ -5269,40 +5518,549 @@ void TryRunTics(tic_t realtics)
return;
}
if (neededtic > gametic)
// record the actual local controls
boolean canSimulate = (gamestate == GS_LEVEL)
&& leveltime >= TICRATE*4
&& gametic >= TICRATE
&& !countdown2 //SRB2Kart: No simulating after everyone has finished ... it breaks stuff
&& (cv_simulate.value && !server) //SRB2Kart: make it impossible to sim before the start of the race
&& !resynch_local_inprogress && gametic >= lastSavestateClearedTic + TICRATE;
boolean recordingStates = canSimulate;
if (simtic > gametic && !canSimulate)
{
// if we can't simulate anymore, we ought to reload and invalidate the savestates
if (gameStateBufferIsValid[gametic % BACKUPTICS])
P_LoadGameState(&gameStateBuffer[gametic % BACKUPTICS]);
else
CONS_Printf("Problem: game state buffer inaccessible! (simtic %d gametic %d)\n", simtic, gametic);
simtic = gametic;
CONS_Printf("Clearing savestates due to !canSim\n");
InvalidateSavestates();
}
PerformDebugRewinds();
liveTic = I_GetTime();
// record the actual local controls for this frame
for (tic_t i = 0; i < realtics; i++)
localTicBuffer[(liveTic - i) % BACKUPTICS] = localcmds;
// Run the real game as received from the server
if (neededtic > gametic && !resynch_local_inprogress)
{
if (advancedemo)
D_StartTitle();
else
// run the count * tics
{
// Load the real state if it exists
if (simtic != gametic && gameStateBufferIsValid[gametic % BACKUPTICS])
{
P_LoadGameState(&gameStateBuffer[gametic % BACKUPTICS]);
if (Consistancy() != consistancy[gametic%BACKUPTICS])
CONS_Printf("oops, consistency error, even I got that one!\n");
}
else if (simtic != gametic && !gameStateBufferIsValid[gametic % BACKUPTICS])
CONS_Printf("Problem: game state buffer inaccessible but a simulation happened!!\n");
// run the tics up to the real game tic
while (neededtic > gametic)
{
DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
targetsimtic = gametic + 1;
G_Ticker((gametic % NEWTICRATERATIO) == 0);
ExtraDataTicker();
gametic++;
simtic = gametic; // game state is reset, we wanna resimulate from here
consistancy[gametic%BACKUPTICS] = Consistancy();
if (recordingStates)
{
// store this real state
P_SaveGameState(&gameStateBuffer[gametic % BACKUPTICS]);
gameStateBufferIsValid[gametic % BACKUPTICS] = true;
// store the ticcmds used during this game tic
for (int i = 0; i < MAXPLAYERS; i++)
gameTicBuffer[gametic % BACKUPTICS][i] = netcmds[(gametic - 1) % BACKUPTICS][i];
}
// Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
break;
}
}
}
// Smooth out the game state according to the network
// \todo rather than re-loading an older state, we could just buffer the tics instead of running them above
if (cv_netsmoothing.value)
{
smoothedTic = liveTic - maxLiveTicOffset - 1;
if (gameStateBufferIsValid[(smoothedTic + BACKUPTICS) % BACKUPTICS] && smoothedTic != gametic)
P_LoadGameState(&gameStateBuffer[(smoothedTic + BACKUPTICS) % BACKUPTICS]);
simtic = smoothedTic;
}
else
smoothedTic = gametic;
// collect net condition data
DetermineNetConditions();
// Simulate the game locally
if (canSimulate)
RunSimulations();
// we're gonna need more debugs...
MakeNetDebugString();
}
UINT64 simStartTime;
UINT64 simEndTime;
static void RunSimulations()
{
if (!gameStateBufferIsValid[gametic % BACKUPTICS])
return; // do not simulate if we cannot guarantee a recovery
static int lastsimtic = 0;
int tastyFudge = 0;
// hack: don't treat duplicate tics as extra round-trip time
for (int j = 1; j < 3; j++)
{
hu_stopped = true;
if (CompareTiccmd(&gameTicBuffer[gametic % BACKUPTICS][consoleplayer], &gameTicBuffer[(gametic - j) % BACKUPTICS][consoleplayer]))
tastyFudge++;
else
break;
}
int numDesiredSimulateTics = min(recommendedSimulateTics, cv_simulatetics.value);
int nextTargetSimTic = min(min(gametic + estimatedRTT - tastyFudge, smoothedTic + numDesiredSimulateTics), simtic + MAXSIMULATIONS);
if (nextTargetSimTic >= 0)
targetsimtic = nextTargetSimTic;
int numToSimulate = targetsimtic - simtic;
if (numToSimulate > 0) // only touch objects if we're definitely going to simulate and rewind!
{
// record steadyplayers' real game position and their simulated position
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].mo && i != consoleplayer)
{
if (simtic == smoothedTic) // this is their real position
{
steadyplayers[i].histx[0] = players[i].mo->x;
steadyplayers[i].histy[0] = players[i].mo->y;
steadyplayers[i].histz[0] = players[i].mo->z;
}
else
{
P_UnsetThingPosition(players[i].mo);
// restore the players' simulated position before simulating more
players[i].mo->x = steadyplayers[i].histx[simtic - smoothedTic];
players[i].mo->y = steadyplayers[i].histy[simtic - smoothedTic];
players[i].mo->z = steadyplayers[i].histz[simtic - smoothedTic];
P_SetThingPosition(players[i].mo);
}
}
}
// and cull distant thinkers if enabled
if (cv_simulateculldistance.value > 0)
{
thinker_t* current;
fixed_t minDistance = cv_simulateculldistance.value << FRACBITS;
mobj_t* playerMos[MAXPLAYERS];
int numPlayers = 0;
int i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].mo)
{
playerMos[numPlayers++] = players[i].mo;
}
}
for (current = thlist[THINK_MOBJ].next; current != &thlist[THINK_MOBJ]; current = current->next)
{
for (i = 0; i < numPlayers; i++)
{
if (P_AproxDistance(playerMos[i]->x - ((mobj_t*)current)->x, playerMos[i]->y - ((mobj_t*)current)->y) < minDistance)
break;
}
if (i == numPlayers)
{
// cull this mobj
((mobj_t*)current)->isculled = true;
}
}
}
}
// simulate the rest o da future
issimulation = true;
con_muted = true;
simStartTime = I_GetTimeUs();
for (int i = 0; i < numToSimulate; i++)
{
// control other players (just use their previous control for now)
for (int j = 0; j < MAXPLAYERS; j++)
{
if (playeringame[j] && j != consoleplayer)
netcmds[gametic % BACKUPTICS][j] = gameTicBuffer[(min(simtic + 1, gametic) + BACKUPTICS) % BACKUPTICS][j];
}
// control the local player
if (simtic + i < gametic) // game is smoothed, take tics from the _actual_ received state
netcmds[gametic % BACKUPTICS][consoleplayer] = gameTicBuffer[(simtic + 1) % BACKUPTICS][consoleplayer];
else
netcmds[gametic % BACKUPTICS][consoleplayer] = localTicBuffer[(liveTic - estimatedRTT + i + 1 + BACKUPTICS) % BACKUPTICS];
G_Ticker(true); // tic a bunch of times lol see what happens lolol
simtic++;
// record simulated players' positions
for (int j = 0; j < MAXPLAYERS; j++)
{
if (playeringame[j] && players[j].mo && j != consoleplayer)
{
// Update steadyplayers' simulated positions
steadyplayers[j].histx[simtic - smoothedTic] = players[j].mo->x;
steadyplayers[j].histy[simtic - smoothedTic] = players[j].mo->y;
steadyplayers[j].histz[simtic - smoothedTic] = players[j].mo->z;
}
}
}
issimulation = false;
con_muted = false;
// Finalise steadyplayers
if (numToSimulate > 0)
{
int histIndex = max((int)(simtic - smoothedTic) - cv_netsteadyplayers.value, 0); // up to cv_netsteadyplayers.value behind the simulation
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].mo && i != consoleplayer && !players[i].spectator)
{
// Set the player's positions to the preferred simulation index (or perhaps just their original position)
// and store it in simx/simy/simz too for trails
P_UnsetThingPosition(players[i].mo);
players[i].mo->x = steadyplayers[i].histx[histIndex];
players[i].mo->y = steadyplayers[i].histy[histIndex];
players[i].mo->z = steadyplayers[i].histz[histIndex];
P_SetThingPosition(players[i].mo);
// fill any holes in the simulation history (due to frame skips)
for (int j = (int)max(lastsimtic + 1, (int)simtic - 5); j <= (int)simtic; j++)
{
steadyplayers[i].simx[j % BACKUPTICS] = steadyplayers[i].histx[histIndex];
steadyplayers[i].simy[j % BACKUPTICS] = steadyplayers[i].histy[histIndex];
steadyplayers[i].simz[j % BACKUPTICS] = steadyplayers[i].histz[histIndex];
}
// Generate trails
if (cv_nettrails.value)
{
int trailLifetime = cv_nettrails.value;
for (int s = 0; s < trailLifetime; s++)
{
// If there is a big discrepency between the player's current position and their last one, spawn a trail showing their movements
fixed_t prevx = steadyplayers[i].simx[(simtic - s - 1) % BACKUPTICS], prevy = steadyplayers[i].simy[(simtic - s - 1) % BACKUPTICS],
prevz = steadyplayers[i].simz[(simtic - s - 1) % BACKUPTICS];
fixed_t curx = steadyplayers[i].simx[(simtic - s) % BACKUPTICS], cury = steadyplayers[i].simy[(simtic - s) % BACKUPTICS],
curz = steadyplayers[i].simz[(simtic - s) % BACKUPTICS];
fixed_t distance = max(abs(curx - prevx), max(abs(cury - prevy), abs(curz - prevz)));
fixed_t stepDistance = 60 << FRACBITS; // distance between trail steps
fixed_t activationDistance = 60 << FRACBITS; // distance between trail steps
// If player is changing direction quickly in a net simulation, create a ghost trail
if (distance > activationDistance)
{
fixed_t numSteps = FixedDiv(distance, stepDistance) & ~FRACMASK;
fixed_t currentStep; // between 0 and 1
fixed_t step = FixedDiv(1 << FRACBITS, numSteps + 1);
for (currentStep = step; currentStep < (1 << FRACBITS); currentStep += step)
{
mobj_t* ghost = P_SpawnGhostMobj(players[i].mo);
ghost->x = prevx + FixedMul(curx - prevx, currentStep);
ghost->y = prevy + FixedMul(cury - prevy, currentStep);
ghost->z = prevz + FixedMul(curz - prevz, currentStep);
ghost->frame = (ghost->frame & FF_TRANSMASK) | ((6 - s) << FF_TRANSSHIFT);
ghost->fuse = 1;
}
}
}
}
}
}
// move steadyplayer shields and signs
if (cv_netsteadyplayers.value)
{
mobj_t* mobj;
player_t* redflagplayer = NULL;
player_t* blueflagplayer = NULL;
#define ADJUSTPOSITION(obj, player) do { \
P_UnsetThingPosition(obj); \
obj->x += steadyplayers[player].histx[histIndex] - steadyplayers[player].histx[simtic - gametic]; \
obj->y += steadyplayers[player].histy[histIndex] - steadyplayers[player].histy[simtic - gametic]; \
obj->z += steadyplayers[player].histz[histIndex] - steadyplayers[player].histz[simtic - gametic]; \
P_SetThingPosition(obj); \
} while(0)
for (int i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
{
if (players[i].gotflag & GF_REDFLAG)
redflagplayer = &players[i];
if (players[i].gotflag & GF_BLUEFLAG)
blueflagplayer = &players[i];
}
// if (players[i].followmobj)
// ADJUSTPOSITION(players[i].followmobj, i);
}
for (mobj = (mobj_t*)thlist[THINK_MOBJ].next; mobj != (mobj_t*)&thlist[THINK_MOBJ]; mobj = (mobj_t*)mobj->thinker.next)
{
// if (mobj->flags2 & MF2_SHIELD && mobj->target != NULL && mobj->target->player != NULL && mobj->target != players[consoleplayer].mo)
// ADJUSTPOSITION(mobj, mobj->target->player - players);
// else if (mobj->type == MT_BLUEFLAG && blueflagplayer)
// ADJUSTPOSITION(mobj, blueflagplayer - players);
// else if (mobj->type == MT_REDFLAG && redflagplayer)
// ADJUSTPOSITION(mobj, redflagplayer - players);
}
}
}
simEndTime = I_GetTimeUs();
lastsimtic = simtic;
rendergametic = gametic;
}
void InvalidateSavestates()
{
if (simtic > gametic)
CONS_Printf("Warning: Savestates were invalidated during a simulation!!\n");
for (int i = 0; i < MAXSIMULATIONS; i++)
gameStateBufferIsValid[i] = false;
lastSavestateClearedTic = gametic;
}
int rttBuffer[70];
int rttBufferIndex = 0;
int rttBufferMax = 70;
void DetermineNetConditions()
{
// Refresh the time offset between real time and server time
minLiveTicOffset = INT_MAX;
maxLiveTicOffset = INT_MIN;
ticTimeOffsetHistory[liveTic % MAXOFFSETHISTORY] = (int)liveTic - (int)gametic;
// Find the net jitter based on the last 20 frames
for (int i = 0; i < MAXOFFSETHISTORY; i++)
{
minLiveTicOffset = min(minLiveTicOffset, ticTimeOffsetHistory[i]);
maxLiveTicOffset = max(maxLiveTicOffset, ticTimeOffsetHistory[i]);
}
serverJitter = min(5, maxLiveTicOffset - minLiveTicOffset);
// Estimate the RTT (once used awkward tic matching stuff, now uses sneaky encoded angles)
#ifndef ENCODE_TICCMD_TIMES
if (!(gametic % 35))
{
int matchingLiveTic, matchingGameTic;
if (FindMatchingTics(&matchingLiveTic, &matchingGameTic) && matchingLiveTic - matchingGameTic < BACKUPTICS - 2)
{
estimatedRTT = matchingLiveTic - matchingGameTic;
}
}
#else
for (int j = 1; j < TICCMD_TIME_SIZE - 1; j++)
{
if (CompareTiccmd(&gameTicBuffer[gametic % BACKUPTICS][consoleplayer], &localTicBuffer[(liveTic - j + BACKUPTICS) % BACKUPTICS]))
{
estimatedRTT = j;
break;
}
}
#endif
// estimate RTT jitter
minRTT = INT_MAX;
maxRTT = INT_MIN;
rttBuffer[rttBufferIndex] = estimatedRTT;
rttBufferIndex = (rttBufferIndex + 1) % rttBufferMax;
for (int i = 0; i < rttBufferMax; i++)
{
minRTT = min(minRTT, rttBuffer[i]);
maxRTT = max(maxRTT, rttBuffer[i]);
}
rttJitter = maxRTT - minRTT;
// stable server conditions? (but not just a big lag spike?)
if (rttJitter <= 2 && maxRTT < BACKUPTICS - 1)
{
// we know roughly how much we should simulate then!
recommendedSimulateTics = maxRTT + 1;
}
}
static void PerformDebugRewinds()
{
if (rewindingWow && rewindingTarget > 0 && gameStateBufferIsValid[(gametic - rewindingTarget) % BACKUPTICS])
{
P_LoadGameState(&gameStateBuffer[(gametic - rewindingTarget) % BACKUPTICS]);
for (int i = 0; i < rewindingTarget; i++)
{
netcmds[gametic%BACKUPTICS][consoleplayer] = gameTicBuffer[(gametic - rewindingTarget + i) % BACKUPTICS][consoleplayer];
G_Ticker(true);
}
/* Ping Update except better:
We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
rewindingWow = false;
}
Why do we do that? Well, I'm a person with unfortunately sometimes unstable internet and happen to keep getting kicked very unconveniently for very short high spikes. (700+ ms)
Because my spikes are so high, the average ping is exponentially higher too (700s really add up...!) which leads me to getting kicked for a short burst of spiking.
With this change here, this doesn't happen anymore as it checks if my ping has been CONSISTENTLY bad for long enough before killing me.
if (gameStateBufferIsValid[gametic % BACKUPTICS] && cv_debugsimulaterewind.value > 0 && players[consoleplayer].mo) {
short consis = Consistancy();
for (int i = 0; i < cv_debugsimulaterewind.value; i++)
G_Ticker(true);
P_LoadGameState(&gameStateBuffer[gametic % BACKUPTICS]);
if (Consistancy() != consis)
CONS_Printf("General consistency error...\n");
}
}
extern UINT32 saveTimes[12];
extern UINT32 loadTimes[12];
void MakeNetDebugString()
{
netDebugText[0] = 0;
for (int i = min(maxRTT + 4, 16); i >= 0; i--)
{
if (simtic - (tic_t)i <= gametic) {
char missed[2] = "+";
if (DecodeTiccmdTime(&(gameTicBuffer[(simtic - i) % BACKUPTICS][consoleplayer])) !=
(DecodeTiccmdTime(&(gameTicBuffer[(simtic - i - 1) % BACKUPTICS][consoleplayer])) + 1) % TICCMD_TIME_SIZE)
missed[0] = 'X'; // missed tic
// show tics and matches
sprintf(&netDebugText[strlen(netDebugText)],
"%s srv: %02d%slcl: %02d%s\n",
missed,
DecodeTiccmdTime(&(gameTicBuffer[(simtic - i) % BACKUPTICS][consoleplayer])),
(i == gametic ? "<" : " "),
(liveTic - i) & (TICCMD_TIME_SIZE-1),
(i == estimatedRTT ? "<" : " "));
}
else {
sprintf(&netDebugText[strlen(netDebugText)],
"____ %02d_lcl: %02d%s\n",
0,
//(i == matchingGameTic ? "<" : " "),
DecodeTiccmdTime(&localTicBuffer[(liveTic - i) % BACKUPTICS]),
(i == estimatedRTT ? "<" : " "));
}
}
static tic_t lastSim = 0;
sprintf(&netDebugText[strlen(netDebugText)], "\n\nJitter: %d", serverJitter);
sprintf(&netDebugText[strlen(netDebugText)], "\nEstRTT: %d", estimatedRTT);
sprintf(&netDebugText[strlen(netDebugText)], "\nGame: %d", gametic);
sprintf(&netDebugText[strlen(netDebugText)], "\nSim: %d", simtic);
sprintf(&netDebugText[strlen(netDebugText)], "\nSim-Game: %d", simtic - gametic);
sprintf(&netDebugText[strlen(netDebugText)], "\nSimDelta: %d", simtic - lastSim);
sprintf(&netDebugText[strlen(netDebugText)], "\nLive: %d", liveTic);
sprintf(&netDebugText[strlen(netDebugText)], "\nTime save/load: %.2f/%.2f", (float)saveStateBenchmark/1000.0f, (float)loadStateBenchmark/1000.0f);
sprintf(&netDebugText[strlen(netDebugText)], "\nTotal +ms: %d", (int)((simEndTime - simStartTime + saveStateBenchmark + loadStateBenchmark) / 1000));
sprintf(&netDebugText[strlen(netDebugText)], "\nseed: %d", P_GetRandSeed());
lastSim = simtic;
unsigned int rtts[20] = { 0 };
for (int i = 0; i < rttBufferMax; i++)
{
if (rttBuffer[i] < 20)
rtts[rttBuffer[i]]++;
}
for (int i = 0; i < 20; i++)
{
if (rtts[i] > 0)
sprintf(&netDebugText[strlen(netDebugText)], "\nRTT %i: %i", i, rtts[i] * 100 / rttBufferMax);
}
}
// startTic and endTics are tics going back in time from the current liveTic
boolean FindMatchingTics(int* liveTicOut, int* gameTicOut) {
int latestGameTic = INT_MAX;
for (int i = 0; i < BACKUPTICS; i++) {
// display estimated matching frames
for (int j = 0; j < BACKUPTICS - 1; j++) {
// if this and the previous tic were different (interesting delta), and this matches some point in the tic buffer, say wow and use that towel
if (!CompareTiccmd(&localTicBuffer[(liveTic - i + BACKUPTICS) % BACKUPTICS], &localTicBuffer[(liveTic - i - 1 + BACKUPTICS) % BACKUPTICS])
&& CompareTiccmd(&localTicBuffer[(liveTic - i + BACKUPTICS) % BACKUPTICS], &gameTicBuffer[(smoothedTic - j + BACKUPTICS) % BACKUPTICS][consoleplayer])
&& CompareTiccmd(&localTicBuffer[(liveTic - i - 1 + BACKUPTICS) % BACKUPTICS], &gameTicBuffer[(smoothedTic - j - 1 + BACKUPTICS) % BACKUPTICS][consoleplayer]))
{
if (j < latestGameTic && i >= j) { // i >= j: uses >= because gameTicBuffer[gametic] is actually the controls used in the -last- tic
latestGameTic = j;
*liveTicOut = i;
*gameTicOut = j;
}
}
}
}
return (latestGameTic != INT_MAX);
}
boolean CompareTiccmd(const ticcmd_t* a, const ticcmd_t* b)
{
return a->aiming == b->aiming && (a->angleturn&~TICCMD_RECEIVED) == (b->angleturn&~TICCMD_RECEIVED) && a->buttons == b->buttons
&& a->forwardmove == b->forwardmove && a->sidemove == b->sidemove;
}
/*
Ping Update except better:
We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
*/
static INT32 pingtimeout[MAXPLAYERS];
+29
View File
@@ -38,6 +38,18 @@
// Networking and tick handling related.
#define BACKUPTICS 32
#define MAXTEXTCMD 256
// How many time bits to encode into ticcmds (aiming and angle components, respectively)
#define ENCODE_TICCMD_TIMES
#define TICCMD_TIMEBITS_AIMING 3
#define TICCMD_TIMEBITS_ANGLE 2
#define TICCMD_TIMEMASK_AIMING (~(0xFFFFFFFF<<TICCMD_TIMEBITS_AIMING))
#define TICCMD_TIMEMASK_ANGLE (~(0xFFFFFFFF<<TICCMD_TIMEBITS_ANGLE))
#define TICCMD_TIME_SIZE (1<<(TICCMD_TIMEBITS_AIMING+TICCMD_TIMEBITS_ANGLE))
// Maximum number of client-side simulations allowed. A simulation is a version of the game state extrapolated some frames ahead to cancel out network latency
#define MAXSIMULATIONS (TICCMD_TIME_SIZE-1)
//
// Packet structure
//
@@ -521,6 +533,17 @@ typedef enum
} kickreason_t;
// Player movement histories for simulated gamestates
typedef struct
{
// stores historical simulated positions where 0 is the real game position and simtic-gametic is the latest simulated position
fixed_t histx[MAXSIMULATIONS + 1], histy[MAXSIMULATIONS + 1], histz[MAXSIMULATIONS + 1];
// stores the final simulated position for each simulated gametic
fixed_t simx[BACKUPTICS], simy[BACKUPTICS], simz[BACKUPTICS];
} steadyplayer_t;
extern boolean server;
#define client (!server)
extern boolean dedicated; // For dedicated server
@@ -529,6 +552,9 @@ extern boolean acceptnewnode;
extern SINT8 servernode;
extern char connectedservername[MAXSERVERNAME];
extern boolean issimulation; // whether the currently executed tic is part of a simulated gamestate
extern steadyplayer_t steadyplayers[MAXPLAYERS];
void Command_Ping_f(void);
extern tic_t connectiontimeout;
extern tic_t jointimeout;
@@ -580,6 +606,9 @@ void D_QuitNetGame(void);
//? How many ticks to run?
void TryRunTics(tic_t realtic);
// Invalidates save states used in simulations
void InvalidateSavestates();
// extra data for lmps
// these functions scare me. they contain magic.
/*boolean AddLmpExtradata(UINT8 **demo_p, INT32 playernum);
+23
View File
@@ -153,6 +153,8 @@ char srb2path[256] = ".";
boolean usehome = true;
const char *pandf = "%s" PATHSEP "%s";
extern char netDebugText[10000];
//
// EVENT HANDLING
//
@@ -591,6 +593,27 @@ static void D_Display(void)
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
}
if (cv_netsimstat.value && netDebugText[0] != 0)
{
const char* str = netDebugText;
int y = 0;
while (str != NULL)
{
char temp[1024];
const char* nextStr = strstr(str + 1, "\n");
int len = nextStr ? nextStr - str : strlen(str);
memcpy(temp, str, len);
temp[len] = 0;
V_DrawRightAlignedSmallString(BASEVIDWIDTH, y, V_YELLOWMAP, temp);
y += 5;
str = nextStr ? nextStr + 1 : NULL;
}
}
if (cv_shittyscreen.value)
V_DrawVhsEffect(cv_shittyscreen.value == 2);
+3
View File
@@ -1452,6 +1452,9 @@ void D_CloseConnection(void)
for (i = 0; i < MAXNETNODES; i++)
Net_CloseConnection(i|FORCECLOSE);
// don't delay this!
SOCK_FlushDelayBuffers(true);
InitAck();
if (I_NetCloseSocket)
+239 -13
View File
@@ -116,6 +116,11 @@ static void KartSpeed_OnChange(void);
static void KartEncore_OnChange(void);
static void KartComeback_OnChange(void);
static void KartEliminateLast_OnChange(void);
static void Command_Savestate(void);
static void Command_Loadstate(void);
static void Command_Rewind(void);
static void Command_Saveloadtest(void);
static void Command_Autotimefudge(void);
#ifdef NETGAME_DEVMODE
static void Fishcake_OnChange(void);
@@ -432,6 +437,7 @@ consvar_t cv_allowexitlevel = {"allowexitlevel", "No", CV_NETVAR, CV_YesNo, NULL
consvar_t cv_killingdead = {"killingdead", "Off", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_netsimstat = { "netsimstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
consvar_t cv_netstat = {"netstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics
static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_nettimeout = {"nettimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
@@ -468,6 +474,44 @@ boolean forceresetplayers = false;
boolean deferencoremode = false;
UINT8 splitscreen = 0;
boolean circuitmap = true; // SRB2kart
consvar_t cv_simulate = { "simulate", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t simulateTics_cons_t[] = { {0, "MIN"}, {MAXSIMULATIONS - 1, "MAX"}, {0, NULL} };
consvar_t cv_simulatetics = { "simtics", "MAX", 0, simulateTics_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t simulateculldistance_cons_t[] = { {0, "MIN"}, {10000, "MAX"}, {0, NULL} };
consvar_t cv_simulateculldistance = { "simcull", "MIN", 0, simulateculldistance_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t netsteadyplayers_cons_t[] = { {0, "MIN"}, {MAXSIMULATIONS - 1, "MAX"}, {0, NULL} };
consvar_t cv_netsteadyplayers = { "simsteadyplayers", "0", 0, netsteadyplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t nettrails_cons_t[] = { {0, "MIN"}, {5, "MAX"}, {0, NULL} };
consvar_t cv_nettrails = { "simtrails", "5", 0, nettrails_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
consvar_t cv_netslingdelay = { "simslingdelay", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t netdelay_cons_t[] = { {0, "MIN"}, {250, "MAX"}, {0, NULL} };
consvar_t cv_netdelay = { "netdelay", "0", 0, netdelay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t netjitter_cons_t[] = { {0, "MIN"}, {5, "MAX"}, {0, NULL} };
consvar_t cv_netjitter = { "netjitter", "0", 0, netdelay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
consvar_t cv_netsmoothing = { "netsmoothing", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
consvar_t cv_netspikes = { "netspikes", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t debugsimulaterewind_cons_t[] = { {0, "MIN"}, {BACKUPTICS - 1, "MAX"}, {0, NULL} };
consvar_t cv_debugsimulaterewind = { "debugsimulaterewind", "0", 0, debugsimulaterewind_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t timefudge_cons_t[] = { {0, "MIN"}, {100, "MAX"}, {0, NULL} };
consvar_t cv_timefudge = { "timefudge", "0", 0, timefudge_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
char timedemo_name[256];
boolean timedemo_csv;
char timedemo_csv_id[256];
boolean timedemo_quit;
INT32 adminplayers[MAXPLAYERS];
/// \warning Keep this up-to-date if you add/remove/rename net text commands
@@ -611,6 +655,26 @@ void D_RegisterServerCommands(void)
#endif
#endif
// experimental load/save game state for possible rewinding in netgames
COM_AddCommand("savestate", Command_Savestate);
COM_AddCommand("loadstate", Command_Loadstate);
COM_AddCommand("rewind", Command_Rewind);
COM_AddCommand("saveloadtest", Command_Saveloadtest);
COM_AddCommand("autotimefudge", Command_Autotimefudge);
CV_RegisterVar(&cv_simulate);
CV_RegisterVar(&cv_simulatetics);
CV_RegisterVar(&cv_simulateculldistance);
CV_RegisterVar(&cv_netdelay);
CV_RegisterVar(&cv_netjitter);
CV_RegisterVar(&cv_netsmoothing);
CV_RegisterVar(&cv_netspikes);
CV_RegisterVar(&cv_netsteadyplayers);
CV_RegisterVar(&cv_debugsimulaterewind);
CV_RegisterVar(&cv_timefudge);
CV_RegisterVar(&cv_nettrails);
CV_RegisterVar(&cv_netslingdelay);
// for master server connection
AddMServCommands();
@@ -712,6 +776,151 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_dummyconsvar);
}
// =========================================================================
// Louis' nasty test bed
// =========================================================================
#include "p_saveg.h"
UINT8* savedState = NULL;
static void Command_Savestate(void)
{
CONS_Printf("Saving game state!\n");
savedState = (UINT8 *)malloc(1024*768 /* lol, 1998 screen resolution */);
if (!savedState)
{
CONS_Alert(CONS_ERROR, M_GetText("Wtf this machine is weak\n"));
return;
}
// Leave room for the uncompressed length.
save_p = savedState;
P_SaveNetGame();
}
static void Command_Loadstate(void)
{
save_p = savedState;
P_LoadNetGame(true);
// try and execute 10 ticcmds...
CONS_Printf("Loading game state!\n");
}
extern boolean rewindingWow;
extern int rewindingTarget;
extern boolean memleak;
static void Command_Rewind(void)
{
rewindingWow = true;
rewindingTarget = COM_Argc() > 1 ? atoi(COM_Argv(1)) : 0;
}
void MemShow() {
Z_CheckHeap(-1);
CONS_Printf("\x82%s", M_GetText("Memory Info\n"));
CONS_Printf(M_GetText("Total heap used : %7s KB\n"), sizeu1(Z_TagsUsage(0, INT32_MAX)>>10));
CONS_Printf(M_GetText("Static : %7s KB\n"), sizeu1(Z_TagUsage(PU_STATIC) >> 10));
CONS_Printf(M_GetText("Static (sound) : %7s KB\n"), sizeu1(Z_TagUsage(PU_SOUND) >> 10));
CONS_Printf(M_GetText("Static (music) : %7s KB\n"), sizeu1(Z_TagUsage(PU_MUSIC) >> 10));
CONS_Printf(M_GetText("Locked cache : %7s KB\n"), sizeu1(Z_TagUsage(PU_CACHE) >> 10));
CONS_Printf(M_GetText("Level : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVEL) >> 10));
CONS_Printf(M_GetText("Special thinker : %7s KB\n"), sizeu1(Z_TagUsage(PU_LEVSPEC) >> 10));
CONS_Printf(M_GetText("All purgable : %7s KB\n"),
sizeu1(Z_TagsUsage(PU_PURGELEVEL, INT32_MAX) >> 10));
}
static savestate_t save;
static void Command_Saveloadtest(void)
{
memleak = true;
MemShow();
for (int i = 0; i < 2; i++) {
P_SaveGameState(&save);
P_LoadGameState(&save);
}
MemShow();
memleak = false;
}
#include "i_net.h"
extern int netUpdateFudge;
static void Command_Autotimefudge(void)
{
UINT64 startTime = I_GetTimeUs();
static UINT64 packetTimeFudge[512];
int numReceivedPackets = 0;
int numSampleTics = 14;
int i;
if (server)
{
if (netgame)
{
CONS_Printf("Servers do not need a time fudge! Heck, why are you hosting with this exe anyway? You don't need to ;)\n");
}
return;
}
// New experimental version! Run a precise while loop that picks up packets instantly
while (I_GetTimeUs() - startTime < ((UINT64)numSampleTics * 1000000 / NEWTICRATE))
{
I_NetGet();
if (doomcom->remotenode != -1 && I_GetTimeUs() - startTime > (unsigned long long)2*1000000/NEWTICRATE) // wait a couple frames before recording
{
unsigned long long frame = I_GetTimeUs() * NEWTICRATE / 1000000;
packetTimeFudge[numReceivedPackets++] = (I_GetTimeUs() - frame * 1000000 / NEWTICRATE) * 100 * NEWTICRATE / 1000000
- netUpdateFudge; // gets the time fudge offset (0-100)
}
}
if (numReceivedPackets > 0)
{
int minOffset = 100, maxOffset = 0, averageOffset = 0;
int newTimeFudge;
int estimatedRange;
for (i = 0; i < numReceivedPackets; i++)
{
minOffset = min(minOffset, packetTimeFudge[i]);
maxOffset = max(maxOffset, packetTimeFudge[i]);
}
if (maxOffset - minOffset > 50)
{
// say maxOffset = 90 and minOffset = 20, we want the average to be 30...
maxOffset = maxOffset - 100;
if (minOffset > maxOffset)
{
int swap = maxOffset;
maxOffset = minOffset;
minOffset = swap;
}
}
estimatedRange = maxOffset - minOffset;
averageOffset = (maxOffset + minOffset) / 2;
CONS_Printf("%i packets, min: %d max: %d avg: %d est. range: %d (mynetupdate: %i)\n", numReceivedPackets, minOffset, maxOffset, averageOffset,
estimatedRange, netUpdateFudge);
newTimeFudge = (cv_timefudge.value + averageOffset + 50) % 100;
CONS_Printf("New time fudge: %i%%\n", newTimeFudge);
CV_SetValue(&cv_timefudge, newTimeFudge);
}
}
// =========================================================================
// CLIENT STARTUP
// =========================================================================
@@ -814,6 +1023,7 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_rollingdemos);
CV_RegisterVar(&cv_netstat);
CV_RegisterVar(&cv_netticbuffer);
CV_RegisterVar(&cv_netsimstat);
#ifdef NETGAME_DEVMODE
CV_RegisterVar(&cv_fishcake);
@@ -4675,6 +4885,8 @@ void ItemFinder_OnChange(void)
* \sa cv_pointlimit, TimeLimit_OnChange
* \author Graue <graue@oceanbase.org>
*/
static int lastpointlimit;
static void PointLimit_OnChange(void)
{
// Don't allow pointlimit in Single Player/Co-Op/Race!
@@ -4685,15 +4897,20 @@ static void PointLimit_OnChange(void)
return;
}
if (cv_pointlimit.value)
if (cv_pointlimit.value != lastpointlimit)
{
CONS_Printf(M_GetText("Levels will end after %s scores %d point%s.\n"),
G_GametypeHasTeams() ? M_GetText("a team") : M_GetText("someone"),
cv_pointlimit.value,
cv_pointlimit.value > 1 ? "s" : "");
if (cv_pointlimit.value)
{
CONS_Printf(M_GetText("Levels will end after %s scores %d point%s.\n"),
G_GametypeHasTeams() ? M_GetText("a team") : M_GetText("someone"),
cv_pointlimit.value,
cv_pointlimit.value > 1 ? "s" : "");
}
else if (netgame || multiplayer)
CONS_Printf(M_GetText("Point limit disabled\n"));
}
else if (netgame || multiplayer)
CONS_Printf(M_GetText("Point limit disabled\n"));
lastpointlimit = cv_pointlimit.value;
}
static void NumLaps_OnChange(void)
@@ -4742,19 +4959,28 @@ static void TimeLimit_OnChange(void)
if (cv_timelimit.value != 0)
{
CONS_Printf(M_GetText("Levels will end after %d minute%s.\n"),cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003
timelimitintics = cv_timelimit.value * 60 * TICRATE;
UINT32 newtimelimit = cv_timelimit.value * 60 * TICRATE;
//add hidetime for tag too!
if (G_TagGametype())
timelimitintics += hidetime * TICRATE;
newtimelimit += hidetime * TICRATE;
if (newtimelimit != timelimitintics)
{
CONS_Printf(M_GetText("Levels will end after %d minute%s.\n"), cv_timelimit.value, cv_timelimit.value == 1 ? "" : "s"); // Graue 11-17-2003
timelimitintics = newtimelimit; // ...and slightly modified by LX 12-11-2019 <3
}
// Note the deliberate absence of any code preventing
// pointlimit and timelimit from being set simultaneously.
// Some people might like to use them together. It works.
}
else if (netgame || multiplayer)
else if ((netgame || multiplayer) && timelimitintics > 0)
{
timelimitintics = 0;
CONS_Printf(M_GetText("Time limit disabled\n"));
}
}
/** Adjusts certain settings to match a changed gametype.
@@ -5376,8 +5602,8 @@ static void Command_Archivetest_f(void)
// assign mobjnum
i = 1;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
if (th->function.acp1 != (actionf_p1)P_RemoveThinkerDelayed)
((mobj_t *)th)->mobjnum = i++;
// allocate buffer
+14
View File
@@ -83,6 +83,7 @@ extern consvar_t cv_teamscramble;
extern consvar_t cv_scrambleonchange;
extern consvar_t cv_netstat;
extern consvar_t cv_netsimstat;
#ifdef WALLSPLATS
extern consvar_t cv_splats;
#endif
@@ -147,6 +148,19 @@ extern consvar_t cv_maxping;
extern consvar_t cv_pingtimeout;
extern consvar_t cv_showping;
extern consvar_t cv_simulate;
extern consvar_t cv_simulatetics;
extern consvar_t cv_simulateculldistance;
extern consvar_t cv_netdelay;
extern consvar_t cv_netjitter;
extern consvar_t cv_netsmoothing;
extern consvar_t cv_netspikes;
extern consvar_t cv_netsteadyplayers;
extern consvar_t cv_debugsimulaterewind;
extern consvar_t cv_timefudge;
extern consvar_t cv_nettrails;
extern consvar_t cv_netslingdelay;
extern consvar_t cv_skipmapcheck;
extern consvar_t cv_sleep;
+3
View File
@@ -500,6 +500,9 @@ extern UINT32 timesBeatenWithEmeralds;
// WAD, partly set at startup time.
extern tic_t gametic;
extern tic_t simtic; // current tic at simulation, if applicable (if no simulation happened, this=gametic)
extern tic_t targetsimtic; // the target sim tic, if applicable
extern tic_t smoothedTic; // current game tic when smoothing is enabled (\todo: could cause problems if the game objects care about gametic)
#define localgametic leveltime
// Player spawn spots.
+3
View File
@@ -1177,6 +1177,9 @@ void F_WaitingPlayersTicker(void)
void F_WaitingPlayersDrawer(void)
{
if (!waitcolormap)
F_StartWaitingPlayers();
UINT32 frame = (finalecount % 8) / 4; // The game only tics every other frame while waitingplayers
INT32 flags = V_FLIP;
const char *waittext1 = "You will join";
+13 -11
View File
@@ -116,6 +116,9 @@ INT32 consoleplayer; // player taking events and displaying
INT32 displayplayers[MAXSPLITSCREENPLAYERS]; // view being displayed
tic_t gametic;
tic_t simtic; // simulated tic
tic_t targetsimtic; // target simulated tic
tic_t smoothedTic;
tic_t levelstarttic; // gametic at level start
UINT32 totalrings; // for intermission
INT16 lastmap; // last level you were at (returning from special stages)
@@ -3096,9 +3099,9 @@ void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo)
I_Assert((oldmo != NULL) && (newmo != NULL));
// scan all thinkers
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -5542,16 +5545,15 @@ void G_ConsGhostTic(INT32 playernum)
demo_p += sizeof(angle_t); // angle, unnecessary for cons.
mobj = NULL;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mobj = (mobj_t *)th;
if (mobj->type == (mobjtype_t)type && mobj->x == x && mobj->y == y && mobj->z == z)
break;
mobj = NULL; // wasn't this one, keep searching.
}
if (mobj && mobj->health != health) // Wasn't damaged?! This is desync! Fix it!
if (th != &thlist[THINK_MOBJ] && mobj->health != health) // Wasn't damaged?! This is desync! Fix it!
{
if (demosynced)
CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n"));
@@ -7313,7 +7315,7 @@ void G_DoPlayDemo(char *defdemoname)
}
// net var data
CV_LoadNetVars(&demo_p);
CV_LoadNetVars(&demo_p, false);
// Sigh ... it's an empty demo.
if (*demo_p == DEMOMARKER)
@@ -7356,7 +7358,7 @@ void G_DoPlayDemo(char *defdemoname)
#endif
// net var data
CV_LoadNetVars(&demo_p);
CV_LoadNetVars(&demo_p, false);
// Sigh ... it's an empty demo.
if (*demo_p == DEMOMARKER)
@@ -7973,16 +7975,16 @@ void G_DoPlayMetal(void)
metalbuffer = metal_p = W_CacheLumpNum(l, PU_STATIC);
// find metal sonic
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)th;
if (mo->type == MT_METALSONIC_RACE)
break;
}
if (!mo)
if (th == &thlist[THINK_MOBJ])
{
CONS_Alert(CONS_ERROR, M_GetText("Failed to find bot entity.\n"));
Z_Free(metalbuffer);
+62 -35
View File
@@ -755,6 +755,14 @@ void HWR_WallLighting(FOutVector *wlVerts)
FSurfaceInfo Surf;
float dist_p2d, d[4], s;
if (!dynlights->mo[j])
continue;
if (P_MobjWasRemoved(dynlights->mo[j]))
{
P_SetTarget(&dynlights->mo[j], NULL);
continue;
}
// check bounding box first
if (SphereTouchBBox3D(&wlVerts[2], &wlVerts[0], &LIGHT_POS(j), DL_RADIUS(j))==false)
continue;
@@ -805,8 +813,6 @@ void HWR_WallLighting(FOutVector *wlVerts)
#ifdef DL_HIGH_QUALITY
Surf.FlatColor.s.alpha = (UINT8)((1-dist_p2d/DL_SQRRADIUS(j))*Surf.FlatColor.s.alpha);
#endif
if (!dynlights->mo[j]->state)
return;
// next state is null so fade out with alpha
if (dynlights->mo[j]->state->nextstate == S_NULL)
Surf.FlatColor.s.alpha = (UINT8)(((float)dynlights->mo[j]->tics/(float)dynlights->mo[j]->state->tics)*Surf.FlatColor.s.alpha);
@@ -837,6 +843,14 @@ void HWR_PlaneLighting(FOutVector *clVerts, int nrClipVerts)
FSurfaceInfo Surf;
float dist_p2d, s;
if (!dynlights->mo[j])
continue;
if (P_MobjWasRemoved(dynlights->mo[j]))
{
P_SetTarget(&dynlights->mo[j], NULL);
continue;
}
// BP: The kickass Optimization: check if light touch bounding box
if (SphereTouchBBox3D(&p1, &p2, &dynlights->position[j], DL_RADIUS(j))==false)
continue;
@@ -868,8 +882,6 @@ void HWR_PlaneLighting(FOutVector *clVerts, int nrClipVerts)
#ifdef DL_HIGH_QUALITY
Surf.FlatColor.s.alpha = (unsigned char)((1 - dist_p2d/DL_SQRRADIUS(j))*Surf.FlatColor.s.alpha);
#endif
if (!dynlights->mo[j]->state)
return;
// next state is null so fade out with alpha
if ((dynlights->mo[j]->state->nextstate == S_NULL))
Surf.FlatColor.s.alpha = (unsigned char)(((float)dynlights->mo[j]->tics/(float)dynlights->mo[j]->state->tics)*Surf.FlatColor.s.alpha);
@@ -1000,6 +1012,14 @@ void HWR_DrawCoronas(void)
if (!(p_lspr->type & CORONA_SPR))
continue;
if (!dynlights->mo[j])
continue;
if (P_MobjWasRemoved(dynlights->mo[j]))
{
P_SetTarget(&dynlights->mo[j], NULL);
continue;
}
transform(&cx,&cy,&cz);
// more realistique corona !
@@ -1061,7 +1081,8 @@ void HWR_DrawCoronas(void)
// --------------------------------------------------------------------------
void HWR_ResetLights(void)
{
dynlights->nb = 0;
while (dynlights->nb)
P_SetTarget(&dynlights->mo[--dynlights->nb], NULL);
}
// --------------------------------------------------------------------------
@@ -1092,24 +1113,25 @@ void HWR_DL_AddLight(gr_vissprite_t *spr, GLPatch_t *patch)
return;
#endif
if (dynlights->nb >= DL_MAX_LIGHT)
return;
// check if sprite contain dynamic light
p_lspr = t_lspr[spr->mobj->sprite];
if ((p_lspr->type&DYNLIGHT_SPR)
&& ((p_lspr->type != LIGHT_SPR) || cv_grstaticlighting.value)
&& (dynlights->nb < DL_MAX_LIGHT)
if (!(p_lspr->type & DYNLIGHT_SPR))
return;
if ((p_lspr->type != LIGHT_SPR) || cv_grstaticlighting.value)
return;
&& spr->mobj->state)
{
LIGHT_POS(dynlights->nb).x = FIXED_TO_FLOAT(spr->mobj->x);
LIGHT_POS(dynlights->nb).y = FIXED_TO_FLOAT(spr->mobj->z)+FIXED_TO_FLOAT(spr->mobj->height>>1)+p_lspr->light_yoffset;
LIGHT_POS(dynlights->nb).z = FIXED_TO_FLOAT(spr->mobj->y);
LIGHT_POS(dynlights->nb).x = FIXED_TO_FLOAT(spr->mobj->x);
LIGHT_POS(dynlights->nb).y = FIXED_TO_FLOAT(spr->mobj->z)+FIXED_TO_FLOAT(spr->mobj->height>>1)+p_lspr->light_yoffset;
LIGHT_POS(dynlights->nb).z = FIXED_TO_FLOAT(spr->mobj->y);
P_SetTarget(&dynlights->mo[dynlights->nb], spr->mobj);
P_SetTarget(&dynlights->mo[dynlights->nb], spr->mobj);
dynlights->p_lspr[dynlights->nb] = p_lspr;
dynlights->p_lspr[dynlights->nb] = p_lspr;
dynlights->nb++;
}
dynlights->nb++;
}
static GLPatch_t lightmappatch;
@@ -1263,6 +1285,14 @@ static void HWR_CheckSubsector(size_t num, fixed_t *bbox)
// if (CircleTouchBBox(&p1, &p2, &LIGHT_POS(lightnum), DL_RADIUS(lightnum))==false)
// continue;
if (!dynlights->mo[lightnum])
continue;
if (P_MobjWasRemoved(dynlights->mo[lightnum]))
{
P_SetTarget(&dynlights->mo[lightnum], NULL);
continue;
}
count = sub->numlines; // how many linedefs
line = &segs[sub->firstline]; // first line seg
while (count--)
@@ -1280,18 +1310,20 @@ static void HWR_CheckSubsector(size_t num, fixed_t *bbox)
// --------------------------------------------------------------------------
static void HWR_AddMobjLights(mobj_t *thing)
{
if (t_lspr[thing->sprite]->type & CORONA_SPR)
{
LIGHT_POS(dynlights->nb).x = FIXED_TO_FLOAT(thing->x);
LIGHT_POS(dynlights->nb).y = FIXED_TO_FLOAT(thing->z) + t_lspr[thing->sprite]->light_yoffset;
LIGHT_POS(dynlights->nb).z = FIXED_TO_FLOAT(thing->y);
if (dynlights->nb >= DL_MAX_LIGHT)
return;
if (!(t_lspr[thing->sprite]->type & CORONA_SPR))
return;
dynlights->p_lspr[dynlights->nb] = t_lspr[thing->sprite];
LIGHT_POS(dynlights->nb).x = FIXED_TO_FLOAT(thing->x);
LIGHT_POS(dynlights->nb).y = FIXED_TO_FLOAT(thing->z) + t_lspr[thing->sprite]->light_yoffset;
LIGHT_POS(dynlights->nb).z = FIXED_TO_FLOAT(thing->y);
dynlights->nb++;
if (dynlights->nb > DL_MAX_LIGHT)
dynlights->nb = DL_MAX_LIGHT;
}
P_SetTarget(&dynlights->mo[dynlights->nb], thing);
dynlights->p_lspr[dynlights->nb] = t_lspr[thing->sprite];
dynlights->nb++;
}
//Hurdler: The goal of this function is to walk through all the bsp starting
@@ -1317,12 +1349,9 @@ static void HWR_SearchLightsInMobjs(void)
//mobj_t * mobj;
// search in the list of thinkers
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
// a mobj ?
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
if (th->function.acp1 != (actionf_p1)P_RemoveThinkerDelayed)
HWR_AddMobjLights((mobj_t *)th);
}
}
#endif
@@ -1334,7 +1363,7 @@ void HWR_CreateStaticLightmaps(int bspnum)
#ifdef STATICLIGHT
CONS_Debug(DBG_RENDER, "HWR_CreateStaticLightmaps\n");
dynlights->nb = 0;
HWR_ResetLights();
// First: Searching for lights
// BP: if i was you, I will make it in create mobj since mobj can be create
@@ -1345,8 +1374,6 @@ void HWR_CreateStaticLightmaps(int bspnum)
// Second: Build all lightmap for walls covered by lights
validcount++; // to be sure
HWR_ComputeLightMapsInBSPNode(bspnum, NULL);
dynlights->nb = 0;
#else
(void)bspnum;
#endif
+4
View File
@@ -46,6 +46,10 @@ UINT32 I_GetFreeMem(UINT32 *total);
*/
tic_t I_GetTime(void);
/** \brief Gets the current time in microseconds
*/
UINT64 I_GetTimeUs(void);
/** \brief The I_Sleep function
\return void
+63 -1
View File
@@ -713,6 +713,35 @@ static boolean SOCK_CanGet(void)
#endif
#ifndef NONET
typedef struct {
boolean inUse;
UINT8* data;
int dataLength;
int time;
SOCKET_TYPE socket;
mysockaddr_t sockaddr;
int sockaddr_size;
} DelayBuffer;
#define NUMDELAYPACKETS 200
DelayBuffer delayPackets[NUMDELAYPACKETS];
tic_t nextSpikeTime = 0;
tic_t nextSpikeDuration = 0;
void SOCK_FlushDelayBuffers(boolean flushImmediate)
{
int time = I_GetTime();
for (int i = 0; i < NUMDELAYPACKETS; i++) {
if (delayPackets[i].inUse && ((time - delayPackets[i].time) * 1000 / TICRATE >= cv_netdelay.value || flushImmediate)) {
sendto(delayPackets[i].socket, (char *)delayPackets[i].data, delayPackets[i].dataLength, 0, &delayPackets[i].sockaddr.any, delayPackets[i].sockaddr_size);
delayPackets[i].inUse = false;
}
}
}
static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr)
{
socklen_t d4 = (socklen_t)sizeof(struct sockaddr_in);
@@ -720,6 +749,7 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6);
#endif
socklen_t d, da = (socklen_t)sizeof(mysockaddr_t);
tic_t time = I_GetTime();
switch (sockaddr->any.sa_family)
{
@@ -730,7 +760,39 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
default: d = da; break;
}
return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
if (cv_netspikes.value && time >= nextSpikeTime + nextSpikeDuration)
{
nextSpikeTime = time + (rand() % 35) + 1 * TICRATE;
nextSpikeDuration = (rand() % 10) + 1;
}
if (cv_netdelay.value > 0 || cv_netjitter.value > 0 || (cv_netspikes.value && time >= nextSpikeTime)) {
// add a nasty packet delay for testing!
boolean writtenPacket = false;
int addSpike = (cv_netspikes.value && time >= nextSpikeTime) ? nextSpikeDuration : 0;
for (int i = 0; i < NUMDELAYPACKETS; i++) {
if (!delayPackets[i].inUse) {
delayPackets[i].data = malloc(doomcom->datalength);
delayPackets[i].dataLength = doomcom->datalength;
delayPackets[i].socket = socket;
delayPackets[i].sockaddr = *sockaddr;
delayPackets[i].time = time + (rand() % (cv_netjitter.value + 1)) + addSpike;
delayPackets[i].sockaddr_size = d;
delayPackets[i].inUse = true;
memcpy(delayPackets[i].data, doomcom->data, doomcom->datalength);
break;
}
}
return doomcom->datalength;
}
else
{
// send it now like a normal human netcode
return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
}
}
static void SOCK_Send(void)
+3
View File
@@ -28,4 +28,7 @@ boolean I_InitTcpNetwork(void);
boolean I_InitTcpDriver(void);
void I_ShutdownTcpDriver(void);
// for debug delayed networking
void SOCK_FlushDelayBuffers(boolean);
#endif
+2
View File
@@ -54,10 +54,12 @@ static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
blockfuncerror = true;
P_SetTarget(&bnext, NULL);
return 0; // *shrugs*
}
if (!lua_isnil(gL, -1))
{ // if nil, continue
P_SetTarget(&bnext, NULL);
if (lua_toboolean(gL, -1))
return 2; // stop whole search
else
-12
View File
@@ -217,8 +217,6 @@ enum slope_e {
slope_normal,
slope_zangle,
slope_xydirection,
slope_sourceline,
slope_refpos,
slope_flags
};
@@ -230,8 +228,6 @@ static const char *const slope_opt[] = {
"normal",
"zangle",
"xydirection",
"sourceline",
"refpos",
"flags",
NULL};
@@ -1285,12 +1281,6 @@ static int slope_get(lua_State *L)
case slope_xydirection: // xydirection
lua_pushangle(L, slope->xydirection);
return 1;
case slope_sourceline: // source linedef
LUA_PushUserdata(L, slope->sourceline, META_LINE);
return 1;
case slope_refpos: // refpos
lua_pushinteger(L, slope->refpos);
return 1;
case slope_flags: // flags
lua_pushinteger(L, slope->flags);
return 1;
@@ -1314,11 +1304,9 @@ static int slope_set(lua_State *L)
switch(field) // todo: reorganize this shit
{
case slope_valid: // valid
case slope_sourceline: // sourceline
case slope_d: // d
case slope_flags: // flags
case slope_normal: // normal
case slope_refpos: // refpos
default:
return luaL_error(L, "pslope_t field " LUA_QS " cannot be set.", slope_opt[field]);
case slope_o: { // o
+10 -1
View File
@@ -260,10 +260,19 @@ static int mobj_get(lua_State *L)
// bprev -- same deal as sprev above, but for the blockmap.
return UNIMPLEMENTED;
case mobj_hnext:
if (mo->hnext && P_MobjWasRemoved(mo->hnext))
{ // don't put invalid mobj back into Lua.
P_SetTarget(&mo->hnext, NULL);
return 0;
}
LUA_PushUserdata(L, mo->hnext, META_MOBJ);
break;
case mobj_hprev:
// implimented differently from sprev and bprev because SSNTails.
if (mo->hprev && P_MobjWasRemoved(mo->hprev))
{ // don't put invalid mobj back into Lua.
P_SetTarget(&mo->hprev, NULL);
return 0;
}
LUA_PushUserdata(L, mo->hprev, META_MOBJ);
break;
case mobj_type:
+12 -2
View File
@@ -476,9 +476,19 @@ static int player_set(lua_State *L)
else if (fastcmp(field,"old_angle_pos"))
plr->old_angle_pos = luaL_checkangle(L, 3);
else if (fastcmp(field,"axis1"))
P_SetTarget(&plr->axis1, *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)));
{
mobj_t *mo = NULL;
if (!lua_isnil(L, 3))
mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
P_SetTarget(&plr->axis1, mo);
}
else if (fastcmp(field,"axis2"))
P_SetTarget(&plr->axis2, *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)));
{
mobj_t *mo = NULL;
if (!lua_isnil(L, 3))
mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
P_SetTarget(&plr->axis2, mo);
}
else if (fastcmp(field,"bumpertime"))
plr->bumpertime = (tic_t)luaL_checkinteger(L, 3);
else if (fastcmp(field,"flyangle"))
+20 -15
View File
@@ -413,9 +413,9 @@ void LUA_InvalidateLevel(void)
size_t i;
if (!gL)
return;
for (th = thinkercap.next; th && th != &thinkercap; th = th->next)
LUA_InvalidateUserdata(th);
for (i = 0; i < NUM_THINKERLISTS; i++)
for (th = thlist[i].next; th && th != &thlist[i]; th = th->next)
LUA_InvalidateUserdata(th);
LUA_InvalidateMapthings();
@@ -1026,16 +1026,17 @@ void LUA_Archive(void)
ArchiveExtVars(&players[i], "player");
}
if (gamestate == GS_LEVEL)
//if (gamestate == GS_LEVEL)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
{
// archive function will determine when to skip mobjs,
// and write mobjnum in otherwise.
ArchiveExtVars(th, "mobj");
}
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
// archive function will determine when to skip mobjs,
// and write mobjnum in otherwise.
ArchiveExtVars(th, "mobj");
}
WRITEUINT32(save_p, UINT32_MAX); // end of mobjs marker, replaces mobjnum.
LUAh_NetArchiveHook(NetArchive); // call the NetArchive hook in archive mode
@@ -1063,10 +1064,14 @@ void LUA_UnArchive(void)
do {
mobjnum = READUINT32(save_p); // read a mobjnum
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker
&& ((mobj_t *)th)->mobjnum == mobjnum) // find matching mobj
UnArchiveExtVars(th); // apply variables
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
if (((mobj_t *)th)->mobjnum != mobjnum) // find matching mobj
continue;
UnArchiveExtVars(th); // apply variables
}
} while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker.
LUAh_NetArchiveHook(NetUnArchive); // call the NetArchive hook in unarchive mode
+7 -7
View File
@@ -18,7 +18,7 @@
#define META_ITERATIONSTATE "iteration state"
static const char *const iter_opt[] = {
/*static const char *const iter_opt[] = {
"all",
"mobj",
NULL};
@@ -26,7 +26,7 @@ static const char *const iter_opt[] = {
static const actionf_p1 iter_funcs[] = {
NULL,
(actionf_p1)P_MobjThinker
};
};*/
struct iterationState {
actionf_p1 filter;
@@ -58,7 +58,7 @@ static int lib_iterateThinkers(lua_State *L)
lua_settop(L, 2);
if (lua_isnil(L, 2))
th = &thinkercap;
th = &thlist[THINK_MOBJ];
else if (lua_isuserdata(L, 2))
{
if (lua_islightuserdata(L, 2))
@@ -88,11 +88,11 @@ static int lib_iterateThinkers(lua_State *L)
if (!next)
return luaL_error(L, "next thinker invalidated during iteration");
for (; next != &thinkercap; next = next->next)
for (; next != &thlist[THINK_MOBJ]; next = next->next)
if (!it->filter || next->function.acp1 == it->filter)
{
push_thinker(next);
if (next->next != &thinkercap)
if (next->next != &thlist[THINK_MOBJ])
{
push_thinker(next->next);
it->next = luaL_ref(L, LUA_REGISTRYINDEX);
@@ -111,7 +111,7 @@ static int lib_startIterate(lua_State *L)
luaL_getmetatable(L, META_ITERATIONSTATE);
lua_setmetatable(L, -2);
it->filter = iter_funcs[luaL_checkoption(L, 1, "mobj", iter_opt)];
it->filter = (actionf_p1)P_MobjThinker; //iter_funcs[luaL_checkoption(L, 1, "mobj", iter_opt)];
it->next = LUA_REFNIL;
return 2;
}
@@ -129,7 +129,7 @@ int LUA_ThinkerLib(lua_State *L)
lua_pushcfunction(L, lib_iterateThinkers);
lua_pushcclosure(L, lib_startIterate, 1);
lua_setfield(L, -2, "iterate");
lua_setglobal(L, "thinkers");
lua_setglobal(L, "mobjs");
return 0;
}
+5 -4
View File
@@ -912,15 +912,16 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c
thinker_t *th;
mobj_t *mo;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)th;
// get offset from mt, which points to old mapthings, then add new location
if (mo->spawnpoint)
mo->spawnpoint = (mo->spawnpoint - mt) + mapthings;
if (!mo->spawnpoint)
continue;
mo->spawnpoint = (mo->spawnpoint - mt) + mapthings;
}
}
+2 -2
View File
@@ -409,7 +409,7 @@ INT32 EV_DoCeiling(line_t *line, ceiling_e type)
// new door thinker
rtn = 1;
ceiling = Z_Calloc(sizeof (*ceiling), PU_LEVSPEC, NULL);
P_AddThinker(&ceiling->thinker);
P_AddThinker(THINK_MAIN, &ceiling->thinker);
sec->ceilingdata = ceiling;
ceiling->thinker.function.acp1 = (actionf_p1)T_MoveCeiling;
ceiling->sector = sec;
@@ -629,7 +629,7 @@ INT32 EV_DoCrush(line_t *line, ceiling_e type)
// new door thinker
rtn = 1;
ceiling = Z_Calloc(sizeof (*ceiling), PU_LEVSPEC, NULL);
P_AddThinker(&ceiling->thinker);
P_AddThinker(THINK_MAIN, &ceiling->thinker);
sec->ceilingdata = ceiling;
ceiling->thinker.function.acp1 = (actionf_p1)T_CrushCeiling;
ceiling->sector = sec;
+57 -40
View File
@@ -2149,7 +2149,7 @@ void A_Boss1Laser(mobj_t *actor)
if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1)
{
point = P_SpawnMobj(x, y, floorz+1, MT_EGGMOBILE_FIRE);
point->target = actor;
P_SetTarget(&point->target, actor);
point->destscale = 3*FRACUNIT;
point->scalespeed = FRACUNIT>>2;
point->fuse = TICRATE;
@@ -2530,10 +2530,11 @@ void A_1upThinker(mobj_t *actor)
if (closestplayer == -1 || skins[players[closestplayer].skin].spritedef.numframes <= states[S_PLAY_BOX1].frame)
{ // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite.
actor->frame = 0;
if (actor->tracer) {
P_RemoveMobj(actor->tracer);
actor->tracer = NULL;
if (actor->tracer)
{
mobj_t *tracer = actor->tracer;
P_SetTarget(&actor->tracer, NULL);
P_RemoveMobj(tracer);
}
return;
}
@@ -2745,9 +2746,9 @@ void A_BossDeath(mobj_t *mo)
// scan the remaining thinkers to see
// if all bosses are dead
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -2802,12 +2803,12 @@ bossjustdie:
P_SetTarget(&mo->target, NULL);
// Flee! Flee! Find a point to escape to! If none, just shoot upward!
// scan the thinkers to find the runaway point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
// Flee! Flee! Find a point to escape to! If none, just shoot upward!
// scan the thinkers to find the runaway point
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -3768,6 +3769,14 @@ void A_ThrownRing(mobj_t *actor)
INT32 stop;
player_t *player;
fixed_t dist;
if (cv_netslingdelay.value && issimulation && (tic_t)cv_netsteadyplayers.value >= targetsimtic - simtic && actor->target == players[consoleplayer].mo)
{
// don't move ring weapons during simulation if netslingdelay is positive
actor->momx = actor->momy = actor->momz = 0;
return;
}
#ifdef HAVE_BLUA
if (LUA_CallAction("A_ThrownRing", actor))
return;
@@ -5437,9 +5446,9 @@ void A_RingExplode(mobj_t *actor)
S_StartSound(actor, sfx_prloop);
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -6538,7 +6547,7 @@ void A_Boss2PogoTarget(mobj_t *actor)
if (actor->info->missilestate) // spawn the pogo stick collision box
{
mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate);
pogo->target = actor;
P_SetTarget(&pogo->target, actor);
}
actor->reactiontime = 1;
@@ -7073,14 +7082,24 @@ void A_Boss3Path(mobj_t *actor)
// scan the thinkers
// to find a point that matches
// the number
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && mo2->spawnpoint->angle == actor->threshold)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type != MT_BOSS3WAYPOINT)
continue;
if (!mo2->spawnpoint)
continue;
if (mo2->spawnpoint->angle != actor->threshold)
continue;
if (mo2->spawnpoint->extrainfo != actor->cusval)
continue;
P_SetTarget(&actor->target, mo2);
break;
}
@@ -7463,9 +7482,9 @@ void A_FindTarget(mobj_t *actor)
CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
// scan the thinkers
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -7528,9 +7547,9 @@ void A_FindTracer(mobj_t *actor)
CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
// scan the thinkers
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -8091,9 +8110,9 @@ void A_RemoteAction(mobj_t *actor)
fixed_t dist1 = 0, dist2 = 0;
// scan the thinkers
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -8879,9 +8898,9 @@ void A_MementosTPParticles(mobj_t *actor)
// Doesn't seem like much given the small amount of mobjs this map has but heh.
if (!actor->target)
{
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -8981,9 +9000,9 @@ void A_ReaperThinker(mobj_t *actor)
}
// We have no target and oughta find one, so let's scan through thinkers for a waypoint of angle 0, or something.
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -9047,9 +9066,9 @@ void A_ReaperThinker(mobj_t *actor)
P_SetTarget(&actor->target, NULL); // remove target so we can default back to first waypoint if things go ham.
// If we reach close to a waypoint, then we should go to the NEXT one.
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -9098,8 +9117,6 @@ void A_FlameParticle(mobj_t *actor)
par->momz = actor->scale<<1;
}
//}
// Function: A_OrbitNights
//
// Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.)
@@ -9228,10 +9245,10 @@ void A_SetObjectTypeState(mobj_t *actor)
return;
#endif
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -9866,9 +9883,9 @@ void A_CheckThingCount(mobj_t *actor)
return;
#endif
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
+22 -17
View File
@@ -1974,20 +1974,25 @@ void T_ThwompSector(levelspecthink_t *thwomp)
// scan the thinkers to find players!
if (!rover || (rover->flags & FF_EXISTS))
{
// scan the thinkers to find players!
for (th = thinkercap.next; th != &thinkercap; th = th->next)
UINT8 i;
// scan the players to find victims!
for (i = 0; i < MAXPLAYERS; i++)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
if (!players[i].mo)
continue;
if (!players[i].mo->health)
continue;
if (players[i].mo->z > thwomp->sector->ceilingheight)
continue;
if (P_AproxDistance(thwompx - players[i].mo->x, thwompy - players[i].mo->y) > 96 * FRACUNIT)
continue;
mo = (mobj_t *)th;
if (mo->type == MT_PLAYER && mo->health && mo->player && !mo->player->spectator
&& mo->z <= thwomp->sector->ceilingheight
&& P_AproxDistance(thwompx - mo->x, thwompy - mo->y) <= 96*FRACUNIT)
{
thwomp->direction = -1;
break;
}
thwomp->direction = -1;
break;
}
}*/
@@ -2675,7 +2680,7 @@ INT32 EV_DoFloor(line_t *line, floor_e floortype)
// new floor thinker
rtn = 1;
dofloor = Z_Calloc(sizeof (*dofloor), PU_LEVSPEC, NULL);
P_AddThinker(&dofloor->thinker);
P_AddThinker(THINK_MAIN, &dofloor->thinker);
// make sure another floor thinker won't get started over this one
sec->floordata = dofloor;
@@ -2896,7 +2901,7 @@ INT32 EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed)
// create and initialize new elevator thinker
rtn = 1;
elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL);
P_AddThinker(&elevator->thinker);
P_AddThinker(THINK_MAIN, &elevator->thinker);
sec->floordata = elevator;
sec->ceilingdata = elevator;
elevator->thinker.function.acp1 = (actionf_p1)T_MoveElevator;
@@ -3078,7 +3083,7 @@ INT32 EV_BounceSector(sector_t *sec, fixed_t momz, line_t *sourceline)
return 0;
bouncer = Z_Calloc(sizeof (*bouncer), PU_LEVSPEC, NULL);
P_AddThinker(&bouncer->thinker);
P_AddThinker(THINK_MAIN, &bouncer->thinker);
sec->ceilingdata = bouncer;
bouncer->thinker.function.acp1 = (actionf_p1)T_BounceCheese;
@@ -3112,7 +3117,7 @@ INT32 EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, bool
// create and initialize new thinker
faller = Z_Calloc(sizeof (*faller), PU_LEVSPEC, NULL);
P_AddThinker(&faller->thinker);
P_AddThinker(THINK_MAIN, &faller->thinker);
faller->thinker.function.acp1 = (actionf_p1)T_ContinuousFalling;
// set up the fields
@@ -3161,7 +3166,7 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating,
// create and initialize new elevator thinker
elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL);
P_AddThinker(&elevator->thinker);
P_AddThinker(THINK_MAIN, &elevator->thinker);
elevator->thinker.function.acp1 = (actionf_p1)T_StartCrumble;
// Does this crumbler return?
@@ -3235,7 +3240,7 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
// create and initialize new elevator thinker
block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL);
P_AddThinker(&block->thinker);
P_AddThinker(THINK_MAIN, &block->thinker);
sec->floordata = block;
sec->ceilingdata = block;
block->thinker.function.acp1 = (actionf_p1)T_MarioBlock;
+63 -8
View File
@@ -27,6 +27,7 @@
#include "m_misc.h"
#include "v_video.h" // video flags for CEchos
#include "k_kart.h" // SRB2kart
#include "p_local.h"
// CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
@@ -923,7 +924,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (player->powers[pw_super])
return;
player->gotflag |= flagflag;
player->gotflag |= flagflag; // >:(
CONS_Printf(M_GetText("%s picked up the %c%s%c!\n"), plname, flagcolor, flagtext, 0x80);
(*flagmobj) = NULL;
// code for dealing with abilities is handled elsewhere now
@@ -948,6 +949,60 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (toucher->tracer && toucher->tracer->target
&& toucher->tracer->target->type == MT_GOTEMERALD)
{
mobj_t *orbittarget = special->target ? special->target : special;
mobj_t *hnext = orbittarget->hnext, *anchorpoint = NULL, *anchorpoint2 = NULL;
mobj_t *mo2;
thinker_t *th;
// The player might have two Ideyas: toucher->tracer and toucher->tracer->hnext
// so handle their anchorpoints accordingly.
// scan the thinkers to find the corresponding anchorpoint
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type != MT_IDEYAANCHOR)
continue;
if (mo2->health == toucher->tracer->health) // do ideya numberes match?
anchorpoint = mo2;
else if (toucher->tracer->hnext && mo2->health == toucher->tracer->hnext->health)
anchorpoint2 = mo2;
if ((!toucher->tracer->hnext && anchorpoint)
|| (toucher->tracer->hnext && anchorpoint && anchorpoint2))
break;
}
if (anchorpoint)
{
toucher->tracer->flags |= MF_GRENADEBOUNCE; // custom radius factors
toucher->tracer->threshold = 8 << 20; // X factor 0, Y factor 0, Z factor 8
}
if (anchorpoint2)
{
toucher->tracer->hnext->flags |= MF_GRENADEBOUNCE; // custom radius factors
toucher->tracer->hnext->threshold = 8 << 20; // X factor 0, Y factor 0, Z factor 8
}
P_SetTarget(&orbittarget->hnext, toucher->tracer);
if (!orbittarget->hnext->hnext)
P_SetTarget(&orbittarget->hnext->hnext, hnext); // Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo.
else
P_SetTarget(&orbittarget->hnext->hnext->target, anchorpoint2 ? anchorpoint2 : orbittarget);
P_SetTarget(&orbittarget->hnext->target, anchorpoint ? anchorpoint : orbittarget);
P_SetTarget(&toucher->tracer, NULL);
if (hnext)
{
orbittarget->hnext->extravalue1 = (angle_t)(hnext->extravalue1 - 72*ANG1);
if (orbittarget->hnext->extravalue1 > hnext->extravalue1)
orbittarget->hnext->extravalue1 -= (72*ANG1)/orbittarget->hnext->extravalue1;
}
}
else // Make sure that SOMEONE has the emerald, at least!
{
@@ -995,9 +1050,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
count = 1;
// scan the remaining thinkers
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -1045,9 +1100,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// Now we RE-scan all the thinkers to find close objects to pull
// in from the paraloop. Isn't this just so efficient?
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -1414,9 +1469,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
EV_DoElevator(&junk, bridgeFall, false);
// scan the remaining thinkers to find koopa
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -2545,7 +2600,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
// scan the thinkers to make sure all the old pinch dummies are gone on death
// this can happen if the boss was hurt earlier than expected
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
+5 -5
View File
@@ -76,7 +76,7 @@ fireflicker_t *P_SpawnAdjustableFireFlicker(sector_t *minsector, sector_t *maxse
P_RemoveLighting(maxsector); // out with the old, in with the new
flick = Z_Calloc(sizeof (*flick), PU_LEVSPEC, NULL);
P_AddThinker(&flick->thinker);
P_AddThinker(THINK_MAIN, &flick->thinker);
flick->thinker.function.acp1 = (actionf_p1)T_FireFlicker;
flick->sector = maxsector;
@@ -155,7 +155,7 @@ void P_SpawnLightningFlash(sector_t *sector)
flash = Z_Calloc(sizeof (*flash), PU_LEVSPEC, NULL);
P_AddThinker(&flash->thinker);
P_AddThinker(THINK_MAIN, &flash->thinker);
flash->thinker.function.acp1 = (actionf_p1)T_LightningFlash;
flash->sector = sector;
@@ -214,7 +214,7 @@ strobe_t *P_SpawnAdjustableStrobeFlash(sector_t *minsector, sector_t *maxsector,
P_RemoveLighting(maxsector); // out with the old, in with the new
flash = Z_Calloc(sizeof (*flash), PU_LEVSPEC, NULL);
P_AddThinker(&flash->thinker);
P_AddThinker(THINK_MAIN, &flash->thinker);
flash->sector = maxsector;
flash->darktime = darktime;
@@ -289,7 +289,7 @@ glow_t *P_SpawnAdjustableGlowingLight(sector_t *minsector, sector_t *maxsector,
P_RemoveLighting(maxsector); // out with the old, in with the new
g = Z_Calloc(sizeof (*g), PU_LEVSPEC, NULL);
P_AddThinker(&g->thinker);
P_AddThinker(THINK_MAIN, &g->thinker);
g->sector = maxsector;
g->minlight = minsector->lightlevel;
@@ -350,7 +350,7 @@ void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed)
ll->thinker.function.acp1 = (actionf_p1)T_LightFade;
sector->lightingdata = ll; // set it to the lightlevel_t
P_AddThinker(&ll->thinker); // add thinker
P_AddThinker(THINK_MAIN, &ll->thinker); // add thinker
ll->sector = sector;
ll->destlevel = destvalue;
+15 -8
View File
@@ -60,15 +60,21 @@
#define mariomode (maptol & TOL_MARIO)
#define twodlevel (maptol & TOL_2D)
//
// P_TICK
//
// both the head and tail of the thinker list
extern thinker_t thinkercap;
typedef enum
{
THINK_POLYOBJ,
THINK_MAIN,
THINK_MOBJ,
#ifdef ESLOPE
THINK_DYNSLOPE,
#endif
THINK_PRECIP,
NUM_THINKERLISTS
} thinklistnum_t; /**< Thinker lists. */
extern thinker_t thlist[];
void P_InitThinkers(void);
void P_AddThinker(thinker_t *thinker);
void P_AddThinker(const thinklistnum_t n, thinker_t *thinker);
void P_RemoveThinker(thinker_t *thinker);
//
@@ -222,7 +228,7 @@ void P_PrecipitationEffects(void);
void P_RemoveMobj(mobj_t *th);
boolean P_MobjWasRemoved(mobj_t *th);
void P_RemoveSavegameMobj(mobj_t *th);
void P_RemoveSavegameMobj(mobj_t *th, boolean preserveLevel);
boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state);
boolean P_SetMobjState(mobj_t *mobj, statenum_t state);
//void P_RunShields(void);
@@ -267,6 +273,7 @@ void P_ColorTeamMissile(mobj_t *missile, player_t *source);
SINT8 P_MobjFlip(mobj_t *mobj);
fixed_t P_GetMobjGravity(mobj_t *mo);
FUNCMATH boolean P_WeaponOrPanel(mobjtype_t type);
FUNCMATH boolean P_IsProjectile(mobjtype_t type);
boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled);
+1 -1
View File
@@ -3994,7 +3994,7 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
thinker_t *think;
elevator_t *crumbler;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)T_StartCrumble)
continue;
+3
View File
@@ -1093,7 +1093,10 @@ boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *))
{
P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
if (!func(mobj))
{
P_SetTarget(&bnext, NULL);
return false;
}
if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue.
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
{
+101 -76
View File
@@ -50,6 +50,8 @@ static mobj_t *overlaycap = NULL;
static mobj_t *shadowcap = NULL;
mobj_t *waypointcap = NULL;
UINT32 globalmobjnum = 0; // this should never overflow, but 4 billion would be an impressive number to reach. \todo ensure this never happens
void P_InitCachedActions(void)
{
actioncachehead.prev = actioncachehead.next = &actioncachehead;
@@ -395,6 +397,12 @@ boolean P_WeaponOrPanel(mobjtype_t type)
return false;
}
boolean P_IsProjectile(mobjtype_t type)
{
return type == MT_THROWNBOUNCE || type == MT_THROWNINFINITY || type == MT_THROWNAUTOMATIC || type == MT_THROWNSCATTER
|| type == MT_THROWNEXPLOSION || type == MT_THROWNGRENADE || type == MT_REDRING;
}
//
// P_EmeraldManager
//
@@ -421,10 +429,10 @@ boolean P_WeaponOrPanel(mobjtype_t type)
spawnpoints[i] = NULL;
}
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)think;
@@ -3498,10 +3506,10 @@ void P_DestroyRobots(void)
mobj_t *mo;
thinker_t *think;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)think;
if (mo->health <= 0 || !(mo->flags & MF_ENEMY || mo->flags & MF_BOSS))
@@ -4305,9 +4313,9 @@ static void P_Boss3Thinker(mobj_t *mobj)
// scan the thinkers to make sure all the old pinch dummies are gone before making new ones
// this can happen if the boss was hurt earlier than expected
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -4396,15 +4404,25 @@ static void P_Boss3Thinker(mobj_t *mobj)
// scan the thinkers
// to find a point that matches
// the number
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && mo2->spawnpoint->angle == mobj->threshold)
{
P_SetTarget(&mobj->target, mo2);
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type != MT_BOSS3WAYPOINT)
continue;
if (!mo2->spawnpoint)
continue;
if (mo2->spawnpoint->angle != mobj->threshold)
continue;
if (mo2->spawnpoint->extrainfo != mobj->cusval)
continue;
P_SetTarget(&mobj->tracer, mo2);
break;
}
}
@@ -4643,8 +4661,8 @@ static void P_Boss4Thinker(mobj_t *mobj)
P_SetTarget(&seg->target, mobj);
for (i = 0; i < 9; i++)
{
seg->hnext = P_SpawnMobj(mobj->x, mobj->y, z, MT_EGGMOBILE4_MACE);
seg->hnext->hprev = seg;
P_SetTarget(&seg->hnext, P_SpawnMobj(mobj->x, mobj->y, z, MT_EGGMOBILE4_MACE));
P_SetTarget(&seg->hnext->hprev, seg);
seg = seg->hnext;
}
}
@@ -5017,9 +5035,9 @@ static void P_Boss7Thinker(mobj_t *mobj)
closestdist = 16384*FRACUNIT; // Just in case...
// Find waypoint he is closest to
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -5072,9 +5090,9 @@ static void P_Boss7Thinker(mobj_t *mobj)
// scan the thinkers to find
// the waypoint to use
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -5195,19 +5213,19 @@ static void P_Boss9Thinker(mobj_t *mobj)
// Run through the thinkers ONCE and find all of the MT_BOSS9GATHERPOINT in the map.
// Build a hoop linked list of 'em!
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_BOSS9GATHERPOINT)
{
if (last)
last->hnext = mo2;
P_SetTarget(&last->hnext, mo2);
else
mobj->hnext = mo2;
mo2->hprev = last;
P_SetTarget(&mobj->hnext, mo2);
P_SetTarget(&mo2->hprev, last);
last = mo2;
}
}
@@ -5629,9 +5647,9 @@ mobj_t *P_GetClosestAxis(mobj_t *source)
fixed_t dist1, dist2 = 0;
// scan the thinkers to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -6159,12 +6177,14 @@ static void P_RemoveOverlay(mobj_t *thing)
{
mobj_t *mo;
for (mo = overlaycap; mo; mo = mo->hnext)
if (mo->hnext == thing)
{
P_SetTarget(&mo->hnext, thing->hnext);
P_SetTarget(&thing->hnext, NULL);
return;
}
{
if (mo->hnext != thing)
continue;
P_SetTarget(&mo->hnext, thing->hnext);
P_SetTarget(&thing->hnext, NULL);
return;
}
}
void P_RunShadows(void)
@@ -6346,6 +6366,9 @@ void P_MobjThinker(mobj_t *mobj)
if (mobj->flags & MF_NOTHINK)
return;
if ((mobj->flags & MF_BOSS) && mobj->spawnpoint)
return;
// Remove dead target/tracer.
if (mobj->target && P_MobjWasRemoved(mobj->target))
P_SetTarget(&mobj->target, NULL);
@@ -9613,6 +9636,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
mobj->type = type;
mobj->info = info;
mobj->localmobjnum = globalmobjnum++;
mobj->x = x;
mobj->y = y;
@@ -10013,7 +10037,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
}
if (!(mobj->flags & MF_NOTHINK))
P_AddThinker(&mobj->thinker);
P_AddThinker(THINK_MOBJ, &mobj->thinker);
// Call action functions when the state is set
if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC))
@@ -10124,7 +10148,7 @@ mobj_t *P_SpawnShadowMobj(mobj_t * caster)
mobj->eflags |= MFE_ONGROUND;
if (!(mobj->flags & MF_NOTHINK))
P_AddThinker(&mobj->thinker);
P_AddThinker(THINK_MOBJ, &mobj->thinker);
// Call action functions when the state is set
if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC))
@@ -10198,7 +10222,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype
mobj->momz = mobjinfo[type].speed;
mobj->thinker.function.acp1 = (actionf_p1)P_NullPrecipThinker;
P_AddThinker(&mobj->thinker);
P_AddThinker(THINK_PRECIP, &mobj->thinker);
CalculatePrecipFloor(mobj);
@@ -10329,37 +10353,28 @@ void P_RemoveMobj(mobj_t *mobj)
}
}
// free block
// DBG: set everything in mobj_t to 0xFF instead of leaving it. debug memory error.
if (mobj->flags & MF_NOTHINK && !mobj->thinker.next)
#ifdef SCRAMBLE_REMOVED
// Invalidate mobj_t data to cause crashes if accessed!
memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
#endif
// free block
if (!mobj->thinker.next)
{ // Uh-oh, the mobj doesn't think, P_RemoveThinker would never go through!
INT32 prevreferences;
if (!mobj->thinker.references)
{
#ifdef SCRAMBLE_REMOVED
// Invalidate mobj_t data to cause crashes if accessed!
memset(mobj, 0xff, sizeof(mobj_t));
#endif
Z_Free(mobj); // No refrences? Can be removed immediately! :D
}
else
{ // Add thinker just to delay removing it until refrences are gone.
mobj->flags &= ~MF_NOTHINK;
P_AddThinker((thinker_t *)mobj);
#ifdef SCRAMBLE_REMOVED
// Invalidate mobj_t data to cause crashes if accessed!
memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
#endif
P_RemoveThinker((thinker_t *)mobj);
Z_Free(mobj); // No refrrences? Can be removed immediately! :D
return;
}
prevreferences = mobj->thinker.references;
P_AddThinker(THINK_MOBJ, (thinker_t *)mobj);
mobj->thinker.references = prevreferences;
}
else
{
#ifdef SCRAMBLE_REMOVED
// Invalidate mobj_t data to cause crashes if accessed!
memset((UINT8 *)mobj + sizeof(thinker_t), 0xff, sizeof(mobj_t) - sizeof(thinker_t));
#endif
P_RemoveThinker((thinker_t *)mobj);
}
P_RemoveThinker((thinker_t *)mobj);
}
// This does not need to be added to Lua.
@@ -10387,7 +10402,7 @@ void P_RemovePrecipMobj(precipmobj_t *mobj)
}
// Clearing out stuff for savegames
void P_RemoveSavegameMobj(mobj_t *mobj)
void P_RemoveSavegameMobj(mobj_t *mobj, boolean preserveLevel)
{
// unlink from sector and block lists
P_UnsetThingPosition(mobj);
@@ -10400,7 +10415,15 @@ void P_RemoveSavegameMobj(mobj_t *mobj)
}
// stop any playing sound
S_StopSound(mobj);
if (!preserveLevel)
{
S_StopSound(mobj);
}
else
{
// detach any playing sounds from this mobj, but don't destroy them as they might still be there
S_DetachChannelsFromOrigin(mobj);
}
// free block
P_RemoveThinker((thinker_t *)mobj);
@@ -10617,7 +10640,7 @@ void P_RespawnSpecials(void)
{
thinker_t *th;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
mobj_t *box;
mobj_t *newmobj;
@@ -11767,9 +11790,9 @@ ML_NOCLIMB : Direction not controllable
mobj->health = (mthing->angle / 360) + 1;
// See if other starposts exist in this level that have the same value.
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -12047,7 +12070,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
//mobj->hprev->hnext = mobj;
}
else
mobj->hprev = mobj->hnext = NULL;
P_SetTarget(&mobj->hprev, P_SetTarget(&mobj->hnext, NULL));
nextmobj = mobj;
}
@@ -12073,11 +12096,9 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
mobj->z -= mobj->height/2;
// Link all the collision sprites together.
mobj->hnext = NULL;
P_SetTarget(&mobj->hnext, NULL);
P_SetTarget(&mobj->hprev, nextmobj);
P_SetTarget(&mobj->hprev->hnext, mobj);
//mobj->hprev = nextmobj;
//mobj->hprev->hnext = mobj;
nextmobj = mobj;
}
@@ -12102,11 +12123,9 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
mobj->z -= mobj->height/2;
// Link all the collision sprites together.
mobj->hnext = NULL;
P_SetTarget(&mobj->hnext, NULL);
P_SetTarget(&mobj->hprev, nextmobj);
P_SetTarget(&mobj->hprev->hnext, mobj);
//mobj->hprev = nextmobj;
//mobj->hprev->hnext = mobj;
nextmobj = mobj;
}
@@ -12195,7 +12214,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
//mobj->hprev->hnext = mobj;
}
else
mobj->hprev = mobj->hnext = NULL;
P_SetTarget(&mobj->hprev, P_SetTarget(&mobj->hnext, NULL));
nextmobj = mobj;
}
@@ -12232,11 +12251,9 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
mobj->z -= mobj->height/2;
// Link all the collision sprites together.
mobj->hnext = NULL;
P_SetTarget(&mobj->hnext, NULL);
P_SetTarget(&mobj->hprev, nextmobj);
P_SetTarget(&mobj->hprev->hnext, mobj);
//mobj->hprev = nextmobj;
//mobj->hprev->hnext = mobj;
nextmobj = mobj;
}
@@ -12867,6 +12884,7 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
angle_t an;
fixed_t x, y, z, slope = 0;
// angle at which you fire, is player angle
an = angle;
@@ -12883,6 +12901,13 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
th = P_SpawnMobj(x, y, z, type);
if (cv_netslingdelay.value && issimulation && (tic_t)cv_netsteadyplayers.value >= targetsimtic - simtic && source == players[consoleplayer].mo)
{
z = 0x80000000; // don't make rings appear when using netslingdelay
th->flags |= MF_NOTHINK;
th->sprite = SPR_NULL;
}
if (source->eflags & MFE_VERTICALFLIP)
th->flags2 |= MF2_OBJECTFLIP;
+7
View File
@@ -354,6 +354,7 @@ typedef struct mobj_s
fixed_t waterbottom; // bottom of the water FOF the mobj is in
UINT32 mobjnum; // A unique number for this mobj. Used for restoring pointers on save games.
UINT32 localmobjnum; // A unique number for this mobj stored locally for association when restoring savestates
fixed_t scale;
fixed_t destscale;
@@ -374,6 +375,8 @@ typedef struct mobj_s
boolean colorized; // Whether the mobj uses the rainbow colormap
boolean isculled; // Whether the mobj is being culled during simulations
// WARNING: New fields must be added separately to savegame and Lua.
} mobj_t;
@@ -464,6 +467,10 @@ void P_SetScale(mobj_t *mobj, fixed_t newscale);
void P_XYMovement(mobj_t *mo);
void P_EmeraldManager(void);
extern INT32 modulothing;
extern UINT32 globalmobjnum;
#define MAXHUNTEMERALDS 64
extern mapthing_t *huntemeralds[MAXHUNTEMERALDS];
extern INT32 numhuntemeralds;
+162 -167
View File
@@ -142,16 +142,6 @@ FUNCINLINE static ATTRINLINE void Polyobj_vecSub2(vertex_t *dst, vertex_t *v1, v
dst->y = v1->y - v2->y;
}
// Add the polyobject's thinker to the thinker list
// Unlike P_AddThinker, this adds it to the front of the list instead of the back, so that carrying physics can work right. -Red
FUNCINLINE static ATTRINLINE void PolyObj_AddThinker(thinker_t *th)
{
thinkercap.next->prev = th;
th->next = thinkercap.next;
th->prev = &thinkercap;
thinkercap.next = th;
}
//
// P_PointInsidePolyobj
//
@@ -1504,31 +1494,31 @@ void Polyobj_InitLevel(void)
// run down the thinker list, count the number of spawn points, and save
// the mobj_t pointers on a queue for use below.
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mobj_t *mo = (mobj_t *)th;
if (mo->info->doomednum == POLYOBJ_SPAWN_DOOMEDNUM ||
mo->info->doomednum == POLYOBJ_SPAWNCRUSH_DOOMEDNUM)
{
mobj_t *mo = (mobj_t *)th;
++numPolyObjects;
if (mo->info->doomednum == POLYOBJ_SPAWN_DOOMEDNUM ||
mo->info->doomednum == POLYOBJ_SPAWNCRUSH_DOOMEDNUM)
{
++numPolyObjects;
qitem = malloc(sizeof(mobjqitem_t));
memset(qitem, 0, sizeof(mobjqitem_t));
qitem->mo = mo;
M_QueueInsert(&(qitem->mqitem), &spawnqueue);
}
else if (mo->info->doomednum == POLYOBJ_ANCHOR_DOOMEDNUM)
{
++numAnchors;
qitem = malloc(sizeof(mobjqitem_t));
memset(qitem, 0, sizeof(mobjqitem_t));
qitem->mo = mo;
M_QueueInsert(&(qitem->mqitem), &spawnqueue);
}
else if (mo->info->doomednum == POLYOBJ_ANCHOR_DOOMEDNUM)
{
++numAnchors;
qitem = malloc(sizeof(mobjqitem_t));
memset(qitem, 0, sizeof(mobjqitem_t));
qitem->mo = mo;
M_QueueInsert(&(qitem->mqitem), &anchorqueue);
}
qitem = malloc(sizeof(mobjqitem_t));
memset(qitem, 0, sizeof(mobjqitem_t));
qitem->mo = mo;
M_QueueInsert(&(qitem->mqitem), &anchorqueue);
}
}
@@ -1548,6 +1538,11 @@ void Polyobj_InitLevel(void)
qitem = (mobjqitem_t *)M_QueueIterator(&spawnqueue);
Polyobj_spawnPolyObj(i, qitem->mo, qitem->mo->spawnpoint->angle);
PolyObjects[i].origx = qitem->mo->x;
PolyObjects[i].origy = qitem->mo->y;
PolyObjects[i].origz = qitem->mo->z;
PolyObjects[i].origangle = qitem->mo->spawnpoint->angle;
}
// move polyobjects to spawn points
@@ -1639,7 +1634,7 @@ void T_PolyObjRotate(polyrotate_t *th)
#else
{
CONS_Debug(DBG_POLYOBJ, "T_PolyObjRotate: thinker with invalid id %d removed.\n", th->polyObjNum);
P_RemoveThinkerDelayed(&th->thinker);
P_RemoveThinker(&th->thinker);
return;
}
#endif
@@ -1724,7 +1719,7 @@ void T_PolyObjMove(polymove_t *th)
#else
{
CONS_Debug(DBG_POLYOBJ, "T_PolyObjMove: thinker with invalid id %d removed.\n", th->polyObjNum);
P_RemoveThinkerDelayed(&th->thinker);
P_RemoveThinker(&th->thinker);
return;
}
#endif
@@ -1797,7 +1792,7 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
#else
{
CONS_Debug(DBG_POLYOBJ, "T_PolyObjWaypoint: thinker with invalid id %d removed.", th->polyObjNum);
P_RemoveThinkerDelayed(&th->thinker);
P_RemoveThinker(&th->thinker);
return;
}
#endif
@@ -1808,9 +1803,9 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
// Find out target first.
// We redo this each tic to make savegame compatibility easier.
for (wp = thinkercap.next; wp != &thinkercap; wp = wp->next)
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
@@ -1889,9 +1884,9 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
CONS_Debug(DBG_POLYOBJ, "Looking for next waypoint...\n");
// Find next waypoint
for (wp = thinkercap.next; wp != &thinkercap; wp = wp->next)
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
@@ -1899,8 +1894,85 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == th->sequence)
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1)
{
if (mo2->health == target->health - 1)
{
waypoint = mo2;
break;
}
}
else
{
if (mo2->health == target->health + 1)
{
waypoint = mo2;
break;
}
}
}
if (!waypoint && th->wrap) // If specified, wrap waypoints
{
if (!th->continuous)
{
th->wrap = 0;
th->stophere = true;
}
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1)
{
if (waypoint == NULL)
waypoint = mo2;
else if (mo2->health > waypoint->health)
waypoint = mo2;
}
else
{
if (mo2->health == 0)
{
waypoint = mo2;
break;
}
}
}
}
else if (!waypoint && th->comeback) // Come back to the start
{
th->direction = -th->direction;
if (!th->continuous)
th->comeback = false;
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1)
{
if (mo2->health == target->health - 1)
@@ -1919,83 +1991,6 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
}
}
}
if (!waypoint && th->wrap) // If specified, wrap waypoints
{
if (!th->continuous)
{
th->wrap = 0;
th->stophere = true;
}
for (wp = thinkercap.next; wp != &thinkercap; wp = wp->next)
{
if (wp->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == th->sequence)
{
if (th->direction == -1)
{
if (waypoint == NULL)
waypoint = mo2;
else if (mo2->health > waypoint->health)
waypoint = mo2;
}
else
{
if (mo2->health == 0)
{
waypoint = mo2;
break;
}
}
}
}
}
else if (!waypoint && th->comeback) // Come back to the start
{
th->direction = -th->direction;
if (!th->continuous)
th->comeback = false;
for (wp = thinkercap.next; wp != &thinkercap; wp = wp->next)
{
if (wp->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)wp;
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == th->sequence)
{
if (th->direction == -1)
{
if (mo2->health == target->health - 1)
{
waypoint = mo2;
break;
}
}
else
{
if (mo2->health == target->health + 1)
{
waypoint = mo2;
break;
}
}
}
}
}
}
if (waypoint)
@@ -2071,7 +2066,7 @@ void T_PolyDoorSlide(polyslidedoor_t *th)
#else
{
CONS_Debug(DBG_POLYOBJ, "T_PolyDoorSlide: thinker with invalid id %d removed.\n", th->polyObjNum);
P_RemoveThinkerDelayed(&th->thinker);
P_RemoveThinker(&th->thinker);
return;
}
#endif
@@ -2176,7 +2171,7 @@ void T_PolyDoorSwing(polyswingdoor_t *th)
#else
{
CONS_Debug(DBG_POLYOBJ, "T_PolyDoorSwing: thinker with invalid id %d removed.\n", th->polyObjNum);
P_RemoveThinkerDelayed(&th->thinker);
P_RemoveThinker(&th->thinker);
return;
}
#endif
@@ -2274,8 +2269,8 @@ void T_PolyObjDisplace(polydisplace_t *th)
I_Error("T_PolyDoorSwing: thinker has invalid id %d\n", th->polyObjNum);
#else
{
CONS_Debug(DBG_POLYOBJ, "T_PolyDoorSwing: thinker with invalid id %d removed.\n", th->polyObjNum);
P_RemoveThinkerDelayed(&th->thinker);
CONS_Debug(DBG_POLYOBJ, "T_PolyObjDisplace: thinker with invalid id %d removed.\n", th->polyObjNum);
P_RemoveThinker(&th->thinker);
return;
}
#endif
@@ -2333,7 +2328,7 @@ INT32 EV_DoPolyObjRotate(polyrotdata_t *prdata)
// create a new thinker
th = Z_Malloc(sizeof(polyrotate_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjRotate;
PolyObj_AddThinker(&th->thinker);
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
// set fields
@@ -2398,7 +2393,7 @@ INT32 EV_DoPolyObjMove(polymovedata_t *pmdata)
// create a new thinker
th = Z_Malloc(sizeof(polymove_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjMove;
PolyObj_AddThinker(&th->thinker);
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
// set fields
@@ -2459,7 +2454,7 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
// create a new thinker
th = Z_Malloc(sizeof(polywaypoint_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjWaypoint;
PolyObj_AddThinker(&th->thinker);
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
// set fields
@@ -2477,9 +2472,9 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
th->stophere = false;
// Find the first waypoint we need to use
for (wp = thinkercap.next; wp != &thinkercap; wp = wp->next)
for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
@@ -2487,31 +2482,31 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == th->sequence)
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1) // highest waypoint #
{
if (th->direction == -1) // highest waypoint #
if (mo2->health == 0)
last = mo2;
else
{
if (mo2->health == 0)
last = mo2;
else
{
if (first == NULL)
first = mo2;
else if (mo2->health > first->health)
first = mo2;
}
}
else // waypoint 0
{
if (mo2->health == 0)
if (first == NULL)
first = mo2;
else
{
if (last == NULL)
last = mo2;
else if (mo2->health > last->health)
last = mo2;
}
else if (mo2->health > first->health)
first = mo2;
}
}
else // waypoint 0
{
if (mo2->health == 0)
first = mo2;
else
{
if (last == NULL)
last = mo2;
else if (mo2->health > last->health)
last = mo2;
}
}
}
@@ -2548,9 +2543,9 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
// Find the actual target movement waypoint
target = first;
/*for (wp = thinkercap.next; wp != &thinkercap; wp = wp->next)
/*for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
{
if (wp->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (wp->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)wp;
@@ -2558,23 +2553,23 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == th->sequence)
if (mo2->threshold != th->sequence)
continue;
if (th->direction == -1) // highest waypoint #
{
if (th->direction == -1) // highest waypoint #
if (mo2->health == first->health - 1)
{
if (mo2->health == first->health - 1)
{
target = mo2;
break;
}
target = mo2;
break;
}
else // waypoint 0
}
else // waypoint 0
{
if (mo2->health == first->health + 1)
{
if (mo2->health == first->health + 1)
{
target = mo2;
break;
}
target = mo2;
break;
}
}
}*/
@@ -2605,7 +2600,7 @@ static void Polyobj_doSlideDoor(polyobj_t *po, polydoordata_t *doordata)
// allocate and add a new slide door thinker
th = Z_Malloc(sizeof(polyslidedoor_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyDoorSlide;
PolyObj_AddThinker(&th->thinker);
P_AddThinker(THINK_POLYOBJ, &th->thinker);
// point the polyobject to this thinker
po->thinker = &th->thinker;
@@ -2653,7 +2648,7 @@ static void Polyobj_doSwingDoor(polyobj_t *po, polydoordata_t *doordata)
// allocate and add a new swing door thinker
th = Z_Malloc(sizeof(polyswingdoor_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyDoorSwing;
PolyObj_AddThinker(&th->thinker);
P_AddThinker(THINK_POLYOBJ, &th->thinker);
// point the polyobject to this thinker
po->thinker = &th->thinker;
@@ -2735,7 +2730,7 @@ INT32 EV_DoPolyObjDisplace(polydisplacedata_t *prdata)
// create a new thinker
th = Z_Malloc(sizeof(polydisplace_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjDisplace;
PolyObj_AddThinker(&th->thinker);
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
// set fields
@@ -2772,7 +2767,7 @@ void T_PolyObjFlag(polymove_t *th)
#else
{
CONS_Debug(DBG_POLYOBJ, "T_PolyObjFlag: thinker with invalid id %d removed.\n", th->polyObjNum);
P_RemoveThinkerDelayed(&th->thinker);
P_RemoveThinker(&th->thinker);
return;
}
#endif
@@ -2836,7 +2831,7 @@ INT32 EV_DoPolyObjFlag(line_t *pfdata)
// create a new thinker
th = Z_Malloc(sizeof(polymove_t), PU_LEVSPEC, NULL);
th->thinker.function.acp1 = (actionf_p1)T_PolyObjFlag;
PolyObj_AddThinker(&th->thinker);
P_AddThinker(THINK_POLYOBJ, &th->thinker);
po->thinker = &th->thinker;
// set fields
+4
View File
@@ -104,6 +104,10 @@ typedef struct polyobj_s
// these are saved for netgames, so do not let Lua touch these!
INT32 spawnflags; // Flags the polyobject originally spawned with
INT32 spawntrans; // Translucency the polyobject originally spawned with
fixed_t origx, origy, origz;
INT16 origangle;
} polyobj_t;
//
+1709 -395
View File
File diff suppressed because it is too large. Load diff
+17 -10
View File
@@ -18,16 +18,6 @@
#pragma interface
#endif
// Persistent storage/archiving.
// These are the load / save game routines.
void P_SaveGame(void);
void P_SaveNetGame(void);
boolean P_LoadGame(INT16 mapoverride);
boolean P_LoadNetGame(void);
mobj_t *P_FindNewPosition(UINT32 oldposition);
typedef struct
{
UINT8 skincolor;
@@ -40,6 +30,23 @@ typedef struct
UINT16 emeralds;
} savedata_t;
typedef struct
{
UINT8 buffer[16 * 1024 * 1024]; // 16 megs per frame should do
} savestate_t;
// Persistent storage/archiving.
// These are the load / save game routines.
void P_SaveGame(void);
void P_SaveNetGame(void);
void P_SaveGameState(savestate_t* savestate);
boolean P_LoadGame(INT16 mapoverride);
boolean P_LoadNetGame(boolean preserveLevel);
boolean P_LoadGameState(const savestate_t* savestate);
mobj_t *P_FindNewPosition(UINT32 oldposition);
extern savedata_t savedata;
extern UINT8 *save_p;
+19 -12
View File
@@ -832,10 +832,10 @@ void P_ReloadRings(void)
mapthing_t *hoopsToRespawn[4096];
mapthing_t *mt = mapthings;
// scan the thinkers to find rings/wings/hoops to unset
for (th = thinkercap.next; th != &thinkercap; th = th->next)
// scan the thinkers to find rings/spheres/hoops to unset
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)th;
@@ -2411,18 +2411,13 @@ static void P_LevelInitStuff(void)
void P_LoadThingsOnly(void)
{
// Search through all the thinkers.
mobj_t *mo;
thinker_t *think;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
mo = (mobj_t *)think;
if (mo)
P_RemoveMobj(mo);
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
P_RemoveMobj((mobj_t *)think);
}
P_LevelInitStuff();
@@ -2775,6 +2770,11 @@ static boolean P_CanSave(void)
*/
boolean P_SetupLevel(boolean skipprecip)
{
if (issimulation)
{
return true;
}
// use gamemap to get map number.
// 99% of the things already did, so.
// Map header should always be in place at this point
@@ -2793,6 +2793,13 @@ boolean P_SetupLevel(boolean skipprecip)
CON_Drawer(); // let the user know what we are going to do
I_FinishUpdate(); // page flip or blit buffer
// Reset the palette
if (rendermode != render_none)
V_SetPaletteLump("PLAYPAL");
// Invalidate simulation save states
InvalidateSavestates();
// Initialize sector node list.
P_Initsecnode();
+76 -79
View File
@@ -1594,7 +1594,7 @@ static void P_AddExecutorDelay(line_t *line, mobj_t *mobj, sector_t *sector)
e->sector = sector;
e->timer = (line->backsector->ceilingheight>>FRACBITS)+(line->backsector->floorheight>>FRACBITS);
P_SetTarget(&e->caller, mobj); // Use P_SetTarget to make sure the mobj doesn't get freed while we're delaying.
P_AddThinker(&e->thinker);
P_AddThinker(THINK_MAIN, &e->thinker);
}
/** Used by P_LinedefExecute to check a trigger linedef's conditions
@@ -2058,7 +2058,7 @@ void P_SwitchWeather(INT32 weathernum)
thinker_t *think;
precipmobj_t *precipmobj;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_PRECIP].next; think != &thlist[THINK_PRECIP]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker)
continue; // not a precipmobj thinker
@@ -2074,7 +2074,7 @@ void P_SwitchWeather(INT32 weathernum)
precipmobj_t *precipmobj;
state_t *st;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_PRECIP].next; think != &thlist[THINK_PRECIP]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker)
continue; // not a precipmobj thinker
@@ -2934,7 +2934,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
scroll_t *scroller;
thinker_t *th;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)T_Scroll)
continue;
@@ -3273,10 +3273,10 @@ void P_SetupSignExit(player_t *player)
// didn't find any signposts in the exit sector.
// spin all signposts in the level then.
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
thing = (mobj_t *)think;
if (thing->type != MT_SIGN)
@@ -3320,23 +3320,18 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
{
thinker_t *think;
mobj_t *mo;
INT32 specialnum = 0;
INT32 specialnum = (flag == MT_REDFLAG) ? 3 : 4;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)think;
if (mo->type != flag)
continue;
if (mo->type == MT_REDFLAG)
specialnum = 3;
else if (mo->type == MT_BLUEFLAG)
specialnum = 4;
if (GETSECSPECIAL(mo->subsector->sector->special, 4) == specialnum)
return true;
else if (mo->subsector->sector->ffloors) // Check the 3D floors
@@ -3351,9 +3346,11 @@ boolean P_IsFlagAtBase(mobjtype_t flag)
if (GETSECSPECIAL(rover->master->frontsector->special, 4) != specialnum)
continue;
if (mo->z <= P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector)
&& mo->z >= P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector))
return true;
if (!(mo->z <= P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector)
&& mo->z >= P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector)))
continue;
return true;
}
}
}
@@ -3763,14 +3760,14 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
// Find the center of the Eggtrap and release all the pretty animals!
// The chimps are my friends.. heeheeheheehehee..... - LouisJM
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_EGGTRAP)
P_KillMobj(mo2, NULL, player->mo);
if (mo2->type != MT_EGGTRAP)
continue;
P_KillMobj(mo2, NULL, player->mo);
}
// clear the special so you can't push the button twice.
@@ -4100,19 +4097,22 @@ DoneSection2:
// scan the thinkers
// to find the first waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_TUBEWAYPOINT && mo2->threshold == sequence
&& mo2->health == 0)
{
waypoint = mo2;
break;
}
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != sequence)
continue;
if (mo2->health != 0)
continue;
waypoint = mo2;
break;
}
if (!waypoint)
@@ -4176,20 +4176,22 @@ DoneSection2:
// scan the thinkers
// to find the last waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_TUBEWAYPOINT && mo2->threshold == sequence)
{
if (!waypoint)
waypoint = mo2;
else if (mo2->health > waypoint->health)
waypoint = mo2;
}
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold != sequence)
continue;
if (!waypoint)
waypoint = mo2;
else if (mo2->health > waypoint->health)
waypoint = mo2;
}
if (!waypoint)
@@ -4391,9 +4393,9 @@ DoneSection2:
// scan the thinkers
// to find the first waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -4429,9 +4431,9 @@ DoneSection2:
}
// Find waypoint before this one (waypointlow)
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -4456,9 +4458,9 @@ DoneSection2:
}
// Find waypoint after this one (waypointhigh)
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -4978,11 +4980,6 @@ void P_UpdateSpecials(void)
// POINT LIMIT
P_CheckPointLimit();
#ifdef ESLOPE
// Dynamic slopeness
P_RunDynamicSlopes();
#endif
// ANIMATE TEXTURES
for (anim = anims; anim < lastanim; anim++)
{
@@ -5151,7 +5148,7 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
// Just initialise both of these to placate the compiler.
i = 0;
th = thinkercap.next;
th = thlist[THINK_MAIN].next;
for(;;)
{
@@ -5161,7 +5158,7 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
th = secthinkers[sec2num].thinkers[i];
else break;
}
else if (th == &thinkercap)
else if (th == &thlist[THINK_MAIN])
break;
// Should this FOF have spikeness?
@@ -5250,7 +5247,7 @@ static void P_AddSpikeThinker(sector_t *sec, INT32 referrer)
// create and initialize new thinker
spikes = Z_Calloc(sizeof (*spikes), PU_LEVSPEC, NULL);
P_AddThinker(&spikes->thinker);
P_AddThinker(THINK_MAIN, &spikes->thinker);
spikes->thinker.function.acp1 = (actionf_p1)T_SpikeSector;
@@ -5272,7 +5269,7 @@ static void P_AddFloatThinker(sector_t *sec, INT32 tag, line_t *sourceline)
// create and initialize new thinker
floater = Z_Calloc(sizeof (*floater), PU_LEVSPEC, NULL);
P_AddThinker(&floater->thinker);
P_AddThinker(THINK_MAIN, &floater->thinker);
floater->thinker.function.acp1 = (actionf_p1)T_FloatSector;
@@ -5296,7 +5293,7 @@ static inline void P_AddBridgeThinker(line_t *sourceline, sector_t *sec)
// create an initialize new thinker
bridge = Z_Calloc(sizeof (*bridge), PU_LEVSPEC, NULL);
P_AddThinker(&bridge->thinker);
P_AddThinker(THINK_MAIN, &bridge->thinker);
bridge->thinker.function.acp1 = (actionf_p1)T_BridgeThinker;
@@ -5330,7 +5327,7 @@ static void P_AddBlockThinker(sector_t *sec, line_t *sourceline)
// create and initialize new elevator thinker
block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL);
P_AddThinker(&block->thinker);
P_AddThinker(THINK_MAIN, &block->thinker);
block->thinker.function.acp1 = (actionf_p1)T_MarioBlockChecker;
block->sourceline = sourceline;
@@ -5359,7 +5356,7 @@ static void P_AddRaiseThinker(sector_t *sec, line_t *sourceline)
levelspecthink_t *raise;
raise = Z_Calloc(sizeof (*raise), PU_LEVSPEC, NULL);
P_AddThinker(&raise->thinker);
P_AddThinker(THINK_MAIN, &raise->thinker);
raise->thinker.function.acp1 = (actionf_p1)T_RaiseSector;
@@ -5398,7 +5395,7 @@ static void P_AddOldAirbob(sector_t *sec, line_t *sourceline, boolean noadjust)
levelspecthink_t *airbob;
airbob = Z_Calloc(sizeof (*airbob), PU_LEVSPEC, NULL);
P_AddThinker(&airbob->thinker);
P_AddThinker(THINK_MAIN, &airbob->thinker);
airbob->thinker.function.acp1 = (actionf_p1)T_RaiseSector;
@@ -5459,7 +5456,7 @@ static inline void P_AddThwompThinker(sector_t *sec, sector_t *actionsector, lin
// create and initialize new elevator thinker
thwomp = Z_Calloc(sizeof (*thwomp), PU_LEVSPEC, NULL);
P_AddThinker(&thwomp->thinker);
P_AddThinker(THINK_MAIN, &thwomp->thinker);
thwomp->thinker.function.acp1 = (actionf_p1)T_ThwompSector;
@@ -5495,7 +5492,7 @@ static inline void P_AddNoEnemiesThinker(sector_t *sec, line_t *sourceline)
// create and initialize new thinker
nobaddies = Z_Calloc(sizeof (*nobaddies), PU_LEVSPEC, NULL);
P_AddThinker(&nobaddies->thinker);
P_AddThinker(THINK_MAIN, &nobaddies->thinker);
nobaddies->thinker.function.acp1 = (actionf_p1)T_NoEnemiesSector;
@@ -5517,7 +5514,7 @@ static inline void P_AddEachTimeThinker(sector_t *sec, line_t *sourceline)
// create and initialize new thinker
eachtime = Z_Calloc(sizeof (*eachtime), PU_LEVSPEC, NULL);
P_AddThinker(&eachtime->thinker);
P_AddThinker(THINK_MAIN, &eachtime->thinker);
eachtime->thinker.function.acp1 = (actionf_p1)T_EachTimeThinker;
@@ -5539,7 +5536,7 @@ static inline void P_AddCameraScanner(sector_t *sourcesec, sector_t *actionsecto
// create and initialize new elevator thinker
elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL);
P_AddThinker(&elevator->thinker);
P_AddThinker(THINK_MAIN, &elevator->thinker);
elevator->thinker.function.acp1 = (actionf_p1)T_CameraScanner;
elevator->type = elevateBounce;
@@ -5633,7 +5630,7 @@ static inline void EV_AddLaserThinker(sector_t *sec, sector_t *sec2, line_t *lin
flash = Z_Calloc(sizeof (*flash), PU_LEVSPEC, NULL);
P_AddThinker(&flash->thinker);
P_AddThinker(THINK_MAIN, &flash->thinker);
flash->thinker.function.acp1 = (actionf_p1)T_LaserFlash;
flash->ffloor = ffloor;
@@ -5762,7 +5759,7 @@ void P_SpawnSpecials(INT32 fromnetsave)
secthinkers = Z_Calloc(numsectors * sizeof(thinkerlist_t), PU_STATIC, NULL);
// Firstly, find out how many there are in each sector
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next)
{
if (th->function.acp1 == (actionf_p1)T_SpikeSector)
secthinkers[((levelspecthink_t *)th)->sector - sectors].count++;
@@ -5782,7 +5779,7 @@ void P_SpawnSpecials(INT32 fromnetsave)
}
// Finally, populate the lists.
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MAIN].next; th != &thlist[THINK_MAIN]; th = th->next)
{
size_t secnum = (size_t)-1;
@@ -6677,14 +6674,14 @@ void P_SpawnSpecials(INT32 fromnetsave)
sectors[s].midmap = lines[i].frontsector->midmap;
break;
#ifdef ESLOPE // Slope copy specials. Handled here for sanity.
case 720:
case 721:
case 722:
P_CopySectorSlope(&lines[i]);
break;
#endif
#ifdef ESLOPE // Slope copy specials. Handled here for sanity.
case 720:
case 721:
case 722:
P_CopySectorSlope(&lines[i]);
break;
#endif
default:
break;
}
@@ -7040,7 +7037,7 @@ static void Add_Scroller(INT32 type, fixed_t dx, fixed_t dy, INT32 control, INT3
if ((s->control = control) != -1)
s->last_height = sectors[control].floorheight + sectors[control].ceilingheight;
s->affectee = affectee;
P_AddThinker(&s->thinker);
P_AddThinker(THINK_MAIN, &s->thinker);
}
/** Adds a wall scroller.
@@ -7199,7 +7196,7 @@ static void Add_MasterDisappearer(tic_t appeartime, tic_t disappeartime, tic_t o
d->exists = true;
d->timer = 1;
P_AddThinker(&d->thinker);
P_AddThinker(THINK_MAIN, &d->thinker);
}
/** Makes a FOF appear/disappear
@@ -7293,7 +7290,7 @@ static void Add_Friction(INT32 friction, INT32 movefactor, INT32 affectee, INT32
else
f->roverfriction = false;
P_AddThinker(&f->thinker);
P_AddThinker(THINK_MAIN, &f->thinker);
}
/** Applies friction to all things in a sector.
@@ -7481,7 +7478,7 @@ static void Add_Pusher(pushertype_e type, fixed_t x_mag, fixed_t y_mag, mobj_t *
p->z = p->source->z;
}
p->affectee = affectee;
P_AddThinker(&p->thinker);
P_AddThinker(THINK_MAIN, &p->thinker);
}
+71 -36
View File
@@ -37,8 +37,8 @@ tic_t leveltime;
// but the first element must be thinker_t.
//
// Both the head and tail of the thinker list.
thinker_t thinkercap;
// The entries will behave like both the head and tail of the lists.
thinker_t thlist[NUM_THINKERLISTS];
void Command_Numthinkers_f(void)
{
@@ -46,6 +46,9 @@ void Command_Numthinkers_f(void)
INT32 count = 0;
actionf_p1 action;
thinker_t *think;
thinklistnum_t start = 0;
thinklistnum_t end = NUM_THINKERLISTS - 1;
thinklistnum_t i;
if (gamestate != GS_LEVEL)
{
@@ -72,6 +75,7 @@ void Command_Numthinkers_f(void)
switch (num)
{
case 1:
start = end = THINK_MOBJ;
action = (actionf_p1)P_MobjThinker;
CONS_Printf(M_GetText("Number of %s: "), "P_MobjThinker");
break;
@@ -84,14 +88,17 @@ void Command_Numthinkers_f(void)
CONS_Printf(M_GetText("Number of %s: "), "P_SnowThinker");
break;*/
case 2:
start = end = THINK_PRECIP;
action = (actionf_p1)P_NullPrecipThinker;
CONS_Printf(M_GetText("Number of %s: "), "P_NullPrecipThinker");
break;
case 3:
start = end = THINK_MAIN;
action = (actionf_p1)T_Friction;
CONS_Printf(M_GetText("Number of %s: "), "T_Friction");
break;
case 4:
start = end = THINK_MAIN;
action = (actionf_p1)T_Pusher;
CONS_Printf(M_GetText("Number of %s: "), "T_Pusher");
break;
@@ -104,12 +111,15 @@ void Command_Numthinkers_f(void)
return;
}
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (i = start; i <= end; i++)
{
if (think->function.acp1 != action)
continue;
for (think = thlist[i].next; think != &thlist[i]; think = think->next)
{
if (think->function.acp1 != action)
continue;
count++;
count++;
}
}
CONS_Printf("%d\n", count);
@@ -141,9 +151,9 @@ void Command_CountMobjs_f(void)
count = 0;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
if (((mobj_t *)th)->type == i)
@@ -161,9 +171,9 @@ void Command_CountMobjs_f(void)
{
count = 0;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
if (((mobj_t *)th)->type == i)
@@ -180,20 +190,24 @@ void Command_CountMobjs_f(void)
//
void P_InitThinkers(void)
{
thinkercap.prev = thinkercap.next = &thinkercap;
waypointcap = NULL;
UINT8 i;
for (i = 0; i < NUM_THINKERLISTS; i++)
thlist[i].prev = thlist[i].next = &thlist[i];
waypointcap = NULL; //Hack transplant
}
//
// P_AddThinker
// Adds a new thinker at the end of the list.
//
void P_AddThinker(thinker_t *thinker)
void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
{
thinkercap.prev->next = thinker;
thinker->next = &thinkercap;
thinker->prev = thinkercap.prev;
thinkercap.prev = thinker;
#ifdef PARANOIA
I_Assert(n >= 0 && n < NUM_THINKERLISTS);
#endif
thlist[n].prev->next = thinker;
thinker->next = &thlist[n];
thinker->prev = thlist[n].prev;
thlist[n].prev = thinker;
thinker->references = 0; // killough 11/98: init reference counter to 0
}
@@ -216,22 +230,33 @@ static thinker_t *currentthinker;
// remove it, and set currentthinker to one node preceeding it, so
// that the next step in P_RunThinkers() will get its successor.
//
void P_RemoveThinkerDelayed(void *pthinker)
void P_RemoveThinkerDelayed(thinker_t *thinker)
{
thinker_t *thinker = pthinker;
if (!thinker->references)
thinker_t *next;
#ifdef PARANOIA
#define BEENAROUNDBIT (0x40000000) // has to be sufficiently high that it's unlikely to happen in regular gameplay. If you change this, pay attention to the bit pattern of INT32_MIN.
if (thinker->references & ~BEENAROUNDBIT)
{
{
/* Remove from main thinker list */
thinker_t *next = thinker->next;
/* Note that currentthinker is guaranteed to point to us,
* and since we're freeing our memory, we had better change that. So
* point it to thinker->prev, so the iterator will correctly move on to
* thinker->prev->next = thinker->next */
(next->prev = currentthinker = thinker->prev)->next = next;
}
Z_Free(thinker);
if (thinker->references & BEENAROUNDBIT) // Usually gets cleared up in one frame; what's going on here, then?
CONS_Printf("Number of potentially faulty references: %d\n", (thinker->references & ~BEENAROUNDBIT));
thinker->references |= BEENAROUNDBIT;
return;
}
#undef BEENAROUNDBIT
#else
if (thinker->references)
return;
#endif
/* Remove from main thinker list */
next = thinker->next;
/* Note that currentthinker is guaranteed to point to us,
* and since we're freeing our memory, we had better change that. So
* point it to thinker->prev, so the iterator will correctly move on to
* thinker->prev->next = thinker->next */
(next->prev = currentthinker = thinker->prev)->next = next;
Z_Free(thinker);
}
//
@@ -251,7 +276,7 @@ void P_RemoveThinker(thinker_t *thinker)
#ifdef HAVE_BLUA
LUA_InvalidateUserdata(thinker);
#endif
thinker->function.acp1 = P_RemoveThinkerDelayed;
thinker->function.acp1 = (actionf_p1)P_RemoveThinkerDelayed;
}
/*
@@ -299,11 +324,21 @@ if ((*mop = targ) != NULL) // Set new target and if non-NULL, increase its count
//
static inline void P_RunThinkers(void)
{
for (currentthinker = thinkercap.next; currentthinker != &thinkercap; currentthinker = currentthinker->next)
size_t i;
for (i = 0; i < NUM_THINKERLISTS; i++)
{
if (currentthinker->function.acp1)
for (currentthinker = thlist[i].next; currentthinker != &thlist[i]; currentthinker = currentthinker->next)
{
#ifdef PARANOIA
I_Assert(currentthinker->function.acp1 != NULL);
#endif
if (currentthinker->function.acp1 == P_MobjThinker && issimulation && ((mobj_t*)currentthinker)->isculled)
continue; // apply distance culling
currentthinker->function.acp1(currentthinker);
}
}
}
//
+1 -1
View File
@@ -27,7 +27,7 @@ void Command_CountMobjs_f(void);
void P_Ticker(boolean run);
void P_PreTicker(INT32 frames);
void P_DoTeamscrambling(void);
void P_RemoveThinkerDelayed(void *pthinker); //killed
void P_RemoveThinkerDelayed(thinker_t *thinker); //killed
mobj_t *P_SetTarget(mobj_t **mo, mobj_t *target); // killough 11/98
#endif
+395 -201
View File
@@ -356,14 +356,18 @@ void P_ResetScore(player_t *player)
// scan the thinkers
// to find the egg capsule with the lowest mare
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_EGGCAPSULE && mo2->health > 0)
if (mo2->type != MT_EGGCAPSULE)
continue;
if (mo2->health <= 0)
continue;
{
const UINT8 threshold = (UINT8)mo2->threshold;
if (mare == 255)
@@ -457,34 +461,34 @@ UINT8 P_FindHighestLap(void)
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_AXIS)
{
if (mo2->threshold == mare)
{
if (closestaxis == NULL)
{
closestaxis = mo2;
lowestaxisnum = mo2->health;
dist2 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y)-mo2->radius;
}
else if (mo2->health < lowestaxisnum)
{
dist1 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y)-mo2->radius;
if (mo2->type != MT_AXIS)
continue;
if (dist1 < dist2)
{
closestaxis = mo2;
lowestaxisnum = mo2->health;
dist2 = dist1;
}
}
if (mo2->threshold != mare)
continue;
if (closestaxis == NULL)
{
closestaxis = mo2;
lowestaxisnum = mo2->health;
dist2 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y) - mo2->radius;
}
else if (mo2->health < lowestaxisnum)
{
dist1 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y) - mo2->radius;
if (dist1 < dist2)
{
closestaxis = mo2;
lowestaxisnum = mo2->health;
dist2 = dist1;
}
}
}
@@ -508,9 +512,9 @@ static mobj_t *P_FindAxis(INT32 mare, INT32 axisnum)
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -519,11 +523,13 @@ static mobj_t *P_FindAxis(INT32 mare, INT32 axisnum)
if (!(mo2->flags2 & MF2_AXIS))
return NULL;
if (mo2->type == MT_AXIS)
{
if (mo2->health == axisnum && mo2->threshold == mare)
return mo2;
}
if (mo2->type != MT_AXIS)
continue;
if (mo2->health != axisnum)
continue;
if (mo2->threshold != mare)
continue;
return mo2;
}
return NULL;
@@ -541,9 +547,9 @@ static mobj_t *P_FindAxisTransfer(INT32 mare, INT32 axisnum, mobjtype_t type)
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -552,11 +558,13 @@ static mobj_t *P_FindAxisTransfer(INT32 mare, INT32 axisnum, mobjtype_t type)
if (!(mo2->flags2 & MF2_AXIS))
return NULL;
if (mo2->type == type)
{
if (mo2->health == axisnum && mo2->threshold == mare)
return mo2;
}
if (mo2->type != type)
continue;
if (mo2->health != axisnum)
continue;
if (mo2->threshold != mare)
continue;
return mo2;
}
return NULL;
@@ -580,32 +588,33 @@ void P_TransferToAxis(player_t *player, INT32 axisnum)
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_AXIS)
{
if (mo2->health == axisnum && mo2->threshold == mare)
{
if (closestaxis == NULL)
{
closestaxis = mo2;
dist2 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y)-mo2->radius;
}
else
{
dist1 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y)-mo2->radius;
if (mo2->type != MT_AXIS)
continue;
if (mo2->health != axisnum)
continue;
if (mo2->threshold != mare)
continue;
if (dist1 < dist2)
{
closestaxis = mo2;
dist2 = dist1;
}
}
if (closestaxis == NULL)
{
closestaxis = mo2;
dist2 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y) - mo2->radius;
}
else
{
dist1 = R_PointToDist2(player->mo->x, player->mo->y, mo2->x, mo2->y) - mo2->radius;
if (dist1 < dist2)
{
closestaxis = mo2;
dist2 = dist1;
}
}
}
@@ -679,9 +688,9 @@ static void P_DeNightserizePlayer(player_t *player)
}
// Check to see if the player should be killed.
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -1593,9 +1602,9 @@ void P_SpawnShieldOrb(player_t *player)
}
// blaze through the thinkers to see if an orb already exists!
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
shieldobj = (mobj_t *)th;
@@ -2813,7 +2822,7 @@ static void P_DoClimbing(player_t *player) // SRB2kart - unused
angle_t sideangle;
fixed_t dx, dy;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MAIN].next; think != &thlist[THINK_MAIN]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)T_Scroll)
continue;
@@ -3293,7 +3302,8 @@ static void P_DoTeeter(player_t *player) // SRB2kart - unused.
if (teeter) // only bother with objects as a last resort if you were already teetering
{
mobj_t *oldtmthing = tmthing;
tmthing = teeterer = player->mo;
teeterer = player->mo;
P_SetTarget(&tmthing, teeterer);
teeterxl = teeterxh = player->mo->x;
teeteryl = teeteryh = player->mo->y;
couldteeter = false;
@@ -3307,7 +3317,7 @@ static void P_DoTeeter(player_t *player) // SRB2kart - unused.
}
teeterdone:
teeter = solidteeter;
tmthing = oldtmthing; // restore old tmthing, goodness knows what the game does with this before mobj thinkers
P_SetTarget(&tmthing, oldtmthing); // restore old tmthing, goodness knows what the game does with this before mobj thinkers
}
}
if (teeter)
@@ -3366,10 +3376,16 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) // SRB2kart - unused.
mobj_t *mo = NULL;
player->pflags |= PF_ATTACKDOWN;
//if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring.
// goto firenormal; //code repetition sucks.
// Bounce ring
if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering])
if (mo)
{
if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING)
{
if (cv_netslingdelay.value && issimulation && (tic_t)cv_netsteadyplayers.value >= targetsimtic - simtic && mo->target == players[consoleplayer].mo)
// don't fire it yet
return;
const boolean nblockmap = !(mo->flags & MF_NOBLOCKMAP);
for (i = 0; i < 256; i++)
{
if (player->health <= 1)
return;
@@ -3620,11 +3636,177 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd) // SRB2kart - unused.
#endif
) && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING))
{
player->pflags |= PF_SPINNING;
//P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
if (!player->spectator)
S_StartSound(player->mo, sfx_spin);
player->pflags |= PF_USEDOWN;
case CA2_SPINDASH: // Spinning and Spindashing
// Start revving
if ((cmd->buttons & BT_USE) && player->speed < FixedMul(5<<FRACBITS, player->mo->scale)
&& !player->mo->momz && onground && !(player->pflags & (PF_USEDOWN|PF_SPINNING))
&& canstand)
{
player->mo->momx = player->cmomx;
player->mo->momy = player->cmomy;
player->pflags |= (PF_USEDOWN|PF_STARTDASH|PF_SPINNING);
player->dashspeed = player->mindash;
P_SetPlayerMobjState(player->mo, S_PLAY_SPINDASH);
if (!player->spectator)
S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
}
// Revving
else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH))
{
if (player->dashspeed < player->maxdash)
{
#define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash)
fixed_t soundcalculation = chargecalculation;
player->dashspeed += FRACUNIT;
if (!player->spectator && soundcalculation != chargecalculation)
S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
#undef chargecalculation
}
if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
{
P_SpawnSpinMobj(player, player->revitem);
if (demorecording)
G_GhostAddRev();
}
}
// If not moving up or down, and travelling faster than a speed of five while not holding
// down the spin button and not spinning.
// AKA Just go into a spin on the ground, you idiot. ;)
else if ((cmd->buttons & BT_USE || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20))
&& !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<<FRACBITS, player->mo->scale)
|| !canstand) && !(player->pflags & (PF_USEDOWN|PF_SPINNING)))
{
player->pflags |= (PF_USEDOWN|PF_SPINNING);
P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
if (!player->spectator)
S_StartSound(player->mo, sfx_spin);
}
else
// Catapult the player from a spindash rev!
if (onground && !(player->pflags & PF_USEDOWN) && (player->pflags & PF_STARTDASH) && (player->pflags & PF_SPINNING))
{
player->pflags &= ~PF_STARTDASH;
if (player->powers[pw_carry] == CR_BRAKGOOP)
player->dashspeed = 0;
if (!((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE))
{
if (player->dashspeed)
{
P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
P_InstaThrust(player->mo, player->mo->angle, (player->speed = FixedMul(player->dashspeed, player->mo->scale))); // catapult forward ho!!
}
else
{
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
player->pflags &= ~PF_SPINNING;
}
if (!player->spectator)
S_StartSound(player->mo, sfx_zoom);
}
player->dashspeed = 0;
}
break;
case CA2_GUNSLINGER:
if (!player->mo->momz && onground && !player->weapondelay && canstand)
{
if (player->speed > FixedMul(10<<FRACBITS, player->mo->scale))
{}
else
{
mobj_t *lockon = P_LookForEnemies(player, false, true);
if (lockon)
{
if (P_IsLocalPlayer(player)) // Only display it on your own view.
{
mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
P_SetTarget(&visual->target, lockon);
}
}
if ((cmd->buttons & BT_USE) && !(player->pflags & PF_USEDOWN))
{
mobj_t *bullet;
P_SetPlayerMobjState(player->mo, S_PLAY_FIRE);
#define zpos(posmo) (posmo->z + (posmo->height - mobjinfo[player->revitem].height)/2)
if (lockon)
{
player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->y);
bullet = P_SpawnPointMissile(player->mo, lockon->x, lockon->y, zpos(lockon), player->revitem, player->mo->x, player->mo->y, zpos(player->mo));
if (!demoplayback || P_AnalogMove(player))
{
if (player == &players[consoleplayer])
localangle = player->mo->angle;
else if (player == &players[secondarydisplayplayer])
localangle2 = player->mo->angle;
}
}
else
{
bullet = P_SpawnPointMissile(player->mo, player->mo->x + P_ReturnThrustX(NULL, player->mo->angle, FRACUNIT), player->mo->y + P_ReturnThrustY(NULL, player->mo->angle, FRACUNIT), zpos(player->mo), player->revitem, player->mo->x, player->mo->y, zpos(player->mo));
if (bullet)
{
bullet->flags &= ~MF_NOGRAVITY;
bullet->momx >>= 1;
bullet->momy >>= 1;
}
}
player->drawangle = player->mo->angle;
#undef zpos
P_SetTarget(&player->mo->tracer, NULL);
player->mo->momx >>= 1;
player->mo->momy >>= 1;
player->pflags |= PF_USEDOWN;
P_SetWeaponDelay(player, TICRATE/2);
}
}
}
break;
case CA2_MELEE: // Melee attack
if (player->panim != PA_ABILITY2 && (cmd->buttons & BT_USE)
&& !player->mo->momz && onground && !(player->pflags & PF_USEDOWN)
&& canstand)
{
P_ResetPlayer(player);
player->pflags |= PF_THOKKED;
#if 0
if ((player->charability == CA_TWINSPIN) && (player->speed > FixedMul(player->runspeed, player->mo->scale)))
{
P_DoJump(player, false);
player->pflags &= ~PF_STARTJUMP;
player->mo->momz = FixedMul(player->mo->momz, 3*FRACUNIT/2); // NOT 1.5 times the jump height, but 2.25 times.
P_SetPlayerMobjState(player->mo, S_PLAY_TWINSPIN);
S_StartSound(player->mo, sfx_s3k8b);
}
else
#endif
{
player->mo->z += P_MobjFlip(player->mo);
P_SetObjectMomZ(player->mo, player->mindash, false);
if (player->mo->eflags & MFE_UNDERWATER)
player->mo->momz >>= 1;
#if 0
if (FixedMul(player->speed, FINECOSINE(((player->mo->angle - R_PointToAngle2(0, 0, player->rmomx, player->rmomy)) >> ANGLETOFINESHIFT) & FINEMASK)) < FixedMul(player->maxdash, player->mo->scale))
#else
if (player->speed < FixedMul(player->maxdash, player->mo->scale))
#endif
{
player->drawangle = player->mo->angle;
P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->maxdash, player->mo->scale));
}
player->mo->momx += player->cmomx;
player->mo->momy += player->cmomy;
P_SetPlayerMobjState(player->mo, S_PLAY_MELEE);
S_StartSound(player->mo, sfx_s3k42);
}
player->pflags |= PF_USEDOWN;
}
break;
}
}
@@ -3706,9 +3888,9 @@ void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range)
if (player->powers[pw_super]) // increase range when super
range *= 2;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -4388,9 +4570,9 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags2 & MF2_AMBUSH ? -1 : 1);
// Find next waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -4398,24 +4580,24 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
// Axis things are only at beginning of list.
if (!(mo2->flags2 & MF2_AXIS))
break;
if (!(mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
continue;
if (mo2->threshold != sequence)
continue;
if ((mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE)
&& mo2->threshold == sequence)
if (player->pflags & PF_TRANSFERTOCLOSEST)
{
if (player->pflags & PF_TRANSFERTOCLOSEST)
{
if (mo2->health == player->axis1->health)
transfer1 = mo2;
else if (mo2->health == player->axis2->health)
transfer2 = mo2;
}
else
{
if (mo2->health == player->mo->target->health)
transfer1 = mo2;
else if (mo2->health == player->mo->target->health + 1)
transfer2 = mo2;
}
if (mo2->health == player->axis1->health)
transfer1 = mo2;
else if (mo2->health == player->axis2->health)
transfer2 = mo2;
}
else
{
if (mo2->health == player->mo->target->health)
transfer1 = mo2;
else if (mo2->health == player->mo->target->health + 1)
transfer2 = mo2;
}
}
@@ -4424,9 +4606,9 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
// Look for a wrapper point.
if (!transfer1)
{
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -4434,27 +4616,28 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
// Axis things are only at beginning of list.
if (!(mo2->flags2 & MF2_AXIS))
break;
if (!(mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
continue;
if (mo2->threshold != sequence)
continue;
if (mo2->threshold == sequence && (mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
if (!transfer1)
{
if (!transfer1)
{
transfer1 = mo2;
transfer1last = true;
}
else if (mo2->health > transfer1->health)
{
transfer1 = mo2;
transfer1last = true;
}
transfer1 = mo2;
transfer1last = true;
}
else if (mo2->health > transfer1->health)
{
transfer1 = mo2;
transfer1last = true;
}
}
}
if (!transfer2)
{
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -4462,19 +4645,20 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
// Axis things are only at beginning of list.
if (!(mo2->flags2 & MF2_AXIS))
break;
if (!(mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
continue;
if (mo2->threshold != sequence)
continue;
if (mo2->threshold == sequence && (mo2->type == MT_AXISTRANSFER || mo2->type == MT_AXISTRANSFERLINE))
if (!transfer2)
{
if (!transfer2)
{
transfer2 = mo2;
transfer2last = true;
}
else if (mo2->health > transfer2->health)
{
transfer2 = mo2;
transfer2last = true;
}
transfer2 = mo2;
transfer2last = true;
}
else if (mo2->health > transfer2->health)
{
transfer2 = mo2;
transfer2last = true;
}
}
}
@@ -4999,36 +5183,34 @@ static void P_NiGHTSMovement(player_t *player)
// scan the thinkers
// to find the closest axis point
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_AXIS)
{
if (mo2->threshold == player->mare)
{
if (closestaxis == NULL)
{
closestaxis = mo2;
dist2 = R_PointToDist2(newx, newy, mo2->x, mo2->y)-mo2->radius;
}
else
{
dist1 = R_PointToDist2(newx, newy, mo2->x, mo2->y)-mo2->radius;
if (mo2->type != MT_AXIS)
continue;
if (mo2->threshold != player->mare)
continue;
if (dist1 < dist2)
{
closestaxis = mo2;
dist2 = dist1;
}
}
if (closestaxis == NULL)
{
closestaxis = mo2;
dist2 = R_PointToDist2(newx, newy, mo2->x, mo2->y) - mo2->radius;
}
else
{
dist1 = R_PointToDist2(newx, newy, mo2->x, mo2->y) - mo2->radius;
if (dist1 < dist2)
{
closestaxis = mo2;
dist2 = dist1;
}
}
}
P_SetTarget(&player->mo->target, closestaxis);
}
@@ -5718,9 +5900,9 @@ static void P_MovePlayer(player_t *player)
thinker_t *th;
mobj_t *mo2;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -6279,7 +6461,20 @@ static void P_MovePlayer(player_t *player)
/* // SRB2kart - what's a shield? Never heard of it. Never happened. Nope.
if (cmd->buttons & BT_BRAKE) // Spin button effects
{
if (player->pflags & PF_JUMPED) // If the player is jumping
mobj_t *lockon = NULL;
if (!player->powers[pw_super] && player->powers[pw_shield] == SH_ATTRACT && !(player->pflags & PF_THOKKED))
{
if ((lockon = P_LookForEnemies(player, false, false)))
{
if (P_IsLocalPlayer(player)) // Only display it on your own view.
{
mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
P_SetTarget(&visual->target, lockon);
P_SetMobjStateNF(visual, visual->info->spawnstate+1);
}
}
}
if (cmd->buttons & BT_USE) // Spin button effects
{
if (!(player->pflags & PF_USEDOWN)) // If the player is not holding down BT_BRAKE
{
@@ -6535,9 +6730,9 @@ static void P_DoZoomTube(player_t *player)
CONS_Debug(DBG_GAMELOGIC, "Looking for next waypoint...\n");
// Find next waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -6545,15 +6740,17 @@ static void P_DoZoomTube(player_t *player)
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == sequence)
{
if ((reverse && mo2->health == player->mo->tracer->health - 1)
|| (!reverse && mo2->health == player->mo->tracer->health + 1))
{
waypoint = mo2;
break;
}
}
if (mo2->threshold != sequence)
continue;
if (reverse && mo2->health != player->mo->tracer->health - 1)
continue;
if (!reverse && mo2->health != player->mo->tracer->health + 1)
continue;
waypoint = mo2;
break;
}
if (waypoint)
@@ -6677,9 +6874,9 @@ static void P_DoRopeHang(player_t *player) // SRB2kart - unused.
CONS_Debug(DBG_GAMELOGIC, "Looking for next waypoint...\n");
// Find next waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -6687,14 +6884,14 @@ static void P_DoRopeHang(player_t *player) // SRB2kart - unused.
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == sequence)
{
if (mo2->health == player->mo->tracer->health + 1)
{
waypoint = mo2;
break;
}
}
if (mo2->threshold != sequence)
continue;
if (mo2->health != player->mo->tracer->health + 1)
continue;
waypoint = mo2;
break;
}
if (!(player->mo->tracer->flags & MF_SLIDEME) && !waypoint)
@@ -6702,9 +6899,9 @@ static void P_DoRopeHang(player_t *player) // SRB2kart - unused.
CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, wrapping to start...\n");
// Wrap around back to first waypoint
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -6712,14 +6909,14 @@ static void P_DoRopeHang(player_t *player) // SRB2kart - unused.
if (mo2->type != MT_TUBEWAYPOINT)
continue;
if (mo2->threshold == sequence)
{
if (mo2->health == 0)
{
waypoint = mo2;
break;
}
}
if (mo2->threshold != sequence)
continue;
if (mo2->health != 0)
continue;
waypoint = mo2;
break;
}
}
@@ -6770,25 +6967,22 @@ static void P_DoRopeHang(player_t *player) // SRB2kart - unused.
static void P_NukeAllPlayers(player_t *player)
{
mobj_t *mo;
thinker_t *think;
UINT8 i;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (i = 0; i < MAXPLAYERS; i++)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
mo = (mobj_t *)think;
if (!mo->player)
if (!playeringame[i])
continue;
if (players[i].spectator)
continue;
if (!players[i].mo)
continue;
if (players[i].mo == player->mo)
continue;
if (players[i].mo->health <= 0)
continue;
if (mo->health <= 0) // dead
continue;
if (mo == player->mo)
continue;
P_DamageMobj(mo, player->mo, player->mo, 1);
P_DamageMobj(players[i].mo, player->mo, player->mo, 1, 0);
}
CONS_Printf(M_GetText("%s caused a world of pain.\n"), player_names[player-players]);
@@ -6822,10 +7016,10 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
}
}
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)think;
@@ -6902,10 +7096,10 @@ boolean P_LookForEnemies(player_t *player)
mobj_t *closestmo = NULL;
angle_t an;
for (think = thinkercap.next; think != &thinkercap; think = think->next)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{
if (think->function.acp1 != (actionf_p1)P_MobjThinker)
continue; // not a mobj thinker
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo = (mobj_t *)think;
if (!(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)))
@@ -7027,9 +7221,9 @@ void P_FindEmerald(void)
// scan the remaining thinkers
// to find all emeralds
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
@@ -8355,9 +8549,9 @@ void P_PlayerThink(player_t *player)
fixed_t y = player->mo->y;
fixed_t z = player->mo->z;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
+2 -2
View File
@@ -1653,8 +1653,8 @@ void R_PrecacheLevel(void)
spritepresent = calloc(numsprites, sizeof (*spritepresent));
if (spritepresent == NULL) I_Error("%s: Out of memory looking up sprites", "R_PrecacheLevel");
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
if (th->function.acp1 != (actionf_p1)P_RemoveThinkerDelayed)
spritepresent[((mobj_t *)th)->sprite] = 1;
spritememory = 0;
+69 -29
View File
@@ -46,7 +46,7 @@ extern INT32 msg_id;
// 3D Sound Interface
#include "hardware/hw3sound.h"
#else
static INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *vol, INT32 *sep, INT32 *pitch, sfxinfo_t *sfxinfo);
static INT32 S_AdjustSoundParams(const mobj_t *listener, fixed_t x, fixed_t y, fixed_t z, INT32 *vol, INT32 *sep, INT32 *pitch, sfxinfo_t *sfxinfo);
#endif
CV_PossibleValue_t soundvolume_cons_t[] = {{0, "MIN"}, {31, "MAX"}, {0, NULL}};
@@ -154,19 +154,6 @@ consvar_t cv_playsoundifunfocused = {"playsoundsifunfocused", "No", CV_SAVE|CV_C
// percent attenuation from front to back
#define S_IFRACVOL 30
typedef struct
{
// sound information (if null, channel avail.)
sfxinfo_t *sfxinfo;
// origin of sound
const void *origin;
// handle of the sound being played
INT32 handle;
} channel_t;
// the set of channels available
static channel_t *channels = NULL;
static INT32 numofchannels = 0;
@@ -248,6 +235,7 @@ static INT32 S_getChannel(const void *origin, sfxinfo_t *sfxinfo)
// channel is decided to be cnum.
c->sfxinfo = sfxinfo;
c->origin = origin;
c->isdetached = false;
return cnum;
}
@@ -450,6 +438,9 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
if (sound_disabled || !sound_started)
return;
if (((simtic != targetsimtic - 1 && origin == listenmobj) || (origin != listenmobj && issimulation))) // local player sounds play immediately during simulations
return;
// Don't want a sound? Okay then...
if (sfx_id == sfx_None)
return;
@@ -583,7 +574,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
if (origin && origin != listenmobj2)
{
INT32 rc;
rc = S_AdjustSoundParams(listenmobj2, origin, &volume, &sep, &pitch, sfx);
rc = S_AdjustSoundParams(listenmobj2, origin->x, origin->y, origin->z, &volume, &sep, &pitch, sfx);
if (!rc)
goto dontplay; // Maybe the other player can hear it...
@@ -639,7 +630,7 @@ dontplay:
if (origin && origin != listenmobj3)
{
INT32 rc;
rc = S_AdjustSoundParams(listenmobj3, origin, &volume, &sep, &pitch, sfx);
rc = S_AdjustSoundParams(listenmobj3, origin->x, origin->y, origin->z , &volume, &sep, &pitch, sfx);
if (!rc)
goto dontplay3; // Maybe the other player can hear it...
@@ -695,7 +686,7 @@ dontplay3:
if (origin && origin != listenmobj4)
{
INT32 rc;
rc = S_AdjustSoundParams(listenmobj4, origin, &volume, &sep, &pitch, sfx);
rc = S_AdjustSoundParams(listenmobj4, origin->x, origin->y, origin->z, &volume, &sep, &pitch, sfx);
if (!rc)
goto dontplay4; // Maybe the other player can hear it...
@@ -749,7 +740,7 @@ dontplay4:
if (origin && origin != listenmobj)
{
INT32 rc;
rc = S_AdjustSoundParams(listenmobj, origin, &volume, &sep, &pitch, sfx);
rc = S_AdjustSoundParams(listenmobj, origin->x, origin->y, origin->z, &volume, &sep, &pitch, sfx);
if (!rc)
return;
@@ -1053,6 +1044,8 @@ void S_UpdateSounds(void)
{
if (I_SoundIsPlaying(c->handle))
{
boolean isspatial = c->origin || c->isdetached;
// initialize parameters
volume = 255; // 8 bits internal volume precision
pitch = NORM_PITCH;
@@ -1063,11 +1056,28 @@ void S_UpdateSounds(void)
// check non-local sounds for distance clipping
// or modify their params
if (c->origin && ((c->origin != players[consoleplayer].mo)
if (isspatial &&
(c->origin && ((c->origin != players[consoleplayer].mo)
|| (splitscreen && c->origin != players[displayplayers[1]].mo)
|| (splitscreen > 1 && c->origin != players[displayplayers[2]].mo)
|| (splitscreen > 2 && c->origin != players[displayplayers[3]].mo)))
|| (splitscreen > 2 && c->origin != players[displayplayers[3]].mo))))
{
fixed_t x, y, z;
const mobj_t *soundmobj = c->origin;
if (c->isdetached)
{
x = c->detachedx;
y = c->detachedy;
z = c->detachedz;
}
else
{
x = soundmobj->x;
y = soundmobj->y;
z = soundmobj->z;
}
// Whomever is closer gets the sound, but only in splitscreen.
if (splitscreen)
{
@@ -1102,25 +1112,25 @@ void S_UpdateSounds(void)
if (p == 1)
{
// Player 2 gets the sound
audible = S_AdjustSoundParams(listenmobj2, c->origin, &volume, &sep, &pitch,
audible = S_AdjustSoundParams(listenmobj2, x, y, z, &volume, &sep, &pitch,
c->sfxinfo);
}
else if (p == 2)
{
// Player 3 gets the sound
audible = S_AdjustSoundParams(listenmobj3, c->origin, &volume, &sep, &pitch,
audible = S_AdjustSoundParams(listenmobj3, x, y, z, &volume, &sep, &pitch,
c->sfxinfo);
}
else if (p == 3)
{
// Player 4 gets the sound
audible = S_AdjustSoundParams(listenmobj4, c->origin, &volume, &sep, &pitch,
audible = S_AdjustSoundParams(listenmobj4, x, y, z, &volume, &sep, &pitch,
c->sfxinfo);
}
else
{
// Player 1 gets the sound
audible = S_AdjustSoundParams(listenmobj, c->origin, &volume, &sep, &pitch,
audible = S_AdjustSoundParams(listenmobj, x, y, z, &volume, &sep, &pitch,
c->sfxinfo);
}
@@ -1133,7 +1143,7 @@ void S_UpdateSounds(void)
else if (listenmobj && !splitscreen)
{
// In the case of a single player, he or she always should get updated sound.
audible = S_AdjustSoundParams(listenmobj, c->origin, &volume, &sep, &pitch,
audible = S_AdjustSoundParams(listenmobj, x, y, z, &volume, &sep, &pitch,
c->sfxinfo);
if (audible)
@@ -1203,6 +1213,37 @@ static void S_StopChannel(INT32 cnum)
}
}
//
// S_DetachChannelFromSource
//
// Detaches a sound channel from the object it came from, leaving it loose
//
void S_DetachChannelsFromOrigin(void* origin)
{
int i;
mobj_t* originmobj = (mobj_t*)origin;
for (i = 0; i < numofchannels; i++)
{
if (channels[i].origin == origin)
{
// small hack: player-made sounds should stay hearable
if (originmobj->player != &players[displayplayers[0]] &&
originmobj->player != &players[displayplayers[1]] &&
originmobj->player != &players[displayplayers[2]] &&
originmobj->player != &players[displayplayers[3]]
)
{
channels[i].isdetached = true;
channels[i].detachedx = originmobj->x;
channels[i].detachedy = originmobj->y;
channels[i].detachedz = originmobj->z;
}
channels[i].origin = NULL;
}
}
}
//
// S_CalculateSoundDistance
//
@@ -1237,7 +1278,7 @@ fixed_t S_CalculateSoundDistance(fixed_t sx1, fixed_t sy1, fixed_t sz1, fixed_t
// If the sound is not audible, returns a 0.
// Otherwise, modifies parameters and returns 1.
//
INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *vol, INT32 *sep, INT32 *pitch,
INT32 S_AdjustSoundParams(const mobj_t *listener, fixed_t x, fixed_t y, fixed_t z, INT32 *vol, INT32 *sep, INT32 *pitch,
sfxinfo_t *sfxinfo)
{
fixed_t approx_dist;
@@ -1318,8 +1359,7 @@ INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *v
}
else
{
approx_dist = S_CalculateSoundDistance(listensource.x, listensource.y, listensource.z,
source->x, source->y, source->z);
approx_dist = S_CalculateSoundDistance(listensource.x, listensource.y, listensource.z, x, y, z);
}
// Ring loss, deaths, etc, should all be heard louder.
@@ -1337,7 +1377,7 @@ INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *v
return 0;
// angle of source to listener
angle = R_PointToAngle2(listensource.x, listensource.y, source->x, source->y);
angle = R_PointToAngle2(listensource.x, listensource.y, x, y);
if (angle > listensource.angle)
angle = angle - listensource.angle;
+38
View File
@@ -74,6 +74,40 @@ typedef struct {
angle_t angle;
} listener_t;
typedef struct
{
// sound information (if null, channel avail.)
sfxinfo_t *sfxinfo;
// origin of sound
const void *origin;
// handle of the sound being played
INT32 handle;
// whether the sound is detached from its source (sound panning should not be changed)
boolean isdetached;
// positioning for detached sounds
fixed_t detachedx, detachedy, detachedz;
} channel_t;
typedef struct {
channel_t *c;
sfxinfo_t *s;
UINT16 t;
UINT8 b;
} caption_t;
#define NUMCAPTIONS 8
#define MAXCAPTIONTICS (2*TICRATE)
#define CAPTIONFADETICS 20
extern caption_t closedcaptions[NUMCAPTIONS];
void S_StartCaption(sfxenum_t sfx_id, INT32 cnum, UINT16 lifespan);
void S_ResetCaptions(void);
// register sound vars and commands at game startup
void S_RegisterSoundStuff(void);
@@ -107,6 +141,10 @@ void S_StartSoundAtVolume(const void *origin, sfxenum_t sound_id, INT32 volume);
// Stop sound for thing at <origin>
void S_StopSound(void *origin);
// Detaches a sound from an object, leaving it in the air at the object's original position
void S_DetachChannelsFromOrigin(void* origin);
//
// Music Status
//
+1 -1
View File
@@ -409,7 +409,7 @@ void SCR_DisplayTicRate(void)
tic_t totaltics = 0;
const UINT8 *ticcntcolor = NULL;
for (i = lasttic + 1; i < TICRATE+lasttic && i < ontic; ++i)
for (i = lasttic + 1; i < TICRATE + lasttic && i < ontic; ++i)
fpsgraph[i % TICRATE] = false;
fpsgraph[ontic % TICRATE] = true;
+117 -39
View File
@@ -5,7 +5,7 @@
//
// Copyright (C) 1993-1996 by id Software, Inc.
// Portions Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2014-2018 by Sonic Team Junior.
// Copyright (C) 2014-2019 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
@@ -205,13 +205,12 @@ static void JoyReset(SDLJoyInfo_t *JoySet)
/** \brief First joystick up and running
*/
static INT32 joystick_started = 0;
static INT32 joystick_started = 0;
/** \brief SDL info about joystick 1
*/
SDLJoyInfo_t JoyInfo;
/** \brief Second joystick up and running
*/
static INT32 joystick2_started = 0;
@@ -547,7 +546,7 @@ static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co)
break;
case VK_RETURN:
entering_con_command = false;
// Fall through.
/* FALLTHRU */
default:
event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char
}
@@ -693,7 +692,7 @@ void I_StartupKeyboard (void)
void I_OutputMsg(const char *fmt, ...)
{
size_t len;
XBOXSTATIC char txt[8192];
char txt[8192];
va_list argptr;
va_start(argptr,fmt);
@@ -872,7 +871,6 @@ INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev)
return -1;
}
// Misleading function: updates device indices for all players BUT the one specified.
// Necessary for SDL_JOYDEVICEADDED events
void I_UpdateJoystickDeviceIndices(INT32 player)
@@ -2162,7 +2160,6 @@ void I_InitJoystick(void)
SDL_Joystick *newjoy = NULL;
//I_ShutdownJoystick();
//SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
if (M_CheckParm("-nojoy"))
return;
@@ -2186,7 +2183,7 @@ void I_InitJoystick(void)
if (cv_usejoystick.value)
newjoy = SDL_JoystickOpen(cv_usejoystick.value-1);
if (newjoy && (JoyInfo2.dev == newjoy || JoyInfo3.dev == newjoy || JoyInfo4.dev == newjoy)) // don't override an active device
if (newjoy && (JoyInfo2.dev == newjoy || JoyInfo3.dev == newjoy || JoyInfo4.dev == newjoy)) // don't override an active device
cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
else if (newjoy && joy_open(cv_usejoystick.value) != -1)
{
@@ -2212,13 +2209,13 @@ void I_InitJoystick2(void)
SDL_Joystick *newjoy = NULL;
//I_ShutdownJoystick2();
//SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
if (M_CheckParm("-nojoy"))
return;
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{
CONS_Printf("I_InitJoystick2()...\n");
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
@@ -2362,7 +2359,7 @@ INT32 I_NumJoys(void)
return numjoy;
}
static char joyname[255]; // MAX_PATH; joystick name is straight from the driver
static char joyname[255]; // joystick name is straight from the driver
const char *I_GetJoyName(INT32 joyindex)
{
@@ -2903,11 +2900,15 @@ ticcmd_t *I_BaseTiccmd4(void)
{
return &emptycmd4;
}
static int lastTimeFudge = -1;
extern consvar_t cv_timefudge;
#if defined (_WIN32)
static HMODULE winmm = NULL;
static DWORD starttickcount = 0; // hack for win2k time bug
static p_timeGetTime pfntimeGetTime = NULL;
static LARGE_INTEGER basetime = { {0, 0} };
static LARGE_INTEGER frequency; // use this if High Resolution timer is found
// ---------
// I_GetTime
@@ -2916,6 +2917,7 @@ static p_timeGetTime pfntimeGetTime = NULL;
// but lower precision on Windows NT
// ---------
tic_t I_GetTime(void)
{
tic_t newtics = 0;
@@ -2923,10 +2925,77 @@ tic_t I_GetTime(void)
if (!starttickcount) // high precision timer
{
LARGE_INTEGER currtime; // use only LowPart if high resolution counter is not available
static LARGE_INTEGER basetime = {{0, 0}};
// use this if High Resolution timer is found
static LARGE_INTEGER frequency;
if (!basetime.QuadPart)
{
if (!QueryPerformanceFrequency(&frequency))
frequency.QuadPart = 0;
else
QueryPerformanceCounter(&basetime);
}
if (frequency.LowPart && QueryPerformanceCounter(&currtime))
{
newtics = (INT32)((currtime.QuadPart - basetime.QuadPart) * NEWTICRATE / frequency.QuadPart);
}
else if (pfntimeGetTime)
{
currtime.LowPart = pfntimeGetTime();
if (!basetime.LowPart)
basetime.LowPart = currtime.LowPart;
newtics = (UINT32)((UINT64)(currtime.LowPart - basetime.LowPart)*NEWTICRATE/1000);
}
}
else
newtics = (UINT32)((UINT64)(GetTickCount() - starttickcount)*NEWTICRATE/1000);
// ADVICE TO OFFICIAL SRB2 DEV TEAM: in the official branch, some of the above lines of code divide by (1000/NEWTICRATE) when it could multiply by 1000 first and preserve precision.
// I recommend changing this because the QueryPerformanceCounter version in fact already does that, making an inconsistent result between timer types.
// If the server and client are using different timer types, this will cause jutter.
// It also messes with SRB2netplus's timer fudge, meaning that for a truly accurate timerfudge it needs to know which timer the server is using...
// Fudge the timer to sync better with online games. Uses multiply-first approach (more accurate)
if (cv_timefudge.value != lastTimeFudge)
{
if (frequency.QuadPart)
{
unsigned long long frame = basetime.QuadPart * NEWTICRATE / frequency.QuadPart;
if (cv_timefudge.value > lastTimeFudge)
{
frame--; // do not allow the same tic to play twice
}
basetime.QuadPart = frame * frequency.QuadPart / NEWTICRATE + frequency.QuadPart * cv_timefudge.value / TICRATE / 100;
}
if (starttickcount)
{
unsigned long long frame = starttickcount * NEWTICRATE / 1000;
if (cv_timefudge.value > lastTimeFudge)
{
frame--; // do not allow the same tic to play twice
}
starttickcount = (DWORD)(frame * 1000 / NEWTICRATE + 1000 * cv_timefudge.value / TICRATE / 100);
}
lastTimeFudge = cv_timefudge.value;
}
return newtics;
}
// I_GetTime
// Wow I remember Windows NT
// Anyway this returns the time passed in microseconds if applicable. Useful for benchmarking
UINT64 I_GetTimeUs(void)
{
UINT64 timeUs;
if (!starttickcount) // high precision timer
{
LARGE_INTEGER currtime; // use only LowPart if high resolution counter is not available
if (!basetime.LowPart)
{
@@ -2938,21 +3007,20 @@ tic_t I_GetTime(void)
if (frequency.LowPart && QueryPerformanceCounter(&currtime))
{
newtics = (INT32)((currtime.QuadPart - basetime.QuadPart) * NEWTICRATE
/ frequency.QuadPart);
timeUs = (INT32)((currtime.QuadPart - basetime.QuadPart) * 1000000 / frequency.QuadPart);
}
else if (pfntimeGetTime)
{
currtime.LowPart = pfntimeGetTime();
if (!basetime.LowPart)
basetime.LowPart = currtime.LowPart;
newtics = ((currtime.LowPart - basetime.LowPart)/(1000/NEWTICRATE));
timeUs = (currtime.LowPart - basetime.LowPart) * 1000;
}
}
else
newtics = (GetTickCount() - starttickcount)/(1000/NEWTICRATE);
timeUs = (GetTickCount() - starttickcount) * 1000;
return newtics;
return timeUs;
}
static void I_ShutdownTimer(void)
@@ -2960,7 +3028,7 @@ static void I_ShutdownTimer(void)
pfntimeGetTime = NULL;
if (winmm)
{
p_timeEndPeriod pfntimeEndPeriod = (p_timeEndPeriod)GetProcAddress(winmm, "timeEndPeriod");
p_timeEndPeriod pfntimeEndPeriod = (p_timeEndPeriod)(LPVOID)GetProcAddress(winmm, "timeEndPeriod");
if (pfntimeEndPeriod)
pfntimeEndPeriod(1);
FreeLibrary(winmm);
@@ -2972,14 +3040,29 @@ static void I_ShutdownTimer(void)
// I_GetTime
// returns time in 1/TICRATE second tics
//
static Uint64 basetime = 0;
tic_t I_GetTime (void)
{
static Uint64 basetime = 0;
Uint64 ticks = SDL_GetTicks();
Uint64 ticks = SDL_GetTicks();
if (!basetime)
basetime = ticks;
// fudge the timer for better netgame sync
if (cv_timefudge.value != lastTimeFudge)
{
Uint64 frame = basetime * NEWTICRATE / 1000;
if (cv_timefudge.value > lastTimeFudge)
{
frame--; // do not allow the same tic to play twice
}
basetime = (Uint64)(frame * 1000 / NEWTICRATE + 1000 * cv_timefudge.value / NEWTICRATE / 100);
lastTimeFudge = cv_timefudge.value;
}
ticks -= basetime;
ticks = (ticks*TICRATE);
@@ -2988,6 +3071,11 @@ tic_t I_GetTime (void)
return (tic_t)ticks;
}
UINT64 I_GetTimeUs(void)
{
return (SDL_GetTicks() - basetime) * 1000;
}
#endif
//
@@ -3005,10 +3093,10 @@ void I_StartupTimer(void)
winmm = LoadLibraryA("winmm.dll");
if (winmm)
{
p_timeEndPeriod pfntimeBeginPeriod = (p_timeEndPeriod)GetProcAddress(winmm, "timeBeginPeriod");
p_timeEndPeriod pfntimeBeginPeriod = (p_timeEndPeriod)(LPVOID)GetProcAddress(winmm, "timeBeginPeriod");
if (pfntimeBeginPeriod)
pfntimeBeginPeriod(1);
pfntimeGetTime = (p_timeGetTime)GetProcAddress(winmm, "timeGetTime");
pfntimeGetTime = (p_timeGetTime)(LPVOID)GetProcAddress(winmm, "timeGetTime");
}
I_AddExitFunc(I_ShutdownTimer);
#endif
@@ -3018,7 +3106,7 @@ void I_StartupTimer(void)
void I_Sleep(void)
{
if (cv_sleep.value > 0)
if (cv_sleep.value != -1)
SDL_Delay(cv_sleep.value);
}
@@ -3153,7 +3241,7 @@ void I_Error(const char *error, ...)
// which should fail gracefully if it can't put a message box up
// on the target system
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
"SRB2Kart "VERSIONSTRING" Recursive Error",
"SRB2 "VERSIONSTRING" Recursive Error",
buffer, NULL);
W_Shutdown();
@@ -3307,7 +3395,7 @@ void I_GetDiskFreeSpace(INT64 *freespace)
if (!testwin95)
{
pfnGetDiskFreeSpaceEx = (p_GetDiskFreeSpaceExA)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA");
pfnGetDiskFreeSpaceEx = (p_GetDiskFreeSpaceExA)(LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA");
testwin95 = true;
}
if (pfnGetDiskFreeSpaceEx)
@@ -3368,7 +3456,7 @@ char *I_GetUserName(void)
INT32 I_mkdir(const char *dirname, INT32 unixright)
{
//[segabor]
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) || defined (__CYGWIN__) || defined (__OS2__)
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) || defined (__CYGWIN__)
return mkdir(dirname, unixright);
#elif defined (_WIN32)
UNREFERENCED_PARAMETER(unixright); /// \todo should implement ntright under nt...
@@ -3507,7 +3595,7 @@ static const char *searchWad(const char *searchDir)
pathonly(tempsw);
return tempsw;
}
strcpy(tempsw, WADKEYWORD2);
fstemp = filesearch(tempsw, searchDir, NULL, true, 20);
if (fstemp == FS_FOUND)
@@ -3740,16 +3828,6 @@ UINT32 I_GetFreeMem(UINT32 *total)
if (total)
*total = (UINT32)info.dwTotalPhys;
return (UINT32)info.dwAvailPhys;
#elif defined (__OS2__)
UINT32 pr_arena;
if (total)
DosQuerySysInfo( QSV_TOTPHYSMEM, QSV_TOTPHYSMEM,
(PVOID) total, sizeof (UINT32));
DosQuerySysInfo( QSV_MAXPRMEM, QSV_MAXPRMEM,
(PVOID) &pr_arena, sizeof (UINT32));
return pr_arena;
#elif defined (__linux__)
/* Linux */
char buf[1024];
@@ -3827,7 +3905,7 @@ const CPUInfoFlags *I_CPUInfo(void)
#if defined (_WIN32)
static CPUInfoFlags WIN_CPUInfo;
SYSTEM_INFO SI;
p_IsProcessorFeaturePresent pfnCPUID = (p_IsProcessorFeaturePresent)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsProcessorFeaturePresent");
p_IsProcessorFeaturePresent pfnCPUID = (p_IsProcessorFeaturePresent)(LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsProcessorFeaturePresent");
ZeroMemory(&WIN_CPUInfo,sizeof (WIN_CPUInfo));
if (pfnCPUID)
+26
View File
@@ -194,17 +194,37 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
}
else // windowed mode
{
int xoffset = 0, yoffset = 0, arg;
if (wasfullscreen)
{
wasfullscreen = SDL_FALSE;
SDL_SetWindowFullscreen(window, 0);
}
// Reposition window only in windowed mode
SDL_SetWindowSize(window, width, height);
SDL_SetWindowPosition(window,
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)),
SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window))
);
// Offset the window w/ command line parameters, useful for multiplayer testing via batch files
if (arg = M_CheckParm("-winxoff"))
{
xoffset = atoi(arg + 1 < myargc ? myargv[arg + 1] : "0");
}
if (arg = M_CheckParm("-winyoff"))
{
yoffset = atoi(arg + 1 < myargc ? myargv[arg + 1] : "0");
}
if (xoffset || yoffset)
{
int x, y;
SDL_GetWindowPosition(window, &x, &y);
SDL_SetWindowPosition(window, x + xoffset, y + yoffset);
}
}
}
else
@@ -1294,8 +1314,14 @@ void I_StartupMouse(void)
//
// I_OsPolling
//
void SOCK_FlushDelayBuffers(boolean flush);
void I_OsPolling(void)
{
SDL_Keymod mod;
SOCK_FlushDelayBuffers(false);
if (consolevent)
I_GetConsoleEvents();
if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
+17 -15
View File
@@ -1762,31 +1762,33 @@ static void ST_doItemFinderIconsAndSound(void) // SRB2kart - unused.
return;
// Scan thinkers to find emblem mobj with these ids
for (th = thinkercap.next; th != &thinkercap; th = th->next)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
continue;
mo2 = (mobj_t *)th;
if (mo2->type == MT_EMBLEM)
if (mo2->type != MT_EMBLEM)
continue;
if (!(mo2->flags & MF_SPECIAL))
continue;
for (i = 0; i < stemblems; ++i)
{
if (!(mo2->flags & MF_SPECIAL))
continue;
for (i = 0; i < stemblems; ++i)
if (mo2->health == emblems[i] + 1)
{
if (mo2->health == emblems[i]+1)
{
soffset = (i * 20) - ((stemblems-1) * 10);
soffset = (i * 20) - ((stemblems - 1) * 10);
newinterval = ST_drawEmeraldHuntIcon(mo2, itemhoming, soffset);
if (newinterval && (!interval || newinterval < interval))
interval = newinterval;
newinterval = ST_drawEmeraldHuntIcon(mo2, itemhoming, soffset);
if (newinterval && (!interval || newinterval < interval))
interval = newinterval;
break;
}
break;
}
}
}
if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0)