mirror of
https://git.do.srb2.org/KartKrew/Kart-Public.git
synced 2025-01-14 13:51:31 +00:00
8f354ad9c1
Co-Authored-By: Sally Coolatta <tehrealsalt@gmail.com> Co-Authored-By: James R <justsomejames2@gmail.com> Co-Authored-By: Monster Iestyn <iestynjealous@ntlworld.com> Co-Authored-By: katsy <katmint@live.com> Place Frame Interpolation in "Experimental" video options header This seems like an appropriate way to describe the feature for now. Add smooth level platter under interpolation, `renderdeltatics` `renderdeltatics` can be used as a standard delta time in any place, allowing for smooth menus. It will always be equal to `realtics` when frame interpolation is turned off, producing consistent framerate behavior everywhere it is used. Add smooth rendering to save select screen Add smooth rendering to Record/NiGHTS Attack, F_SkyScroll Ensure viewsector is accurate to viewx/viewy This fixes a potential crash in OpenGL when changing between levels. Ensure + commands get executed before map start Always have precise_t defined Fix misc dropshadow issues Reset view interpolation on level load Remove unnecessary precipmobj thinker hack Add reset interpolation state functions Reset precip interpolation on snap to ceil Reset mobj interp state on TeleportMove Only swap view interp state if a tick is run Run anti-lag chasecam at tic frequency Fixes jittery and unstable chasecam in high latency netgames Homogenize mobj interpolations Add sector plane level interpolations Add SectorScroll interpolator Add SideScroll interpolator Add Polyobj interpolator Intialize interpolator list at a better time Delete interpolators associated with thinkers Interpolate mobj angles and player drawangle Interpolate HWR_DrawModel Add functions to handle interpolation Much less code duplication P_InitAngle, to fix angle interpolation on spawning objects Fully fix drop shadows It used the thing's floorz / ceilingz directly -- that wouldn't account for interpolated coordinates. Do not speed up underwater/heatwave effect in OpenGL Closer OpenGL underwater/heatwave effect to Software Interpolate from time of previous tic Previously interpolated from last 35th of a second, which may be offset from game time due to connection lag. Consider this the proper fix to 54148a0dd0 too. Calculate FPS stuff even if frame is skipped I decided ultimately to actually keep the frame skip optimization disabled, because I think it is actually a little bit helpful that you can still get accurate rendering perfstats while paused, however if we decide otherwise then we can have this optimization back without making the game act like it's lagging. Keep rect in memory Feel better about this than creating one all da time Lots of FPS stuff - Disabled VSync, due to the numerous problems it has. - Instead, added an FPS cap. - Frame interpolation is now tied to fpscap != 35. - By default, the FPS cap is set to the monitor's refresh rate. - Rewrote the FPS counter. (This also consolidates several more commits ahead of this fixing various issues. -eid) Misc changes after Kart cherry-picks Fix renderdeltatics with new timing data Update mobj oldstates before all thinkers Allow FPS cap values Adjust how FPS cap is checked to improve FPS stability Fix precip crash from missing vars Improve the framerate limiter's timing for extreme stable FPS Handle the sleep at the end of D_SRB2Loop instead of the start Simplifies logic in the other parts of the loop, and fixes problems with it frequently waiting too long. Reset mobj interp state on add Add mobj interpolator on load netgame Move mobj interpolators to r_fps Dynamic slope interpolators I_GetFrameTime to try and improve frame pace (It doesn't feel that much better though.) Move I_FinishUpdate to D_SRB2Loop to sync screen updates with FPS cap, use timestamps in I_FrameCapSleep to simplify the code Fix plane interpolation light level flickering Fix flickering plane interpolation for OpenGL in the exact same way Funny OpenGL renderer being at least 50% copy-pasted Software code :) P_SetOrigin & P_MoveOrigin to replace P_TeleportMove Convert P_TeleportMove use to origin funcs Revert "P_InitAngle, to fix angle interpolation on spawning objects" This reverts commit a80c98bd164a2748cbbfad9027b34601185d93f5. Waypoint polyobjects interpolate z & children Add interpolation to more moving plane types Adds interpolation to the following: - Crumbling platforms - Mario blocks - Floatbob platforms (this one works really strangely due to two thinkers, maybe double-check this one?) Reset overlays interp states each TryRunTics Interpolate model interpolation (lol) Use interp tracer pos for GL linkdraw Papersprite angle interpolation Makes the ending signpost smooth Move intermission emerald bounce to ticker Bring back shadows on polyobjects Also optimizes the method used so rings can show their shadows too. Using just the subsector is a tad bit imprecise admittedly but any more precise methods get really laggy. Fix a bunch of ticking in hu_ drawing functions Revert "Reset overlays interp states each TryRunTics" This reverts commit a71a216faa20e8751b3bd0157354e8d748940c92. Move intro ticking out of the drawer Adjust 1up monitor icon z offsets Fixes interpolation issues with 1up monitors. Delta time choose player menu animations Add drawerlib deltaTime function Interpolate afterimages further back Use old sleep in dedicated mode Clamp cechotimer to 0 Fixes issues with cechos staying on-screen and glitching out (NiGHTS items for example). Revert "Remove unnecessary precipmobj thinker hack" This reverts commit 0e38208620d19ec2ab690740438ac2fc7862a49e. Fix frame pacing when game lags behind The frame timestamp should've been made at the start of the frame, not the end. Fix I_FrameCapSleep not respecting cpusleep Jonathan Joestar bruh Allow dedicated to use precise sleep timing again Instead of only using one old sleep, just enforce framerate cap to match TICRATE. Make Lua TeleportMove call MoveOrigin Reset Metal fume interp state on appear Add interpdebug Put interpdebug stuff in perfstats instead Add timescale cvar Slow the game down to debug animations / interpolation problems! Speed it up if you need to get somewhere quickly while mapping! Enable timescale outside of DEVELOP builds It has NETVAR, so it should be fine -- put an end to useful debugging features excluded in multiplayer! Force interpolation when timescale != 1.0 Reset old_z in MT_LOCKON think Fixes interpolation artifacting due to spawn pos. Fix cutscenes in interp Fix boss1 laser in interp Interpolate mobj scale Precalculate refresh rate Slower PCs can have issue querying mode over and over. This might kinda suck for windowed mode if you have different refresh rate displays but oh well Fix interp scaling crashing software Reset interp scale when Lua sets .scale Disable angle interp on fresh mobjs Fix interp scale crash for hires sprites Interp shadow scales Copy interp state in P_SpawnMobjFromMobj Fix multiplayer character select Don't interpolate mobj state if frac = 1.0 Fix Mario block item placement Interpolate spritescale/offset x/y Fix offset copies for SpawnMobjFromMobj THANKS SAL Add Lua HUD drawlists Buffers draw calls between tics to ensure hooks run at the originally intended rate. Rename drawerlib deltaTime to getDeltaTime Make renderisnewtic is false between tics I know what I'm doing! I swear Completely refactor timing system Time is now tracked internally in the game using I_GetPreciseTime and I_UpdateTime. I_Time now pulls from this internal timer. The system code no longer needs to keep track of time itself. This significantly improves frame and tic timing in interp mode, resulting in a much smoother image with essentially no judder at any framerate. Ensure mobj interpolators reset on level load Ensure view is not interpolated on first frame Disable sprite offset interpolation (for now) Refactor timing code even more System layer is greatly simplified and framecap logic has been moved internally. I_Sleep now takes a sleep duration and I_SleepDuration generically implements a precise sleep with spin loop.
3491 lines
88 KiB
C
3491 lines
88 KiB
C
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
|
// Copyright (C) 1999-2018 by Sonic Team Junior.
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file p_saveg.c
|
|
/// \brief Archiving: SaveGame I/O
|
|
|
|
#include "doomdef.h"
|
|
#include "byteptr.h"
|
|
#include "d_main.h"
|
|
#include "doomstat.h"
|
|
#include "g_game.h"
|
|
#include "m_random.h"
|
|
#include "m_misc.h"
|
|
#include "p_local.h"
|
|
#include "p_setup.h"
|
|
#include "p_saveg.h"
|
|
#include "r_fps.h"
|
|
#include "r_things.h"
|
|
#include "r_state.h"
|
|
#include "w_wad.h"
|
|
#include "y_inter.h"
|
|
#include "z_zone.h"
|
|
#include "r_main.h"
|
|
#include "r_sky.h"
|
|
#include "p_polyobj.h"
|
|
#include "lua_script.h"
|
|
#include "p_slopes.h"
|
|
|
|
savedata_t savedata;
|
|
UINT8 *save_p;
|
|
|
|
// Block UINT32s to attempt to ensure that the correct data is
|
|
// being sent and received
|
|
#define ARCHIVEBLOCK_MISC 0x7FEEDEED
|
|
#define ARCHIVEBLOCK_PLAYERS 0x7F448008
|
|
#define ARCHIVEBLOCK_WORLD 0x7F8C08C0
|
|
#define ARCHIVEBLOCK_POBJS 0x7F928546
|
|
#define ARCHIVEBLOCK_THINKERS 0x7F37037C
|
|
#define ARCHIVEBLOCK_SPECIALS 0x7F228378
|
|
|
|
// Note: This cannot be bigger
|
|
// than an UINT16
|
|
typedef enum
|
|
{
|
|
// RFLAGPOINT = 0x01,
|
|
// BFLAGPOINT = 0x02,
|
|
CAPSULE = 0x04,
|
|
AWAYVIEW = 0x08,
|
|
FIRSTAXIS = 0x10,
|
|
SECONDAXIS = 0x20,
|
|
} player_saveflags;
|
|
|
|
//
|
|
// P_ArchivePlayer
|
|
//
|
|
static inline void P_ArchivePlayer(void)
|
|
{
|
|
const player_t *player = &players[consoleplayer];
|
|
INT32 pllives = player->lives;
|
|
if (pllives < 3) // Bump up to 3 lives if the player
|
|
pllives = 3; // has less than that.
|
|
|
|
WRITEUINT8(save_p, player->skincolor);
|
|
WRITEUINT8(save_p, player->skin);
|
|
|
|
WRITEUINT32(save_p, player->score);
|
|
WRITEINT32(save_p, pllives);
|
|
WRITEINT32(save_p, player->continues);
|
|
|
|
if (botskin)
|
|
{
|
|
WRITEUINT8(save_p, botskin);
|
|
WRITEUINT8(save_p, botcolor);
|
|
}
|
|
}
|
|
|
|
//
|
|
// P_UnArchivePlayer
|
|
//
|
|
static inline void P_UnArchivePlayer(void)
|
|
{
|
|
savedata.skincolor = READUINT8(save_p);
|
|
savedata.skin = READUINT8(save_p);
|
|
|
|
savedata.score = READINT32(save_p);
|
|
savedata.lives = READINT32(save_p);
|
|
savedata.continues = READINT32(save_p);
|
|
|
|
if (savedata.botcolor)
|
|
{
|
|
savedata.botskin = READUINT8(save_p);
|
|
if (savedata.botskin-1 >= numskins)
|
|
savedata.botskin = 0;
|
|
savedata.botcolor = READUINT8(save_p);
|
|
}
|
|
else
|
|
savedata.botskin = 0;
|
|
}
|
|
|
|
//
|
|
// P_NetArchivePlayers
|
|
//
|
|
static void P_NetArchivePlayers(void)
|
|
{
|
|
INT32 i, j;
|
|
UINT16 flags;
|
|
// size_t q;
|
|
|
|
WRITEUINT32(save_p, ARCHIVEBLOCK_PLAYERS);
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
flags = 0;
|
|
|
|
// no longer send ticcmds, player name, skin, or color
|
|
|
|
WRITEANGLE(save_p, players[i].aiming);
|
|
WRITEANGLE(save_p, players[i].awayviewaiming);
|
|
WRITEINT32(save_p, players[i].awayviewtics);
|
|
WRITEINT32(save_p, players[i].health);
|
|
|
|
WRITESINT8(save_p, players[i].pity);
|
|
WRITEINT32(save_p, players[i].currentweapon);
|
|
WRITEINT32(save_p, players[i].ringweapons);
|
|
|
|
for (j = 0; j < NUMPOWERS; j++)
|
|
WRITEUINT16(save_p, players[i].powers[j]);
|
|
for (j = 0; j < NUMKARTSTUFF; j++)
|
|
WRITEINT32(save_p, players[i].kartstuff[j]);
|
|
|
|
WRITEANGLE(save_p, players[i].frameangle);
|
|
|
|
WRITEUINT8(save_p, players[i].playerstate);
|
|
WRITEUINT32(save_p, players[i].pflags);
|
|
WRITEUINT8(save_p, players[i].panim);
|
|
WRITEUINT8(save_p, players[i].spectator);
|
|
|
|
WRITEUINT16(save_p, players[i].flashpal);
|
|
WRITEUINT16(save_p, players[i].flashcount);
|
|
|
|
WRITEUINT32(save_p, players[i].score);
|
|
WRITEFIXED(save_p, players[i].dashspeed);
|
|
WRITEINT32(save_p, players[i].dashtime);
|
|
WRITESINT8(save_p, players[i].lives);
|
|
WRITESINT8(save_p, players[i].continues);
|
|
WRITESINT8(save_p, players[i].xtralife);
|
|
WRITEUINT8(save_p, players[i].gotcontinue);
|
|
WRITEFIXED(save_p, players[i].speed);
|
|
WRITEUINT8(save_p, players[i].jumping);
|
|
WRITEUINT8(save_p, players[i].secondjump);
|
|
WRITEUINT8(save_p, players[i].fly1);
|
|
WRITEUINT8(save_p, players[i].scoreadd);
|
|
WRITEUINT32(save_p, players[i].glidetime);
|
|
WRITEUINT8(save_p, players[i].climbing);
|
|
WRITEINT32(save_p, players[i].deadtimer);
|
|
WRITEUINT32(save_p, players[i].exiting);
|
|
WRITEUINT8(save_p, players[i].homing);
|
|
WRITEUINT32(save_p, players[i].skidtime);
|
|
|
|
////////////////////////////
|
|
// Conveyor Belt Movement //
|
|
////////////////////////////
|
|
WRITEFIXED(save_p, players[i].cmomx); // Conveyor momx
|
|
WRITEFIXED(save_p, players[i].cmomy); // Conveyor momy
|
|
WRITEFIXED(save_p, players[i].rmomx); // "Real" momx (momx - cmomx)
|
|
WRITEFIXED(save_p, players[i].rmomy); // "Real" momy (momy - cmomy)
|
|
|
|
/////////////////////
|
|
// Race Mode Stuff //
|
|
/////////////////////
|
|
WRITEINT16(save_p, players[i].numboxes);
|
|
WRITEINT16(save_p, players[i].totalring);
|
|
WRITEUINT32(save_p, players[i].realtime);
|
|
WRITEUINT8(save_p, players[i].laps);
|
|
|
|
////////////////////
|
|
// CTF Mode Stuff //
|
|
////////////////////
|
|
WRITEINT32(save_p, players[i].ctfteam);
|
|
WRITEUINT16(save_p, players[i].gotflag);
|
|
|
|
WRITEINT32(save_p, players[i].weapondelay);
|
|
WRITEINT32(save_p, players[i].tossdelay);
|
|
|
|
WRITEUINT32(save_p, players[i].starposttime);
|
|
WRITEINT16(save_p, players[i].starpostx);
|
|
WRITEINT16(save_p, players[i].starposty);
|
|
WRITEINT16(save_p, players[i].starpostz);
|
|
WRITEINT32(save_p, players[i].starpostnum);
|
|
WRITEANGLE(save_p, players[i].starpostangle);
|
|
|
|
WRITEANGLE(save_p, players[i].angle_pos);
|
|
WRITEANGLE(save_p, players[i].old_angle_pos);
|
|
|
|
WRITEINT32(save_p, players[i].flyangle);
|
|
WRITEUINT32(save_p, players[i].drilltimer);
|
|
WRITEINT32(save_p, players[i].linkcount);
|
|
WRITEUINT32(save_p, players[i].linktimer);
|
|
WRITEINT32(save_p, players[i].anotherflyangle);
|
|
WRITEUINT32(save_p, players[i].nightstime);
|
|
WRITEUINT32(save_p, players[i].bumpertime);
|
|
WRITEINT32(save_p, players[i].drillmeter);
|
|
WRITEUINT8(save_p, players[i].drilldelay);
|
|
WRITEUINT8(save_p, players[i].bonustime);
|
|
WRITEUINT8(save_p, players[i].mare);
|
|
|
|
WRITEUINT32(save_p, players[i].marebegunat);
|
|
WRITEUINT32(save_p, players[i].startedtime);
|
|
WRITEUINT32(save_p, players[i].finishedtime);
|
|
WRITEINT16(save_p, players[i].finishedrings);
|
|
WRITEUINT32(save_p, players[i].marescore);
|
|
WRITEUINT32(save_p, players[i].lastmarescore);
|
|
WRITEUINT8(save_p, players[i].lastmare);
|
|
WRITEINT32(save_p, players[i].maxlink);
|
|
WRITEUINT8(save_p, players[i].texttimer);
|
|
WRITEUINT8(save_p, players[i].textvar);
|
|
|
|
if (players[i].capsule)
|
|
flags |= CAPSULE;
|
|
|
|
if (players[i].awayviewmobj)
|
|
flags |= AWAYVIEW;
|
|
|
|
if (players[i].axis1)
|
|
flags |= FIRSTAXIS;
|
|
|
|
if (players[i].axis2)
|
|
flags |= SECONDAXIS;
|
|
|
|
WRITEINT16(save_p, players[i].lastsidehit);
|
|
WRITEINT16(save_p, players[i].lastlinehit);
|
|
|
|
WRITEUINT32(save_p, players[i].losstime);
|
|
|
|
WRITEUINT8(save_p, players[i].timeshit);
|
|
|
|
WRITEINT32(save_p, players[i].onconveyor);
|
|
|
|
WRITEUINT32(save_p, players[i].jointime);
|
|
|
|
WRITEUINT8(save_p, players[i].splitscreenindex);
|
|
|
|
WRITEUINT16(save_p, flags);
|
|
|
|
if (flags & CAPSULE)
|
|
WRITEUINT32(save_p, players[i].capsule->mobjnum);
|
|
|
|
if (flags & FIRSTAXIS)
|
|
WRITEUINT32(save_p, players[i].axis1->mobjnum);
|
|
|
|
if (flags & SECONDAXIS)
|
|
WRITEUINT32(save_p, players[i].axis2->mobjnum);
|
|
|
|
if (flags & AWAYVIEW)
|
|
WRITEUINT32(save_p, players[i].awayviewmobj->mobjnum);
|
|
|
|
WRITEUINT32(save_p, players[i].charflags);
|
|
// SRB2kart
|
|
WRITEUINT8(save_p, players[i].kartspeed);
|
|
WRITEUINT8(save_p, players[i].kartweight);
|
|
//
|
|
|
|
for (j = 0; j < MAXPREDICTTICS; j++)
|
|
{
|
|
WRITEINT16(save_p, players[i].lturn_max[j]);
|
|
WRITEINT16(save_p, players[i].rturn_max[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// P_NetUnArchivePlayers
|
|
//
|
|
static void P_NetUnArchivePlayers(void)
|
|
{
|
|
INT32 i, j;
|
|
UINT16 flags;
|
|
|
|
if (READUINT32(save_p) != ARCHIVEBLOCK_PLAYERS)
|
|
I_Error("Bad $$$.sav at archive block Players");
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
// Do NOT memset player struct to 0
|
|
// other areas may initialize data elsewhere
|
|
//memset(&players[i], 0, sizeof (player_t));
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
// NOTE: sending tics should (hopefully) no longer be necessary
|
|
// sending player names, skin and color should not be necessary at all!
|
|
// (that data is handled in the server config now)
|
|
|
|
players[i].aiming = READANGLE(save_p);
|
|
players[i].awayviewaiming = READANGLE(save_p);
|
|
players[i].awayviewtics = READINT32(save_p);
|
|
players[i].health = READINT32(save_p);
|
|
|
|
players[i].pity = READSINT8(save_p);
|
|
players[i].currentweapon = READINT32(save_p);
|
|
players[i].ringweapons = READINT32(save_p);
|
|
|
|
for (j = 0; j < NUMPOWERS; j++)
|
|
players[i].powers[j] = READUINT16(save_p);
|
|
for (j = 0; j < NUMKARTSTUFF; j++)
|
|
players[i].kartstuff[j] = READINT32(save_p);
|
|
|
|
players[i].frameangle = READANGLE(save_p);
|
|
|
|
players[i].playerstate = READUINT8(save_p);
|
|
players[i].pflags = READUINT32(save_p);
|
|
players[i].panim = READUINT8(save_p);
|
|
players[i].spectator = READUINT8(save_p);
|
|
|
|
players[i].flashpal = READUINT16(save_p);
|
|
players[i].flashcount = READUINT16(save_p);
|
|
|
|
players[i].score = READUINT32(save_p);
|
|
players[i].dashspeed = READFIXED(save_p); // dashing speed
|
|
players[i].dashtime = READINT32(save_p); // dashing speed
|
|
players[i].lives = READSINT8(save_p);
|
|
players[i].continues = READSINT8(save_p); // continues that player has acquired
|
|
players[i].xtralife = READSINT8(save_p); // Ring Extra Life counter
|
|
players[i].gotcontinue = READUINT8(save_p); // got continue from stage
|
|
players[i].speed = READFIXED(save_p); // Player's speed (distance formula of MOMX and MOMY values)
|
|
players[i].jumping = READUINT8(save_p); // Jump counter
|
|
players[i].secondjump = READUINT8(save_p);
|
|
players[i].fly1 = READUINT8(save_p); // Tails flying
|
|
players[i].scoreadd = READUINT8(save_p); // Used for multiple enemy attack bonus
|
|
players[i].glidetime = READUINT32(save_p); // Glide counter for thrust
|
|
players[i].climbing = READUINT8(save_p); // Climbing on the wall
|
|
players[i].deadtimer = READINT32(save_p); // End game if game over lasts too long
|
|
players[i].exiting = READUINT32(save_p); // Exitlevel timer
|
|
players[i].homing = READUINT8(save_p); // Are you homing?
|
|
players[i].skidtime = READUINT32(save_p); // Skid timer
|
|
|
|
////////////////////////////
|
|
// Conveyor Belt Movement //
|
|
////////////////////////////
|
|
players[i].cmomx = READFIXED(save_p); // Conveyor momx
|
|
players[i].cmomy = READFIXED(save_p); // Conveyor momy
|
|
players[i].rmomx = READFIXED(save_p); // "Real" momx (momx - cmomx)
|
|
players[i].rmomy = READFIXED(save_p); // "Real" momy (momy - cmomy)
|
|
|
|
/////////////////////
|
|
// Race Mode Stuff //
|
|
/////////////////////
|
|
players[i].numboxes = READINT16(save_p); // Number of item boxes obtained for Race Mode
|
|
players[i].totalring = READINT16(save_p); // Total number of rings obtained for Race Mode
|
|
players[i].realtime = READUINT32(save_p); // integer replacement for leveltime
|
|
players[i].laps = READUINT8(save_p); // Number of laps (optional)
|
|
|
|
////////////////////
|
|
// CTF Mode Stuff //
|
|
////////////////////
|
|
players[i].ctfteam = READINT32(save_p); // 1 == Red, 2 == Blue
|
|
players[i].gotflag = READUINT16(save_p); // 1 == Red, 2 == Blue Do you have the flag?
|
|
|
|
players[i].weapondelay = READINT32(save_p);
|
|
players[i].tossdelay = READINT32(save_p);
|
|
|
|
players[i].starposttime = READUINT32(save_p);
|
|
players[i].starpostx = READINT16(save_p);
|
|
players[i].starposty = READINT16(save_p);
|
|
players[i].starpostz = READINT16(save_p);
|
|
players[i].starpostnum = READINT32(save_p);
|
|
players[i].starpostangle = READANGLE(save_p);
|
|
|
|
players[i].angle_pos = READANGLE(save_p);
|
|
players[i].old_angle_pos = READANGLE(save_p);
|
|
|
|
players[i].flyangle = READINT32(save_p);
|
|
players[i].drilltimer = READUINT32(save_p);
|
|
players[i].linkcount = READINT32(save_p);
|
|
players[i].linktimer = READUINT32(save_p);
|
|
players[i].anotherflyangle = READINT32(save_p);
|
|
players[i].nightstime = READUINT32(save_p);
|
|
players[i].bumpertime = READUINT32(save_p);
|
|
players[i].drillmeter = READINT32(save_p);
|
|
players[i].drilldelay = READUINT8(save_p);
|
|
players[i].bonustime = (boolean)READUINT8(save_p);
|
|
players[i].mare = READUINT8(save_p);
|
|
|
|
players[i].marebegunat = READUINT32(save_p);
|
|
players[i].startedtime = READUINT32(save_p);
|
|
players[i].finishedtime = READUINT32(save_p);
|
|
players[i].finishedrings = READINT16(save_p);
|
|
players[i].marescore = READUINT32(save_p);
|
|
players[i].lastmarescore = READUINT32(save_p);
|
|
players[i].lastmare = READUINT8(save_p);
|
|
players[i].maxlink = READINT32(save_p);
|
|
players[i].texttimer = READUINT8(save_p);
|
|
players[i].textvar = READUINT8(save_p);
|
|
|
|
players[i].lastsidehit = READINT16(save_p);
|
|
players[i].lastlinehit = READINT16(save_p);
|
|
|
|
players[i].losstime = READUINT32(save_p);
|
|
|
|
players[i].timeshit = READUINT8(save_p);
|
|
|
|
players[i].onconveyor = READINT32(save_p);
|
|
|
|
players[i].jointime = READUINT32(save_p);
|
|
|
|
players[i].splitscreenindex = READUINT8(save_p);
|
|
|
|
flags = READUINT16(save_p);
|
|
|
|
if (flags & CAPSULE)
|
|
players[i].capsule = (mobj_t *)(size_t)READUINT32(save_p);
|
|
|
|
if (flags & FIRSTAXIS)
|
|
players[i].axis1 = (mobj_t *)(size_t)READUINT32(save_p);
|
|
|
|
if (flags & SECONDAXIS)
|
|
players[i].axis2 = (mobj_t *)(size_t)READUINT32(save_p);
|
|
|
|
if (flags & AWAYVIEW)
|
|
players[i].awayviewmobj = (mobj_t *)(size_t)READUINT32(save_p);
|
|
|
|
players[i].viewheight = 32<<FRACBITS;
|
|
|
|
//SetPlayerSkinByNum(i, players[i].skin);
|
|
players[i].charflags = READUINT32(save_p);
|
|
// SRB2kart
|
|
players[i].kartspeed = READUINT8(save_p);
|
|
players[i].kartweight = READUINT8(save_p);
|
|
//
|
|
|
|
for (j = 0; j < MAXPREDICTTICS; j++)
|
|
{
|
|
players[i].lturn_max[j] = READINT16(save_p);
|
|
players[i].rturn_max[j] = READINT16(save_p);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SD_FLOORHT 0x01
|
|
#define SD_CEILHT 0x02
|
|
#define SD_FLOORPIC 0x04
|
|
#define SD_CEILPIC 0x08
|
|
#define SD_LIGHT 0x10
|
|
#define SD_SPECIAL 0x20
|
|
#define SD_DIFF2 0x40
|
|
#define SD_FFLOORS 0x80
|
|
|
|
// diff2 flags
|
|
#define SD_FXOFFS 0x01
|
|
#define SD_FYOFFS 0x02
|
|
#define SD_CXOFFS 0x04
|
|
#define SD_CYOFFS 0x08
|
|
#define SD_TAG 0x10
|
|
#define SD_FLOORANG 0x20
|
|
#define SD_CEILANG 0x40
|
|
#define SD_TAGLIST 0x80
|
|
|
|
#define LD_FLAG 0x01
|
|
#define LD_SPECIAL 0x02
|
|
#define LD_CLLCOUNT 0x04
|
|
#define LD_S1TEXOFF 0x08
|
|
#define LD_S1TOPTEX 0x10
|
|
#define LD_S1BOTTEX 0x20
|
|
#define LD_S1MIDTEX 0x40
|
|
#define LD_DIFF2 0x80
|
|
|
|
// diff2 flags
|
|
#define LD_S2TEXOFF 0x01
|
|
#define LD_S2TOPTEX 0x02
|
|
#define LD_S2BOTTEX 0x04
|
|
#define LD_S2MIDTEX 0x08
|
|
|
|
//
|
|
// P_NetArchiveWorld
|
|
//
|
|
static void P_NetArchiveWorld(void)
|
|
{
|
|
size_t i;
|
|
INT32 statsec = 0, statline = 0;
|
|
const line_t *li = lines;
|
|
const side_t *si;
|
|
UINT8 *put;
|
|
|
|
// reload the map just to see difference
|
|
mapsector_t *ms;
|
|
mapsidedef_t *msd;
|
|
maplinedef_t *mld;
|
|
const sector_t *ss = sectors;
|
|
UINT8 diff, diff2;
|
|
|
|
WRITEUINT32(save_p, ARCHIVEBLOCK_WORLD);
|
|
put = save_p;
|
|
|
|
if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
|
|
{ // HACK: Open wad file rather quickly so we can get the data from the relevant lumps
|
|
UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
|
|
filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
|
|
#define retrieve_mapdata(d, f)\
|
|
d = Z_Malloc((f)->size, PU_CACHE, NULL); \
|
|
M_Memcpy(d, wadData + (f)->filepos, (f)->size)
|
|
retrieve_mapdata(ms, fileinfo + ML_SECTORS);
|
|
retrieve_mapdata(mld, fileinfo + ML_LINEDEFS);
|
|
retrieve_mapdata(msd, fileinfo + ML_SIDEDEFS);
|
|
#undef retrieve_mapdata
|
|
Z_Free(wadData); // we're done with this now
|
|
}
|
|
else // phew it's just a WAD
|
|
{
|
|
ms = W_CacheLumpNum(lastloadedmaplumpnum+ML_SECTORS, PU_CACHE);
|
|
mld = W_CacheLumpNum(lastloadedmaplumpnum+ML_LINEDEFS, PU_CACHE);
|
|
msd = W_CacheLumpNum(lastloadedmaplumpnum+ML_SIDEDEFS, PU_CACHE);
|
|
}
|
|
|
|
for (i = 0; i < numsectors; i++, ss++, ms++)
|
|
{
|
|
diff = diff2 = 0;
|
|
if (ss->floorheight != SHORT(ms->floorheight)<<FRACBITS)
|
|
diff |= SD_FLOORHT;
|
|
if (ss->ceilingheight != SHORT(ms->ceilingheight)<<FRACBITS)
|
|
diff |= SD_CEILHT;
|
|
//
|
|
// flats
|
|
//
|
|
if (ss->floorpic != P_CheckLevelFlat(ms->floorpic))
|
|
diff |= SD_FLOORPIC;
|
|
if (ss->ceilingpic != P_CheckLevelFlat(ms->ceilingpic))
|
|
diff |= SD_CEILPIC;
|
|
|
|
if (ss->lightlevel != SHORT(ms->lightlevel))
|
|
diff |= SD_LIGHT;
|
|
if (ss->special != SHORT(ms->special))
|
|
diff |= SD_SPECIAL;
|
|
|
|
if (ss->floor_xoffs != ss->spawn_flr_xoffs)
|
|
diff2 |= SD_FXOFFS;
|
|
if (ss->floor_yoffs != ss->spawn_flr_yoffs)
|
|
diff2 |= SD_FYOFFS;
|
|
if (ss->ceiling_xoffs != ss->spawn_ceil_xoffs)
|
|
diff2 |= SD_CXOFFS;
|
|
if (ss->ceiling_yoffs != ss->spawn_ceil_yoffs)
|
|
diff2 |= SD_CYOFFS;
|
|
if (ss->floorpic_angle != ss->spawn_flrpic_angle)
|
|
diff2 |= SD_FLOORANG;
|
|
if (ss->ceilingpic_angle != ss->spawn_flrpic_angle)
|
|
diff2 |= SD_CEILANG;
|
|
|
|
if (ss->tag != SHORT(ms->tag))
|
|
diff2 |= SD_TAG;
|
|
if (ss->nexttag != ss->spawn_nexttag || ss->firsttag != ss->spawn_firsttag)
|
|
diff2 |= SD_TAGLIST;
|
|
|
|
// Check if any of the sector's FOFs differ from how they spawned
|
|
if (ss->ffloors)
|
|
{
|
|
ffloor_t *rover;
|
|
for (rover = ss->ffloors; rover; rover = rover->next)
|
|
{
|
|
if (rover->flags != rover->spawnflags
|
|
|| rover->alpha != rover->spawnalpha)
|
|
{
|
|
diff |= SD_FFLOORS; // we found an FOF that changed!
|
|
break; // don't bother checking for more, we do that later
|
|
}
|
|
}
|
|
}
|
|
|
|
if (diff2)
|
|
diff |= SD_DIFF2;
|
|
|
|
if (diff)
|
|
{
|
|
statsec++;
|
|
|
|
WRITEUINT16(put, i);
|
|
WRITEUINT8(put, diff);
|
|
if (diff & SD_DIFF2)
|
|
WRITEUINT8(put, diff2);
|
|
if (diff & SD_FLOORHT)
|
|
WRITEFIXED(put, ss->floorheight);
|
|
if (diff & SD_CEILHT)
|
|
WRITEFIXED(put, ss->ceilingheight);
|
|
if (diff & SD_FLOORPIC)
|
|
WRITEMEM(put, levelflats[ss->floorpic].name, 8);
|
|
if (diff & SD_CEILPIC)
|
|
WRITEMEM(put, levelflats[ss->ceilingpic].name, 8);
|
|
if (diff & SD_LIGHT)
|
|
WRITEINT16(put, ss->lightlevel);
|
|
if (diff & SD_SPECIAL)
|
|
WRITEINT16(put, ss->special);
|
|
if (diff2 & SD_FXOFFS)
|
|
WRITEFIXED(put, ss->floor_xoffs);
|
|
if (diff2 & SD_FYOFFS)
|
|
WRITEFIXED(put, ss->floor_yoffs);
|
|
if (diff2 & SD_CXOFFS)
|
|
WRITEFIXED(put, ss->ceiling_xoffs);
|
|
if (diff2 & SD_CYOFFS)
|
|
WRITEFIXED(put, ss->ceiling_yoffs);
|
|
if (diff2 & SD_TAG) // save only the tag
|
|
WRITEINT16(put, ss->tag);
|
|
if (diff2 & SD_FLOORANG)
|
|
WRITEANGLE(put, ss->floorpic_angle);
|
|
if (diff2 & SD_CEILANG)
|
|
WRITEANGLE(put, ss->ceilingpic_angle);
|
|
if (diff2 & SD_TAGLIST) // save both firsttag and nexttag
|
|
{ // either of these could be changed even if tag isn't
|
|
WRITEINT32(put, ss->firsttag);
|
|
WRITEINT32(put, ss->nexttag);
|
|
}
|
|
|
|
// Special case: save the stats of all modified ffloors along with their ffloor "number"s
|
|
// we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed
|
|
if (diff & SD_FFLOORS)
|
|
{
|
|
size_t j = 0; // ss->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc
|
|
ffloor_t *rover;
|
|
UINT8 fflr_diff;
|
|
for (rover = ss->ffloors; rover; rover = rover->next)
|
|
{
|
|
fflr_diff = 0; // reset diff flags
|
|
if (rover->flags != rover->spawnflags)
|
|
fflr_diff |= 1;
|
|
if (rover->alpha != rover->spawnalpha)
|
|
fflr_diff |= 2;
|
|
|
|
if (fflr_diff)
|
|
{
|
|
WRITEUINT16(put, j); // save ffloor "number"
|
|
WRITEUINT8(put, fflr_diff);
|
|
if (fflr_diff & 1)
|
|
WRITEUINT32(put, rover->flags);
|
|
if (fflr_diff & 2)
|
|
WRITEINT16(put, rover->alpha);
|
|
}
|
|
j++;
|
|
}
|
|
WRITEUINT16(put, 0xffff);
|
|
}
|
|
}
|
|
}
|
|
|
|
WRITEUINT16(put, 0xffff);
|
|
|
|
// do lines
|
|
for (i = 0; i < numlines; i++, mld++, li++)
|
|
{
|
|
diff = diff2 = 0;
|
|
|
|
if (li->special != SHORT(mld->special))
|
|
diff |= LD_SPECIAL;
|
|
|
|
if (SHORT(mld->special) == 321 || SHORT(mld->special) == 322) // only reason li->callcount would be non-zero is if either of these are involved
|
|
diff |= LD_CLLCOUNT;
|
|
|
|
if (li->sidenum[0] != 0xffff)
|
|
{
|
|
si = &sides[li->sidenum[0]];
|
|
if (si->textureoffset != SHORT(msd[li->sidenum[0]].textureoffset)<<FRACBITS)
|
|
diff |= LD_S1TEXOFF;
|
|
//SoM: 4/1/2000: Some textures are colormaps. Don't worry about invalid textures.
|
|
if (R_CheckTextureNumForName(msd[li->sidenum[0]].toptexture) != -1
|
|
&& si->toptexture != R_TextureNumForName(msd[li->sidenum[0]].toptexture))
|
|
diff |= LD_S1TOPTEX;
|
|
if (R_CheckTextureNumForName(msd[li->sidenum[0]].bottomtexture) != -1
|
|
&& si->bottomtexture != R_TextureNumForName(msd[li->sidenum[0]].bottomtexture))
|
|
diff |= LD_S1BOTTEX;
|
|
if (R_CheckTextureNumForName(msd[li->sidenum[0]].midtexture) != -1
|
|
&& si->midtexture != R_TextureNumForName(msd[li->sidenum[0]].midtexture))
|
|
diff |= LD_S1MIDTEX;
|
|
}
|
|
if (li->sidenum[1] != 0xffff)
|
|
{
|
|
si = &sides[li->sidenum[1]];
|
|
if (si->textureoffset != SHORT(msd[li->sidenum[1]].textureoffset)<<FRACBITS)
|
|
diff2 |= LD_S2TEXOFF;
|
|
if (R_CheckTextureNumForName(msd[li->sidenum[1]].toptexture) != -1
|
|
&& si->toptexture != R_TextureNumForName(msd[li->sidenum[1]].toptexture))
|
|
diff2 |= LD_S2TOPTEX;
|
|
if (R_CheckTextureNumForName(msd[li->sidenum[1]].bottomtexture) != -1
|
|
&& si->bottomtexture != R_TextureNumForName(msd[li->sidenum[1]].bottomtexture))
|
|
diff2 |= LD_S2BOTTEX;
|
|
if (R_CheckTextureNumForName(msd[li->sidenum[1]].midtexture) != -1
|
|
&& si->midtexture != R_TextureNumForName(msd[li->sidenum[1]].midtexture))
|
|
diff2 |= LD_S2MIDTEX;
|
|
if (diff2)
|
|
diff |= LD_DIFF2;
|
|
}
|
|
|
|
if (diff)
|
|
{
|
|
statline++;
|
|
WRITEINT16(put, i);
|
|
WRITEUINT8(put, diff);
|
|
if (diff & LD_DIFF2)
|
|
WRITEUINT8(put, diff2);
|
|
if (diff & LD_FLAG)
|
|
WRITEINT16(put, li->flags);
|
|
if (diff & LD_SPECIAL)
|
|
WRITEINT16(put, li->special);
|
|
if (diff & LD_CLLCOUNT)
|
|
WRITEINT16(put, li->callcount);
|
|
|
|
si = &sides[li->sidenum[0]];
|
|
if (diff & LD_S1TEXOFF)
|
|
WRITEFIXED(put, si->textureoffset);
|
|
if (diff & LD_S1TOPTEX)
|
|
WRITEINT32(put, si->toptexture);
|
|
if (diff & LD_S1BOTTEX)
|
|
WRITEINT32(put, si->bottomtexture);
|
|
if (diff & LD_S1MIDTEX)
|
|
WRITEINT32(put, si->midtexture);
|
|
|
|
si = &sides[li->sidenum[1]];
|
|
if (diff2 & LD_S2TEXOFF)
|
|
WRITEFIXED(put, si->textureoffset);
|
|
if (diff2 & LD_S2TOPTEX)
|
|
WRITEINT32(put, si->toptexture);
|
|
if (diff2 & LD_S2BOTTEX)
|
|
WRITEINT32(put, si->bottomtexture);
|
|
if (diff2 & LD_S2MIDTEX)
|
|
WRITEINT32(put, si->midtexture);
|
|
}
|
|
}
|
|
WRITEUINT16(put, 0xffff);
|
|
R_ClearTextureNumCache(false);
|
|
|
|
save_p = put;
|
|
}
|
|
|
|
//
|
|
// P_NetUnArchiveWorld
|
|
//
|
|
static void P_NetUnArchiveWorld(void)
|
|
{
|
|
UINT16 i;
|
|
line_t *li;
|
|
side_t *si;
|
|
UINT8 *get;
|
|
UINT8 diff, diff2;
|
|
|
|
if (READUINT32(save_p) != ARCHIVEBLOCK_WORLD)
|
|
I_Error("Bad $$$.sav at archive block World");
|
|
|
|
get = save_p;
|
|
|
|
for (;;)
|
|
{
|
|
i = READUINT16(get);
|
|
|
|
if (i == 0xffff)
|
|
break;
|
|
|
|
if (i > numsectors)
|
|
I_Error("Invalid sector number %u from server (expected end at %s)", i, sizeu1(numsectors));
|
|
|
|
diff = READUINT8(get);
|
|
if (diff & SD_DIFF2)
|
|
diff2 = READUINT8(get);
|
|
else
|
|
diff2 = 0;
|
|
|
|
if (diff & SD_FLOORHT)
|
|
sectors[i].floorheight = READFIXED(get);
|
|
if (diff & SD_CEILHT)
|
|
sectors[i].ceilingheight = READFIXED(get);
|
|
if (diff & SD_FLOORPIC)
|
|
{
|
|
sectors[i].floorpic = P_AddLevelFlatRuntime((char *)get);
|
|
get += 8;
|
|
}
|
|
if (diff & SD_CEILPIC)
|
|
{
|
|
sectors[i].ceilingpic = P_AddLevelFlatRuntime((char *)get);
|
|
get += 8;
|
|
}
|
|
if (diff & SD_LIGHT)
|
|
sectors[i].lightlevel = READINT16(get);
|
|
if (diff & SD_SPECIAL)
|
|
sectors[i].special = READINT16(get);
|
|
|
|
if (diff2 & SD_FXOFFS)
|
|
sectors[i].floor_xoffs = READFIXED(get);
|
|
if (diff2 & SD_FYOFFS)
|
|
sectors[i].floor_yoffs = READFIXED(get);
|
|
if (diff2 & SD_CXOFFS)
|
|
sectors[i].ceiling_xoffs = READFIXED(get);
|
|
if (diff2 & SD_CYOFFS)
|
|
sectors[i].ceiling_yoffs = READFIXED(get);
|
|
if (diff2 & SD_TAG)
|
|
sectors[i].tag = READINT16(get); // DON'T use P_ChangeSectorTag
|
|
if (diff2 & SD_TAGLIST)
|
|
{
|
|
sectors[i].firsttag = READINT32(get);
|
|
sectors[i].nexttag = READINT32(get);
|
|
}
|
|
if (diff2 & SD_FLOORANG)
|
|
sectors[i].floorpic_angle = READANGLE(get);
|
|
if (diff2 & SD_CEILANG)
|
|
sectors[i].ceilingpic_angle = READANGLE(get);
|
|
|
|
if (diff & SD_FFLOORS)
|
|
{
|
|
UINT16 j = 0; // number of current ffloor in loop
|
|
UINT16 fflr_i; // saved ffloor "number" of next modified ffloor
|
|
UINT16 fflr_diff; // saved ffloor diff
|
|
ffloor_t *rover;
|
|
|
|
rover = sectors[i].ffloors;
|
|
if (!rover) // it is assumed sectors[i].ffloors actually exists, but just in case...
|
|
I_Error("Sector does not have any ffloors!");
|
|
|
|
fflr_i = READUINT16(get); // get first modified ffloor's number ready
|
|
for (;;) // for some reason the usual for (rover = x; ...) thing doesn't work here?
|
|
{
|
|
if (fflr_i == 0xffff) // end of modified ffloors list, let's stop already
|
|
break;
|
|
// should NEVER need to be checked
|
|
//if (rover == NULL)
|
|
//break;
|
|
if (j != fflr_i) // this ffloor was not modified
|
|
{
|
|
j++;
|
|
rover = rover->next;
|
|
continue;
|
|
}
|
|
|
|
fflr_diff = READUINT8(get);
|
|
|
|
if (fflr_diff & 1)
|
|
rover->flags = READUINT32(get);
|
|
if (fflr_diff & 2)
|
|
rover->alpha = READINT16(get);
|
|
|
|
fflr_i = READUINT16(get); // get next ffloor "number" ready
|
|
|
|
j++;
|
|
rover = rover->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
i = READUINT16(get);
|
|
|
|
if (i == 0xffff)
|
|
break;
|
|
if (i > numlines)
|
|
I_Error("Invalid line number %u from server", i);
|
|
|
|
diff = READUINT8(get);
|
|
li = &lines[i];
|
|
|
|
if (diff & LD_DIFF2)
|
|
diff2 = READUINT8(get);
|
|
else
|
|
diff2 = 0;
|
|
if (diff & LD_FLAG)
|
|
li->flags = READINT16(get);
|
|
if (diff & LD_SPECIAL)
|
|
li->special = READINT16(get);
|
|
if (diff & LD_CLLCOUNT)
|
|
li->callcount = READINT16(get);
|
|
|
|
si = &sides[li->sidenum[0]];
|
|
if (diff & LD_S1TEXOFF)
|
|
si->textureoffset = READFIXED(get);
|
|
if (diff & LD_S1TOPTEX)
|
|
si->toptexture = READINT32(get);
|
|
if (diff & LD_S1BOTTEX)
|
|
si->bottomtexture = READINT32(get);
|
|
if (diff & LD_S1MIDTEX)
|
|
si->midtexture = READINT32(get);
|
|
|
|
si = &sides[li->sidenum[1]];
|
|
if (diff2 & LD_S2TEXOFF)
|
|
si->textureoffset = READFIXED(get);
|
|
if (diff2 & LD_S2TOPTEX)
|
|
si->toptexture = READINT32(get);
|
|
if (diff2 & LD_S2BOTTEX)
|
|
si->bottomtexture = READINT32(get);
|
|
if (diff2 & LD_S2MIDTEX)
|
|
si->midtexture = READINT32(get);
|
|
}
|
|
|
|
save_p = get;
|
|
}
|
|
|
|
//
|
|
// Thinkers
|
|
//
|
|
|
|
typedef enum
|
|
{
|
|
MD_SPAWNPOINT = 1,
|
|
MD_POS = 1<<1,
|
|
MD_TYPE = 1<<2,
|
|
MD_MOM = 1<<3,
|
|
MD_RADIUS = 1<<4,
|
|
MD_HEIGHT = 1<<5,
|
|
MD_FLAGS = 1<<6,
|
|
MD_HEALTH = 1<<7,
|
|
MD_RTIME = 1<<8,
|
|
MD_STATE = 1<<9,
|
|
MD_TICS = 1<<10,
|
|
MD_SPRITE = 1<<11,
|
|
MD_FRAME = 1<<12,
|
|
MD_EFLAGS = 1<<13,
|
|
MD_PLAYER = 1<<14,
|
|
MD_MOVEDIR = 1<<15,
|
|
MD_MOVECOUNT = 1<<16,
|
|
MD_THRESHOLD = 1<<17,
|
|
MD_LASTLOOK = 1<<18,
|
|
MD_TARGET = 1<<19,
|
|
MD_TRACER = 1<<20,
|
|
MD_FRICTION = 1<<21,
|
|
MD_MOVEFACTOR = 1<<22,
|
|
MD_FLAGS2 = 1<<23,
|
|
MD_FUSE = 1<<24,
|
|
MD_WATERTOP = 1<<25,
|
|
MD_WATERBOTTOM = 1<<26,
|
|
MD_SCALE = 1<<27,
|
|
MD_DSCALE = 1<<28,
|
|
MD_BLUEFLAG = 1<<29,
|
|
MD_REDFLAG = 1<<30,
|
|
MD_MORE = 1<<31
|
|
} mobj_diff_t;
|
|
|
|
typedef enum
|
|
{
|
|
MD2_CUSVAL = 1,
|
|
MD2_CVMEM = 1<<1,
|
|
MD2_SKIN = 1<<2,
|
|
MD2_COLOR = 1<<3,
|
|
MD2_SCALESPEED = 1<<4,
|
|
MD2_EXTVAL1 = 1<<5,
|
|
MD2_EXTVAL2 = 1<<6,
|
|
MD2_HNEXT = 1<<7,
|
|
MD2_HPREV = 1<<8,
|
|
MD2_COLORIZED = 1<<9,
|
|
MD2_WAYPOINTCAP = 1<<10
|
|
, MD2_SLOPE = 1<<11
|
|
} mobj_diff2_t;
|
|
|
|
typedef enum
|
|
{
|
|
tc_mobj,
|
|
tc_ceiling,
|
|
tc_floor,
|
|
tc_flash,
|
|
tc_strobe,
|
|
tc_glow,
|
|
tc_fireflicker,
|
|
tc_thwomp,
|
|
tc_camerascanner,
|
|
tc_elevator,
|
|
tc_continuousfalling,
|
|
tc_bouncecheese,
|
|
tc_startcrumble,
|
|
tc_marioblock,
|
|
tc_marioblockchecker,
|
|
tc_spikesector,
|
|
tc_floatsector,
|
|
tc_bridgethinker,
|
|
tc_crushceiling,
|
|
tc_scroll,
|
|
tc_friction,
|
|
tc_pusher,
|
|
tc_laserflash,
|
|
tc_lightfade,
|
|
tc_executor,
|
|
tc_raisesector,
|
|
tc_noenemies,
|
|
tc_eachtime,
|
|
tc_disappear,
|
|
tc_polyrotate, // haleyjd 03/26/06: polyobjects
|
|
tc_polymove,
|
|
tc_polywaypoint,
|
|
tc_polyslidedoor,
|
|
tc_polyswingdoor,
|
|
tc_polyflag,
|
|
tc_polydisplace,
|
|
tc_end
|
|
} specials_e;
|
|
|
|
static inline UINT32 SaveMobjnum(const mobj_t *mobj)
|
|
{
|
|
if (mobj) return mobj->mobjnum;
|
|
return 0;
|
|
}
|
|
|
|
static UINT32 SaveSector(const sector_t *sector)
|
|
{
|
|
if (sector) return (UINT32)(sector - sectors);
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
static UINT32 SaveLine(const line_t *line)
|
|
{
|
|
if (line) return (UINT32)(line - lines);
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
static inline UINT32 SavePlayer(const player_t *player)
|
|
{
|
|
if (player) return (UINT32)(player - players);
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
//
|
|
// SaveMobjThinker
|
|
//
|
|
// Saves a mobj_t thinker
|
|
//
|
|
static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const mobj_t *mobj = (const mobj_t *)th;
|
|
UINT32 diff;
|
|
UINT16 diff2;
|
|
|
|
// Ignore stationary hoops - these will be respawned from mapthings.
|
|
if (mobj->type == MT_HOOP)
|
|
return;
|
|
|
|
// These are NEVER saved.
|
|
if (mobj->type == MT_HOOPCOLLIDE)
|
|
return;
|
|
|
|
// This hoop has already been collected.
|
|
if (mobj->type == MT_HOOPCENTER && mobj->threshold == 4242)
|
|
return;
|
|
|
|
if (mobj->spawnpoint && mobj->info->doomednum != -1)
|
|
{
|
|
// spawnpoint is not modified but we must save it since it is an identifier
|
|
diff = MD_SPAWNPOINT;
|
|
|
|
if ((mobj->x != mobj->spawnpoint->x << FRACBITS) ||
|
|
(mobj->y != mobj->spawnpoint->y << FRACBITS) ||
|
|
(mobj->angle != FixedAngle(mobj->spawnpoint->angle*FRACUNIT)))
|
|
diff |= MD_POS;
|
|
|
|
if (mobj->info->doomednum != mobj->spawnpoint->type)
|
|
diff |= MD_TYPE;
|
|
}
|
|
else
|
|
diff = MD_POS | MD_TYPE; // not a map spawned thing so make it from scratch
|
|
|
|
diff2 = 0;
|
|
|
|
// not the default but the most probable
|
|
if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0)
|
|
diff |= MD_MOM;
|
|
if (mobj->radius != mobj->info->radius)
|
|
diff |= MD_RADIUS;
|
|
if (mobj->height != mobj->info->height)
|
|
diff |= MD_HEIGHT;
|
|
if (mobj->flags != mobj->info->flags)
|
|
diff |= MD_FLAGS;
|
|
if (mobj->flags2)
|
|
diff |= MD_FLAGS2;
|
|
if (mobj->health != mobj->info->spawnhealth)
|
|
diff |= MD_HEALTH;
|
|
if (mobj->reactiontime != mobj->info->reactiontime)
|
|
diff |= MD_RTIME;
|
|
if ((statenum_t)(mobj->state-states) != mobj->info->spawnstate)
|
|
diff |= MD_STATE;
|
|
if (mobj->tics != mobj->state->tics)
|
|
diff |= MD_TICS;
|
|
if (mobj->sprite != mobj->state->sprite)
|
|
diff |= MD_SPRITE;
|
|
if (mobj->frame != mobj->state->frame)
|
|
diff |= MD_FRAME;
|
|
if (mobj->anim_duration != (UINT16)mobj->state->var2)
|
|
diff |= MD_FRAME;
|
|
if (mobj->eflags)
|
|
diff |= MD_EFLAGS;
|
|
if (mobj->player)
|
|
diff |= MD_PLAYER;
|
|
|
|
if (mobj->movedir)
|
|
diff |= MD_MOVEDIR;
|
|
if (mobj->movecount)
|
|
diff |= MD_MOVECOUNT;
|
|
if (mobj->threshold)
|
|
diff |= MD_THRESHOLD;
|
|
if (mobj->lastlook != -1)
|
|
diff |= MD_LASTLOOK;
|
|
if (mobj->target)
|
|
diff |= MD_TARGET;
|
|
if (mobj->tracer)
|
|
diff |= MD_TRACER;
|
|
if (mobj->friction != ORIG_FRICTION)
|
|
diff |= MD_FRICTION;
|
|
if (mobj->movefactor != FRACUNIT) //if (mobj->movefactor != ORIG_FRICTION_FACTOR)
|
|
diff |= MD_MOVEFACTOR;
|
|
if (mobj->fuse)
|
|
diff |= MD_FUSE;
|
|
if (mobj->watertop)
|
|
diff |= MD_WATERTOP;
|
|
if (mobj->waterbottom)
|
|
diff |= MD_WATERBOTTOM;
|
|
if (mobj->scale != FRACUNIT)
|
|
diff |= MD_SCALE;
|
|
if (mobj->destscale != mobj->scale)
|
|
diff |= MD_DSCALE;
|
|
if (mobj->scalespeed != mapobjectscale/12)
|
|
diff2 |= MD2_SCALESPEED;
|
|
|
|
if (mobj == redflag)
|
|
diff |= MD_REDFLAG;
|
|
if (mobj == blueflag)
|
|
diff |= MD_BLUEFLAG;
|
|
|
|
if (mobj->cusval)
|
|
diff2 |= MD2_CUSVAL;
|
|
if (mobj->cvmem)
|
|
diff2 |= MD2_CVMEM;
|
|
if (mobj->color)
|
|
diff2 |= MD2_COLOR;
|
|
if (mobj->skin)
|
|
diff2 |= MD2_SKIN;
|
|
if (mobj->extravalue1)
|
|
diff2 |= MD2_EXTVAL1;
|
|
if (mobj->extravalue2)
|
|
diff2 |= MD2_EXTVAL2;
|
|
if (mobj->hnext)
|
|
diff2 |= MD2_HNEXT;
|
|
if (mobj->hprev)
|
|
diff2 |= MD2_HPREV;
|
|
if (mobj->standingslope)
|
|
diff2 |= MD2_SLOPE;
|
|
if (mobj->colorized)
|
|
diff2 |= MD2_COLORIZED;
|
|
if (mobj == waypointcap)
|
|
diff2 |= MD2_WAYPOINTCAP;
|
|
if (diff2 != 0)
|
|
diff |= MD_MORE;
|
|
|
|
// Scrap all of that. If we're a hoop center, this is ALL we're saving.
|
|
if (mobj->type == MT_HOOPCENTER)
|
|
diff = MD_SPAWNPOINT;
|
|
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, diff);
|
|
if (diff & MD_MORE)
|
|
WRITEUINT16(save_p, diff2);
|
|
|
|
// save pointer, at load time we will search this pointer to reinitilize pointers
|
|
WRITEUINT32(save_p, (size_t)mobj);
|
|
|
|
WRITEFIXED(save_p, mobj->z); // Force this so 3dfloor problems don't arise.
|
|
WRITEFIXED(save_p, mobj->floorz);
|
|
WRITEFIXED(save_p, mobj->ceilingz);
|
|
|
|
if (diff & MD_SPAWNPOINT)
|
|
{
|
|
size_t z;
|
|
|
|
for (z = 0; z < nummapthings; z++)
|
|
if (&mapthings[z] == mobj->spawnpoint)
|
|
WRITEUINT16(save_p, z);
|
|
if (mobj->type == MT_HOOPCENTER)
|
|
return;
|
|
}
|
|
|
|
if (diff & MD_TYPE)
|
|
WRITEUINT32(save_p, mobj->type);
|
|
if (diff & MD_POS)
|
|
{
|
|
WRITEFIXED(save_p, mobj->x);
|
|
WRITEFIXED(save_p, mobj->y);
|
|
WRITEANGLE(save_p, mobj->angle);
|
|
}
|
|
if (diff & MD_MOM)
|
|
{
|
|
WRITEFIXED(save_p, mobj->momx);
|
|
WRITEFIXED(save_p, mobj->momy);
|
|
WRITEFIXED(save_p, mobj->momz);
|
|
}
|
|
if (diff & MD_RADIUS)
|
|
WRITEFIXED(save_p, mobj->radius);
|
|
if (diff & MD_HEIGHT)
|
|
WRITEFIXED(save_p, mobj->height);
|
|
if (diff & MD_FLAGS)
|
|
WRITEUINT32(save_p, mobj->flags);
|
|
if (diff & MD_FLAGS2)
|
|
WRITEUINT32(save_p, mobj->flags2);
|
|
if (diff & MD_HEALTH)
|
|
WRITEINT32(save_p, mobj->health);
|
|
if (diff & MD_RTIME)
|
|
WRITEINT32(save_p, mobj->reactiontime);
|
|
if (diff & MD_STATE)
|
|
WRITEUINT16(save_p, mobj->state-states);
|
|
if (diff & MD_TICS)
|
|
WRITEINT32(save_p, mobj->tics);
|
|
if (diff & MD_SPRITE)
|
|
WRITEUINT16(save_p, mobj->sprite);
|
|
if (diff & MD_FRAME)
|
|
{
|
|
WRITEUINT32(save_p, mobj->frame);
|
|
WRITEUINT16(save_p, mobj->anim_duration);
|
|
}
|
|
if (diff & MD_EFLAGS)
|
|
WRITEUINT16(save_p, mobj->eflags);
|
|
if (diff & MD_PLAYER)
|
|
WRITEUINT8(save_p, mobj->player-players);
|
|
if (diff & MD_MOVEDIR)
|
|
WRITEANGLE(save_p, mobj->movedir);
|
|
if (diff & MD_MOVECOUNT)
|
|
WRITEINT32(save_p, mobj->movecount);
|
|
if (diff & MD_THRESHOLD)
|
|
WRITEINT32(save_p, mobj->threshold);
|
|
if (diff & MD_LASTLOOK)
|
|
WRITEINT32(save_p, mobj->lastlook);
|
|
if (diff & MD_TARGET)
|
|
WRITEUINT32(save_p, mobj->target->mobjnum);
|
|
if (diff & MD_TRACER)
|
|
WRITEUINT32(save_p, mobj->tracer->mobjnum);
|
|
if (diff & MD_FRICTION)
|
|
WRITEFIXED(save_p, mobj->friction);
|
|
if (diff & MD_MOVEFACTOR)
|
|
WRITEFIXED(save_p, mobj->movefactor);
|
|
if (diff & MD_FUSE)
|
|
WRITEINT32(save_p, mobj->fuse);
|
|
if (diff & MD_WATERTOP)
|
|
WRITEFIXED(save_p, mobj->watertop);
|
|
if (diff & MD_WATERBOTTOM)
|
|
WRITEFIXED(save_p, mobj->waterbottom);
|
|
if (diff & MD_SCALE)
|
|
WRITEFIXED(save_p, mobj->scale);
|
|
if (diff & MD_DSCALE)
|
|
WRITEFIXED(save_p, mobj->destscale);
|
|
if (diff2 & MD2_SCALESPEED)
|
|
WRITEFIXED(save_p, mobj->scalespeed);
|
|
if (diff2 & MD2_CUSVAL)
|
|
WRITEINT32(save_p, mobj->cusval);
|
|
if (diff2 & MD2_CVMEM)
|
|
WRITEINT32(save_p, mobj->cvmem);
|
|
if (diff2 & MD2_SKIN)
|
|
WRITEUINT8(save_p, (UINT8)((skin_t *)mobj->skin - skins));
|
|
if (diff2 & MD2_COLOR)
|
|
WRITEUINT8(save_p, mobj->color);
|
|
if (diff2 & MD2_EXTVAL1)
|
|
WRITEINT32(save_p, mobj->extravalue1);
|
|
if (diff2 & MD2_EXTVAL2)
|
|
WRITEINT32(save_p, mobj->extravalue2);
|
|
if (diff2 & MD2_HNEXT)
|
|
WRITEUINT32(save_p, mobj->hnext->mobjnum);
|
|
if (diff2 & MD2_HPREV)
|
|
WRITEUINT32(save_p, mobj->hprev->mobjnum);
|
|
if (diff2 & MD2_SLOPE)
|
|
WRITEUINT16(save_p, mobj->standingslope->id);
|
|
if (diff2 & MD2_COLORIZED)
|
|
WRITEUINT8(save_p, mobj->colorized);
|
|
|
|
WRITEUINT32(save_p, mobj->mobjnum);
|
|
}
|
|
|
|
//
|
|
// SaveSpecialLevelThinker
|
|
//
|
|
// Saves a levelspecthink_t thinker
|
|
//
|
|
static void SaveSpecialLevelThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const levelspecthink_t *ht = (const void *)th;
|
|
size_t i;
|
|
WRITEUINT8(save_p, type);
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
WRITEFIXED(save_p, ht->vars[i]); //var[16]
|
|
WRITEFIXED(save_p, ht->var2s[i]); //var[16]
|
|
}
|
|
WRITEUINT32(save_p, SaveLine(ht->sourceline));
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
}
|
|
|
|
//
|
|
// SaveCeilingThinker
|
|
//
|
|
// Saves a ceiling_t thinker
|
|
//
|
|
static void SaveCeilingThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const ceiling_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT8(save_p, ht->type);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEFIXED(save_p, ht->bottomheight);
|
|
WRITEFIXED(save_p, ht->topheight);
|
|
WRITEFIXED(save_p, ht->speed);
|
|
WRITEFIXED(save_p, ht->oldspeed);
|
|
WRITEFIXED(save_p, ht->delay);
|
|
WRITEFIXED(save_p, ht->delaytimer);
|
|
WRITEUINT8(save_p, ht->crush);
|
|
WRITEINT32(save_p, ht->texture);
|
|
WRITEINT32(save_p, ht->direction);
|
|
WRITEINT32(save_p, ht->tag);
|
|
WRITEINT32(save_p, ht->olddirection);
|
|
WRITEFIXED(save_p, ht->origspeed);
|
|
WRITEFIXED(save_p, ht->sourceline);
|
|
}
|
|
|
|
//
|
|
// SaveFloormoveThinker
|
|
//
|
|
// Saves a floormove_t thinker
|
|
//
|
|
static void SaveFloormoveThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const floormove_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT8(save_p, ht->type);
|
|
WRITEUINT8(save_p, ht->crush);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEINT32(save_p, ht->direction);
|
|
WRITEINT32(save_p, ht->texture);
|
|
WRITEFIXED(save_p, ht->floordestheight);
|
|
WRITEFIXED(save_p, ht->speed);
|
|
WRITEFIXED(save_p, ht->origspeed);
|
|
WRITEFIXED(save_p, ht->delay);
|
|
WRITEFIXED(save_p, ht->delaytimer);
|
|
}
|
|
|
|
//
|
|
// SaveLightflashThinker
|
|
//
|
|
// Saves a lightflash_t thinker
|
|
//
|
|
static void SaveLightflashThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const lightflash_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEINT32(save_p, ht->maxlight);
|
|
WRITEINT32(save_p, ht->minlight);
|
|
}
|
|
|
|
//
|
|
// SaveStrobeThinker
|
|
//
|
|
// Saves a strobe_t thinker
|
|
//
|
|
static void SaveStrobeThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const strobe_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEINT32(save_p, ht->count);
|
|
WRITEINT32(save_p, ht->minlight);
|
|
WRITEINT32(save_p, ht->maxlight);
|
|
WRITEINT32(save_p, ht->darktime);
|
|
WRITEINT32(save_p, ht->brighttime);
|
|
}
|
|
|
|
//
|
|
// SaveGlowThinker
|
|
//
|
|
// Saves a glow_t thinker
|
|
//
|
|
static void SaveGlowThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const glow_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEINT32(save_p, ht->minlight);
|
|
WRITEINT32(save_p, ht->maxlight);
|
|
WRITEINT32(save_p, ht->direction);
|
|
WRITEINT32(save_p, ht->speed);
|
|
}
|
|
//
|
|
// SaveFireflickerThinker
|
|
//
|
|
// Saves a fireflicker_t thinker
|
|
//
|
|
static inline void SaveFireflickerThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const fireflicker_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEINT32(save_p, ht->count);
|
|
WRITEINT32(save_p, ht->resetcount);
|
|
WRITEINT32(save_p, ht->maxlight);
|
|
WRITEINT32(save_p, ht->minlight);
|
|
}
|
|
//
|
|
// SaveElevatorThinker
|
|
//
|
|
// Saves a elevator_t thinker
|
|
//
|
|
static void SaveElevatorThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const elevator_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT8(save_p, ht->type);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEUINT32(save_p, SaveSector(ht->actionsector));
|
|
WRITEINT32(save_p, ht->direction);
|
|
WRITEFIXED(save_p, ht->floordestheight);
|
|
WRITEFIXED(save_p, ht->ceilingdestheight);
|
|
WRITEFIXED(save_p, ht->speed);
|
|
WRITEFIXED(save_p, ht->origspeed);
|
|
WRITEFIXED(save_p, ht->low);
|
|
WRITEFIXED(save_p, ht->high);
|
|
WRITEFIXED(save_p, ht->distance);
|
|
WRITEFIXED(save_p, ht->delay);
|
|
WRITEFIXED(save_p, ht->delaytimer);
|
|
WRITEFIXED(save_p, ht->floorwasheight);
|
|
WRITEFIXED(save_p, ht->ceilingwasheight);
|
|
WRITEUINT32(save_p, SavePlayer(ht->player)); // was dummy
|
|
WRITEUINT32(save_p, SaveLine(ht->sourceline));
|
|
}
|
|
|
|
//
|
|
// SaveScrollThinker
|
|
//
|
|
// Saves a scroll_t thinker
|
|
//
|
|
static inline void SaveScrollThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const scroll_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEFIXED(save_p, ht->dx);
|
|
WRITEFIXED(save_p, ht->dy);
|
|
WRITEINT32(save_p, ht->affectee);
|
|
WRITEINT32(save_p, ht->control);
|
|
WRITEFIXED(save_p, ht->last_height);
|
|
WRITEFIXED(save_p, ht->vdx);
|
|
WRITEFIXED(save_p, ht->vdy);
|
|
WRITEINT32(save_p, ht->accel);
|
|
WRITEINT32(save_p, ht->exclusive);
|
|
WRITEUINT8(save_p, ht->type);
|
|
}
|
|
|
|
//
|
|
// SaveFrictionThinker
|
|
//
|
|
// Saves a friction_t thinker
|
|
//
|
|
static inline void SaveFrictionThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const friction_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEINT32(save_p, ht->friction);
|
|
WRITEINT32(save_p, ht->movefactor);
|
|
WRITEINT32(save_p, ht->affectee);
|
|
WRITEINT32(save_p, ht->referrer);
|
|
WRITEUINT8(save_p, ht->roverfriction);
|
|
}
|
|
|
|
//
|
|
// SavePusherThinker
|
|
//
|
|
// Saves a pusher_t thinker
|
|
//
|
|
static inline void SavePusherThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const pusher_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT8(save_p, ht->type);
|
|
WRITEINT32(save_p, ht->x_mag);
|
|
WRITEINT32(save_p, ht->y_mag);
|
|
WRITEINT32(save_p, ht->magnitude);
|
|
WRITEINT32(save_p, ht->radius);
|
|
WRITEINT32(save_p, ht->x);
|
|
WRITEINT32(save_p, ht->y);
|
|
WRITEINT32(save_p, ht->z);
|
|
WRITEINT32(save_p, ht->affectee);
|
|
WRITEUINT8(save_p, ht->roverpusher);
|
|
WRITEINT32(save_p, ht->referrer);
|
|
WRITEINT32(save_p, ht->exclusive);
|
|
WRITEINT32(save_p, ht->slider);
|
|
}
|
|
|
|
//
|
|
// SaveLaserThinker
|
|
//
|
|
// Saves a laserthink_t thinker
|
|
//
|
|
static void SaveLaserThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const laserthink_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEUINT32(save_p, SaveSector(ht->sec));
|
|
WRITEUINT32(save_p, SaveLine(ht->sourceline));
|
|
}
|
|
|
|
//
|
|
// SaveLightlevelThinker
|
|
//
|
|
// Saves a lightlevel_t thinker
|
|
//
|
|
static void SaveLightlevelThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const lightlevel_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEINT32(save_p, ht->destlevel);
|
|
WRITEINT32(save_p, ht->speed);
|
|
}
|
|
|
|
//
|
|
// SaveExecutorThinker
|
|
//
|
|
// Saves a executor_t thinker
|
|
//
|
|
static void SaveExecutorThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const executor_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, SaveLine(ht->line));
|
|
WRITEUINT32(save_p, SaveMobjnum(ht->caller));
|
|
WRITEUINT32(save_p, SaveSector(ht->sector));
|
|
WRITEINT32(save_p, ht->timer);
|
|
}
|
|
|
|
//
|
|
// SaveDisappearThinker
|
|
//
|
|
// Saves a disappear_t thinker
|
|
//
|
|
static void SaveDisappearThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const disappear_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEUINT32(save_p, ht->appeartime);
|
|
WRITEUINT32(save_p, ht->disappeartime);
|
|
WRITEUINT32(save_p, ht->offset);
|
|
WRITEUINT32(save_p, ht->timer);
|
|
WRITEINT32(save_p, ht->affectee);
|
|
WRITEINT32(save_p, ht->sourceline);
|
|
WRITEINT32(save_p, ht->exists);
|
|
}
|
|
|
|
|
|
//
|
|
// SavePolyrotateThinker
|
|
//
|
|
// Saves a polyrotate_t thinker
|
|
//
|
|
static inline void SavePolyrotatetThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const polyrotate_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEINT32(save_p, ht->polyObjNum);
|
|
WRITEINT32(save_p, ht->speed);
|
|
WRITEINT32(save_p, ht->distance);
|
|
}
|
|
|
|
//
|
|
// SavePolymoveThinker
|
|
//
|
|
// Saves a polymovet_t thinker
|
|
//
|
|
static void SavePolymoveThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const polymove_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEINT32(save_p, ht->polyObjNum);
|
|
WRITEINT32(save_p, ht->speed);
|
|
WRITEFIXED(save_p, ht->momx);
|
|
WRITEFIXED(save_p, ht->momy);
|
|
WRITEINT32(save_p, ht->distance);
|
|
WRITEANGLE(save_p, ht->angle);
|
|
}
|
|
|
|
//
|
|
// SavePolywaypointThinker
|
|
//
|
|
// Saves a polywaypoint_t thinker
|
|
//
|
|
static void SavePolywaypointThinker(const thinker_t *th, UINT8 type)
|
|
{
|
|
const polywaypoint_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEINT32(save_p, ht->polyObjNum);
|
|
WRITEINT32(save_p, ht->speed);
|
|
WRITEINT32(save_p, ht->sequence);
|
|
WRITEINT32(save_p, ht->pointnum);
|
|
WRITEINT32(save_p, ht->direction);
|
|
WRITEUINT8(save_p, ht->comeback);
|
|
WRITEUINT8(save_p, ht->wrap);
|
|
WRITEUINT8(save_p, ht->continuous);
|
|
WRITEUINT8(save_p, ht->stophere);
|
|
WRITEFIXED(save_p, ht->diffx);
|
|
WRITEFIXED(save_p, ht->diffy);
|
|
WRITEFIXED(save_p, ht->diffz);
|
|
}
|
|
|
|
//
|
|
// SavePolyslidedoorThinker
|
|
//
|
|
// Saves a polyslidedoor_t thinker
|
|
//
|
|
static void SavePolyslidedoorThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const polyslidedoor_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEINT32(save_p, ht->polyObjNum);
|
|
WRITEINT32(save_p, ht->delay);
|
|
WRITEINT32(save_p, ht->delayCount);
|
|
WRITEINT32(save_p, ht->initSpeed);
|
|
WRITEINT32(save_p, ht->speed);
|
|
WRITEINT32(save_p, ht->initDistance);
|
|
WRITEINT32(save_p, ht->distance);
|
|
WRITEUINT32(save_p, ht->initAngle);
|
|
WRITEUINT32(save_p, ht->angle);
|
|
WRITEUINT32(save_p, ht->revAngle);
|
|
WRITEFIXED(save_p, ht->momx);
|
|
WRITEFIXED(save_p, ht->momy);
|
|
WRITEUINT8(save_p, ht->closing);
|
|
}
|
|
|
|
//
|
|
// SavePolyswingdoorThinker
|
|
//
|
|
// Saves a polyswingdoor_t thinker
|
|
//
|
|
static void SavePolyswingdoorThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const polyswingdoor_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEINT32(save_p, ht->polyObjNum);
|
|
WRITEINT32(save_p, ht->delay);
|
|
WRITEINT32(save_p, ht->delayCount);
|
|
WRITEINT32(save_p, ht->initSpeed);
|
|
WRITEINT32(save_p, ht->speed);
|
|
WRITEINT32(save_p, ht->initDistance);
|
|
WRITEINT32(save_p, ht->distance);
|
|
WRITEUINT8(save_p, ht->closing);
|
|
}
|
|
|
|
static void SavePolydisplaceThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const polydisplace_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
WRITEINT32(save_p, ht->polyObjNum);
|
|
WRITEUINT32(save_p, SaveSector(ht->controlSector));
|
|
WRITEFIXED(save_p, ht->dx);
|
|
WRITEFIXED(save_p, ht->dy);
|
|
WRITEFIXED(save_p, ht->oldHeights);
|
|
}
|
|
|
|
/*
|
|
//
|
|
// SaveWhatThinker
|
|
//
|
|
// Saves a what_t thinker
|
|
//
|
|
static inline void SaveWhatThinker(const thinker_t *th, const UINT8 type)
|
|
{
|
|
const what_t *ht = (const void *)th;
|
|
WRITEUINT8(save_p, type);
|
|
}
|
|
*/
|
|
|
|
//
|
|
// P_NetArchiveThinkers
|
|
//
|
|
//
|
|
static void P_NetArchiveThinkers(void)
|
|
{
|
|
const thinker_t *th;
|
|
UINT32 numsaved = 0;
|
|
|
|
WRITEUINT32(save_p, ARCHIVEBLOCK_THINKERS);
|
|
|
|
// save off the current thinkers
|
|
for (th = thinkercap.next; th != &thinkercap; th = th->next)
|
|
{
|
|
if (!(th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed
|
|
|| th->function.acp1 == (actionf_p1)P_NullPrecipThinker))
|
|
numsaved++;
|
|
|
|
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
|
|
{
|
|
SaveMobjThinker(th, tc_mobj);
|
|
continue;
|
|
}
|
|
#ifdef PARANOIA
|
|
else if (th->function.acp1 == (actionf_p1)P_NullPrecipThinker);
|
|
#endif
|
|
else if (th->function.acp1 == (actionf_p1)T_MoveCeiling)
|
|
{
|
|
SaveCeilingThinker(th, tc_ceiling);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_CrushCeiling)
|
|
{
|
|
SaveCeilingThinker(th, tc_crushceiling);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_MoveFloor)
|
|
{
|
|
SaveFloormoveThinker(th, tc_floor);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_LightningFlash)
|
|
{
|
|
SaveLightflashThinker(th, tc_flash);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_StrobeFlash)
|
|
{
|
|
SaveStrobeThinker(th, tc_strobe);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_Glow)
|
|
{
|
|
SaveGlowThinker(th, tc_glow);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_FireFlicker)
|
|
{
|
|
SaveFireflickerThinker(th, tc_fireflicker);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_MoveElevator)
|
|
{
|
|
SaveElevatorThinker(th, tc_elevator);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_ContinuousFalling)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_continuousfalling);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_ThwompSector)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_thwomp);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_NoEnemiesSector)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_noenemies);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_EachTimeThinker)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_eachtime);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_RaiseSector)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_raisesector);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_CameraScanner)
|
|
{
|
|
SaveElevatorThinker(th, tc_camerascanner);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_Scroll)
|
|
{
|
|
SaveScrollThinker(th, tc_scroll);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_Friction)
|
|
{
|
|
SaveFrictionThinker(th, tc_friction);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_Pusher)
|
|
{
|
|
SavePusherThinker(th, tc_pusher);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_BounceCheese)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_bouncecheese);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_StartCrumble)
|
|
{
|
|
SaveElevatorThinker(th, tc_startcrumble);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_MarioBlock)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_marioblock);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_MarioBlockChecker)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_marioblockchecker);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_SpikeSector)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_spikesector);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_FloatSector)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_floatsector);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_BridgeThinker)
|
|
{
|
|
SaveSpecialLevelThinker(th, tc_bridgethinker);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_LaserFlash)
|
|
{
|
|
SaveLaserThinker(th, tc_laserflash);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_LightFade)
|
|
{
|
|
SaveLightlevelThinker(th, tc_lightfade);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_ExecutorDelay)
|
|
{
|
|
SaveExecutorThinker(th, tc_executor);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_Disappear)
|
|
{
|
|
SaveDisappearThinker(th, tc_disappear);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_PolyObjRotate)
|
|
{
|
|
SavePolyrotatetThinker(th, tc_polyrotate);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_PolyObjMove)
|
|
{
|
|
SavePolymoveThinker(th, tc_polymove);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_PolyObjWaypoint)
|
|
{
|
|
SavePolywaypointThinker(th, tc_polywaypoint);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_PolyDoorSlide)
|
|
{
|
|
SavePolyslidedoorThinker(th, tc_polyslidedoor);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_PolyDoorSwing)
|
|
{
|
|
SavePolyswingdoorThinker(th, tc_polyswingdoor);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_PolyObjFlag)
|
|
{
|
|
SavePolymoveThinker(th, tc_polyflag);
|
|
continue;
|
|
}
|
|
else if (th->function.acp1 == (actionf_p1)T_PolyObjDisplace)
|
|
{
|
|
SavePolydisplaceThinker(th, tc_polydisplace);
|
|
continue;
|
|
}
|
|
#ifdef PARANOIA
|
|
else if (th->function.acv != P_RemoveThinkerDelayed) // wait garbage collection
|
|
I_Error("unknown thinker type %p", th->function.acp1);
|
|
#endif
|
|
}
|
|
|
|
CONS_Debug(DBG_NETPLAY, "%u thinkers saved\n", numsaved);
|
|
|
|
WRITEUINT8(save_p, tc_end);
|
|
}
|
|
|
|
// Now save the pointers, tracer and target, but at load time we must
|
|
// relink to this; the savegame contains the old position in the pointer
|
|
// field copyed in the info field temporarily, but finally we just search
|
|
// for the old position and relink to it.
|
|
mobj_t *P_FindNewPosition(UINT32 oldposition)
|
|
{
|
|
thinker_t *th;
|
|
mobj_t *mobj;
|
|
|
|
for (th = thinkercap.next; th != &thinkercap; th = th->next)
|
|
{
|
|
if (th->function.acp1 != (actionf_p1)P_MobjThinker)
|
|
continue;
|
|
|
|
mobj = (mobj_t *)th;
|
|
if (mobj->mobjnum == oldposition)
|
|
return mobj;
|
|
}
|
|
CONS_Debug(DBG_GAMELOGIC, "mobj not found\n");
|
|
return NULL;
|
|
}
|
|
|
|
static inline mobj_t *LoadMobj(UINT32 mobjnum)
|
|
{
|
|
if (mobjnum == 0) return NULL;
|
|
return (mobj_t *)(size_t)mobjnum;
|
|
}
|
|
|
|
static sector_t *LoadSector(UINT32 sector)
|
|
{
|
|
if (sector >= numsectors) return NULL;
|
|
return §ors[sector];
|
|
}
|
|
|
|
static line_t *LoadLine(UINT32 line)
|
|
{
|
|
if (line >= numlines) return NULL;
|
|
return &lines[line];
|
|
}
|
|
|
|
static inline player_t *LoadPlayer(UINT32 player)
|
|
{
|
|
if (player >= MAXPLAYERS) return NULL;
|
|
return &players[player];
|
|
}
|
|
|
|
//
|
|
// LoadMobjThinker
|
|
//
|
|
// Loads a mobj_t from a save game
|
|
//
|
|
static void LoadMobjThinker(actionf_p1 thinker)
|
|
{
|
|
thinker_t *next;
|
|
mobj_t *mobj;
|
|
UINT32 diff;
|
|
UINT16 diff2;
|
|
INT32 i;
|
|
fixed_t z, floorz, ceilingz;
|
|
|
|
diff = READUINT32(save_p);
|
|
if (diff & MD_MORE)
|
|
diff2 = READUINT16(save_p);
|
|
else
|
|
diff2 = 0;
|
|
|
|
next = (void *)(size_t)READUINT32(save_p);
|
|
|
|
z = READFIXED(save_p); // Force this so 3dfloor problems don't arise.
|
|
floorz = READFIXED(save_p);
|
|
ceilingz = READFIXED(save_p);
|
|
|
|
if (diff & MD_SPAWNPOINT)
|
|
{
|
|
UINT16 spawnpointnum = READUINT16(save_p);
|
|
|
|
if (mapthings[spawnpointnum].type == 1705 || mapthings[spawnpointnum].type == 1713) // NiGHTS Hoop special case
|
|
{
|
|
P_SpawnHoopsAndRings(&mapthings[spawnpointnum]);
|
|
return;
|
|
}
|
|
|
|
mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
|
|
|
|
mobj->spawnpoint = &mapthings[spawnpointnum];
|
|
mapthings[spawnpointnum].mobj = mobj;
|
|
}
|
|
else
|
|
mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
|
|
|
|
// declare this as a valid mobj as soon as possible.
|
|
mobj->thinker.function.acp1 = thinker;
|
|
|
|
mobj->z = z;
|
|
mobj->floorz = floorz;
|
|
mobj->ceilingz = ceilingz;
|
|
|
|
if (diff & MD_TYPE)
|
|
mobj->type = READUINT32(save_p);
|
|
else
|
|
{
|
|
for (i = 0; i < NUMMOBJTYPES; i++)
|
|
if (mobj->spawnpoint && mobj->spawnpoint->type == mobjinfo[i].doomednum)
|
|
break;
|
|
if (i == NUMMOBJTYPES)
|
|
{
|
|
if (mobj->spawnpoint)
|
|
CONS_Alert(CONS_ERROR, "Found mobj with unknown map thing type %d\n", mobj->spawnpoint->type);
|
|
else
|
|
CONS_Alert(CONS_ERROR, "Found mobj with unknown map thing type NULL\n");
|
|
I_Error("Savegame corrupted");
|
|
}
|
|
mobj->type = i;
|
|
}
|
|
mobj->info = &mobjinfo[mobj->type];
|
|
if (diff & MD_POS)
|
|
{
|
|
mobj->x = READFIXED(save_p);
|
|
mobj->y = READFIXED(save_p);
|
|
mobj->angle = READANGLE(save_p);
|
|
}
|
|
else
|
|
{
|
|
mobj->x = mobj->spawnpoint->x << FRACBITS;
|
|
mobj->y = mobj->spawnpoint->y << FRACBITS;
|
|
mobj->angle = FixedAngle(mobj->spawnpoint->angle*FRACUNIT);
|
|
}
|
|
if (diff & MD_MOM)
|
|
{
|
|
mobj->momx = READFIXED(save_p);
|
|
mobj->momy = READFIXED(save_p);
|
|
mobj->momz = READFIXED(save_p);
|
|
} // otherwise they're zero, and the memset took care of it
|
|
|
|
if (diff & MD_RADIUS)
|
|
mobj->radius = READFIXED(save_p);
|
|
else
|
|
mobj->radius = mobj->info->radius;
|
|
if (diff & MD_HEIGHT)
|
|
mobj->height = READFIXED(save_p);
|
|
else
|
|
mobj->height = mobj->info->height;
|
|
if (diff & MD_FLAGS)
|
|
mobj->flags = READUINT32(save_p);
|
|
else
|
|
mobj->flags = mobj->info->flags;
|
|
if (diff & MD_FLAGS2)
|
|
mobj->flags2 = READUINT32(save_p);
|
|
if (diff & MD_HEALTH)
|
|
mobj->health = READINT32(save_p);
|
|
else
|
|
mobj->health = mobj->info->spawnhealth;
|
|
if (diff & MD_RTIME)
|
|
mobj->reactiontime = READINT32(save_p);
|
|
else
|
|
mobj->reactiontime = mobj->info->reactiontime;
|
|
|
|
if (diff & MD_STATE)
|
|
mobj->state = &states[READUINT16(save_p)];
|
|
else
|
|
mobj->state = &states[mobj->info->spawnstate];
|
|
if (diff & MD_TICS)
|
|
mobj->tics = READINT32(save_p);
|
|
else
|
|
mobj->tics = mobj->state->tics;
|
|
if (diff & MD_SPRITE)
|
|
mobj->sprite = READUINT16(save_p);
|
|
else
|
|
mobj->sprite = mobj->state->sprite;
|
|
if (diff & MD_FRAME)
|
|
{
|
|
mobj->frame = READUINT32(save_p);
|
|
mobj->anim_duration = READUINT16(save_p);
|
|
}
|
|
else
|
|
{
|
|
mobj->frame = mobj->state->frame;
|
|
mobj->anim_duration = (UINT16)mobj->state->var2;
|
|
}
|
|
if (diff & MD_EFLAGS)
|
|
mobj->eflags = READUINT16(save_p);
|
|
if (diff & MD_PLAYER)
|
|
{
|
|
i = READUINT8(save_p);
|
|
mobj->player = &players[i];
|
|
mobj->player->mo = mobj;
|
|
// added for angle prediction
|
|
if (consoleplayer == i)
|
|
localangle[0] = mobj->angle;
|
|
if (displayplayers[1] == i)
|
|
localangle[1] = mobj->angle;
|
|
if (displayplayers[2] == i)
|
|
localangle[2] = mobj->angle;
|
|
if (displayplayers[3] == i)
|
|
localangle[3] = mobj->angle;
|
|
}
|
|
if (diff & MD_MOVEDIR)
|
|
mobj->movedir = READANGLE(save_p);
|
|
if (diff & MD_MOVECOUNT)
|
|
mobj->movecount = READINT32(save_p);
|
|
if (diff & MD_THRESHOLD)
|
|
mobj->threshold = READINT32(save_p);
|
|
if (diff & MD_LASTLOOK)
|
|
mobj->lastlook = READINT32(save_p);
|
|
else
|
|
mobj->lastlook = -1;
|
|
if (diff & MD_TARGET)
|
|
mobj->target = (mobj_t *)(size_t)READUINT32(save_p);
|
|
if (diff & MD_TRACER)
|
|
mobj->tracer = (mobj_t *)(size_t)READUINT32(save_p);
|
|
if (diff & MD_FRICTION)
|
|
mobj->friction = READFIXED(save_p);
|
|
else
|
|
mobj->friction = ORIG_FRICTION;
|
|
if (diff & MD_MOVEFACTOR)
|
|
mobj->movefactor = READFIXED(save_p);
|
|
else
|
|
mobj->movefactor = FRACUNIT; //mobj->movefactor = ORIG_FRICTION_FACTOR;
|
|
if (diff & MD_FUSE)
|
|
mobj->fuse = READINT32(save_p);
|
|
if (diff & MD_WATERTOP)
|
|
mobj->watertop = READFIXED(save_p);
|
|
if (diff & MD_WATERBOTTOM)
|
|
mobj->waterbottom = READFIXED(save_p);
|
|
if (diff & MD_SCALE)
|
|
mobj->scale = READFIXED(save_p);
|
|
else
|
|
mobj->scale = FRACUNIT;
|
|
if (diff & MD_DSCALE)
|
|
mobj->destscale = READFIXED(save_p);
|
|
else
|
|
mobj->destscale = mobj->scale;
|
|
if (diff2 & MD2_SCALESPEED)
|
|
mobj->scalespeed = READFIXED(save_p);
|
|
else
|
|
mobj->scalespeed = mapobjectscale/12;
|
|
if (diff2 & MD2_CUSVAL)
|
|
mobj->cusval = READINT32(save_p);
|
|
if (diff2 & MD2_CVMEM)
|
|
mobj->cvmem = READINT32(save_p);
|
|
if (diff2 & MD2_SKIN)
|
|
mobj->skin = &skins[READUINT8(save_p)];
|
|
if (diff2 & MD2_COLOR)
|
|
mobj->color = READUINT8(save_p);
|
|
if (diff2 & MD2_EXTVAL1)
|
|
mobj->extravalue1 = READINT32(save_p);
|
|
if (diff2 & MD2_EXTVAL2)
|
|
mobj->extravalue2 = READINT32(save_p);
|
|
if (diff2 & MD2_HNEXT)
|
|
mobj->hnext = (mobj_t *)(size_t)READUINT32(save_p);
|
|
if (diff2 & MD2_HPREV)
|
|
mobj->hprev = (mobj_t *)(size_t)READUINT32(save_p);
|
|
if (diff2 & MD2_SLOPE)
|
|
mobj->standingslope = P_SlopeById(READUINT16(save_p));
|
|
if (diff2 & MD2_COLORIZED)
|
|
mobj->colorized = READUINT8(save_p);
|
|
|
|
if (diff & MD_REDFLAG)
|
|
{
|
|
redflag = mobj;
|
|
rflagpoint = mobj->spawnpoint;
|
|
}
|
|
if (diff & MD_BLUEFLAG)
|
|
{
|
|
blueflag = mobj;
|
|
bflagpoint = mobj->spawnpoint;
|
|
}
|
|
|
|
// set sprev, snext, bprev, bnext, subsector
|
|
P_SetThingPosition(mobj);
|
|
|
|
mobj->mobjnum = READUINT32(save_p);
|
|
|
|
if (mobj->player)
|
|
{
|
|
if (mobj->eflags & MFE_VERTICALFLIP)
|
|
mobj->player->viewz = mobj->z + mobj->height - mobj->player->viewheight;
|
|
else
|
|
mobj->player->viewz = mobj->player->mo->z + mobj->player->viewheight;
|
|
}
|
|
|
|
P_AddThinker(&mobj->thinker);
|
|
|
|
if (diff2 & MD2_WAYPOINTCAP)
|
|
P_SetTarget(&waypointcap, mobj);
|
|
|
|
mobj->info = (mobjinfo_t *)next; // temporarily, set when leave this function
|
|
|
|
R_AddMobjInterpolator(mobj);
|
|
}
|
|
|
|
//
|
|
// LoadSpecialLevelThinker
|
|
//
|
|
// Loads a levelspecthink_t from a save game
|
|
//
|
|
// floorOrCeiling:
|
|
// 0 - Don't set
|
|
// 1 - Floor Only
|
|
// 2 - Ceiling Only
|
|
// 3 - Both
|
|
//
|
|
static void LoadSpecialLevelThinker(actionf_p1 thinker, UINT8 floorOrCeiling)
|
|
{
|
|
levelspecthink_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
size_t i;
|
|
ht->thinker.function.acp1 = thinker;
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
ht->vars[i] = READFIXED(save_p); //var[16]
|
|
ht->var2s[i] = READFIXED(save_p); //var[16]
|
|
}
|
|
ht->sourceline = LoadLine(READUINT32(save_p));
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
|
|
if (ht->sector)
|
|
{
|
|
if (floorOrCeiling & 2)
|
|
ht->sector->ceilingdata = ht;
|
|
if (floorOrCeiling & 1)
|
|
ht->sector->floordata = ht;
|
|
}
|
|
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadCeilingThinker
|
|
//
|
|
// Loads a ceiling_t from a save game
|
|
//
|
|
static void LoadCeilingThinker(actionf_p1 thinker)
|
|
{
|
|
ceiling_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->type = READUINT8(save_p);
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->bottomheight = READFIXED(save_p);
|
|
ht->topheight = READFIXED(save_p);
|
|
ht->speed = READFIXED(save_p);
|
|
ht->oldspeed = READFIXED(save_p);
|
|
ht->delay = READFIXED(save_p);
|
|
ht->delaytimer = READFIXED(save_p);
|
|
ht->crush = READUINT8(save_p);
|
|
ht->texture = READINT32(save_p);
|
|
ht->direction = READINT32(save_p);
|
|
ht->tag = READINT32(save_p);
|
|
ht->olddirection = READINT32(save_p);
|
|
ht->origspeed = READFIXED(save_p);
|
|
ht->sourceline = READFIXED(save_p);
|
|
if (ht->sector)
|
|
ht->sector->ceilingdata = ht;
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadFloormoveThinker
|
|
//
|
|
// Loads a floormove_t from a save game
|
|
//
|
|
static void LoadFloormoveThinker(actionf_p1 thinker)
|
|
{
|
|
floormove_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->type = READUINT8(save_p);
|
|
ht->crush = READUINT8(save_p);
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->direction = READINT32(save_p);
|
|
ht->texture = READINT32(save_p);
|
|
ht->floordestheight = READFIXED(save_p);
|
|
ht->speed = READFIXED(save_p);
|
|
ht->origspeed = READFIXED(save_p);
|
|
ht->delay = READFIXED(save_p);
|
|
ht->delaytimer = READFIXED(save_p);
|
|
if (ht->sector)
|
|
ht->sector->floordata = ht;
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadLightflashThinker
|
|
//
|
|
// Loads a lightflash_t from a save game
|
|
//
|
|
static void LoadLightflashThinker(actionf_p1 thinker)
|
|
{
|
|
lightflash_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->maxlight = READINT32(save_p);
|
|
ht->minlight = READINT32(save_p);
|
|
if (ht->sector)
|
|
ht->sector->lightingdata = ht;
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadStrobeThinker
|
|
//
|
|
// Loads a strobe_t from a save game
|
|
//
|
|
static void LoadStrobeThinker(actionf_p1 thinker)
|
|
{
|
|
strobe_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->count = READINT32(save_p);
|
|
ht->minlight = READINT32(save_p);
|
|
ht->maxlight = READINT32(save_p);
|
|
ht->darktime = READINT32(save_p);
|
|
ht->brighttime = READINT32(save_p);
|
|
if (ht->sector)
|
|
ht->sector->lightingdata = ht;
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadGlowThinker
|
|
//
|
|
// Loads a glow_t from a save game
|
|
//
|
|
static void LoadGlowThinker(actionf_p1 thinker)
|
|
{
|
|
glow_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->minlight = READINT32(save_p);
|
|
ht->maxlight = READINT32(save_p);
|
|
ht->direction = READINT32(save_p);
|
|
ht->speed = READINT32(save_p);
|
|
if (ht->sector)
|
|
ht->sector->lightingdata = ht;
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
//
|
|
// LoadFireflickerThinker
|
|
//
|
|
// Loads a fireflicker_t from a save game
|
|
//
|
|
static void LoadFireflickerThinker(actionf_p1 thinker)
|
|
{
|
|
fireflicker_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->count = READINT32(save_p);
|
|
ht->resetcount = READINT32(save_p);
|
|
ht->maxlight = READINT32(save_p);
|
|
ht->minlight = READINT32(save_p);
|
|
if (ht->sector)
|
|
ht->sector->lightingdata = ht;
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
//
|
|
// LoadElevatorThinker
|
|
//
|
|
// Loads a elevator_t from a save game
|
|
//
|
|
static void LoadElevatorThinker(actionf_p1 thinker, UINT8 floorOrCeiling)
|
|
{
|
|
elevator_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->type = READUINT8(save_p);
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->actionsector = LoadSector(READUINT32(save_p));
|
|
ht->direction = READINT32(save_p);
|
|
ht->floordestheight = READFIXED(save_p);
|
|
ht->ceilingdestheight = READFIXED(save_p);
|
|
ht->speed = READFIXED(save_p);
|
|
ht->origspeed = READFIXED(save_p);
|
|
ht->low = READFIXED(save_p);
|
|
ht->high = READFIXED(save_p);
|
|
ht->distance = READFIXED(save_p);
|
|
ht->delay = READFIXED(save_p);
|
|
ht->delaytimer = READFIXED(save_p);
|
|
ht->floorwasheight = READFIXED(save_p);
|
|
ht->ceilingwasheight = READFIXED(save_p);
|
|
ht->player = LoadPlayer(READUINT32(save_p)); // was dummy
|
|
ht->sourceline = LoadLine(READUINT32(save_p));
|
|
|
|
if (ht->sector)
|
|
{
|
|
if (floorOrCeiling & 2)
|
|
ht->sector->ceilingdata = ht;
|
|
if (floorOrCeiling & 1)
|
|
ht->sector->floordata = ht;
|
|
}
|
|
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadScrollThinker
|
|
//
|
|
// Loads a scroll_t from a save game
|
|
//
|
|
static void LoadScrollThinker(actionf_p1 thinker)
|
|
{
|
|
scroll_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->dx = READFIXED(save_p);
|
|
ht->dy = READFIXED(save_p);
|
|
ht->affectee = READINT32(save_p);
|
|
ht->control = READINT32(save_p);
|
|
ht->last_height = READFIXED(save_p);
|
|
ht->vdx = READFIXED(save_p);
|
|
ht->vdy = READFIXED(save_p);
|
|
ht->accel = READINT32(save_p);
|
|
ht->exclusive = READINT32(save_p);
|
|
ht->type = READUINT8(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadFrictionThinker
|
|
//
|
|
// Loads a friction_t from a save game
|
|
//
|
|
static inline void LoadFrictionThinker(actionf_p1 thinker)
|
|
{
|
|
friction_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->friction = READINT32(save_p);
|
|
ht->movefactor = READINT32(save_p);
|
|
ht->affectee = READINT32(save_p);
|
|
ht->referrer = READINT32(save_p);
|
|
ht->roverfriction = READUINT8(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadPusherThinker
|
|
//
|
|
// Loads a pusher_t from a save game
|
|
//
|
|
static void LoadPusherThinker(actionf_p1 thinker)
|
|
{
|
|
pusher_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->type = READUINT8(save_p);
|
|
ht->x_mag = READINT32(save_p);
|
|
ht->y_mag = READINT32(save_p);
|
|
ht->magnitude = READINT32(save_p);
|
|
ht->radius = READINT32(save_p);
|
|
ht->x = READINT32(save_p);
|
|
ht->y = READINT32(save_p);
|
|
ht->z = READINT32(save_p);
|
|
ht->affectee = READINT32(save_p);
|
|
ht->roverpusher = READUINT8(save_p);
|
|
ht->referrer = READINT32(save_p);
|
|
ht->exclusive = READINT32(save_p);
|
|
ht->slider = READINT32(save_p);
|
|
ht->source = P_GetPushThing(ht->affectee);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadLaserThinker
|
|
//
|
|
// Loads a laserthink_t from a save game
|
|
//
|
|
static inline void LoadLaserThinker(actionf_p1 thinker)
|
|
{
|
|
laserthink_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ffloor_t *rover = NULL;
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->sec = LoadSector(READUINT32(save_p));
|
|
ht->sourceline = LoadLine(READUINT32(save_p));
|
|
for (rover = ht->sector->ffloors; rover; rover = rover->next)
|
|
if (rover->secnum == (size_t)(ht->sec - sectors)
|
|
&& rover->master == ht->sourceline)
|
|
ht->ffloor = rover;
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadLightlevelThinker
|
|
//
|
|
// Loads a lightlevel_t from a save game
|
|
//
|
|
static inline void LoadLightlevelThinker(actionf_p1 thinker)
|
|
{
|
|
lightlevel_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->destlevel = READINT32(save_p);
|
|
ht->speed = READINT32(save_p);
|
|
if (ht->sector)
|
|
ht->sector->lightingdata = ht;
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadExecutorThinker
|
|
//
|
|
// Loads a executor_t from a save game
|
|
//
|
|
static inline void LoadExecutorThinker(actionf_p1 thinker)
|
|
{
|
|
executor_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->line = LoadLine(READUINT32(save_p));
|
|
ht->caller = LoadMobj(READUINT32(save_p));
|
|
ht->sector = LoadSector(READUINT32(save_p));
|
|
ht->timer = READINT32(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadDisappearThinker
|
|
//
|
|
// Loads a disappear_t thinker
|
|
//
|
|
static inline void LoadDisappearThinker(actionf_p1 thinker)
|
|
{
|
|
disappear_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->appeartime = READUINT32(save_p);
|
|
ht->disappeartime = READUINT32(save_p);
|
|
ht->offset = READUINT32(save_p);
|
|
ht->timer = READUINT32(save_p);
|
|
ht->affectee = READINT32(save_p);
|
|
ht->sourceline = READINT32(save_p);
|
|
ht->exists = READINT32(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
|
|
//
|
|
// LoadPolyrotateThinker
|
|
//
|
|
// Loads a polyrotate_t thinker
|
|
//
|
|
static inline void LoadPolyrotatetThinker(actionf_p1 thinker)
|
|
{
|
|
polyrotate_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->polyObjNum = READINT32(save_p);
|
|
ht->speed = READINT32(save_p);
|
|
ht->distance = READINT32(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadPolymoveThinker
|
|
//
|
|
// Loads a polymovet_t thinker
|
|
//
|
|
static void LoadPolymoveThinker(actionf_p1 thinker)
|
|
{
|
|
polymove_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->polyObjNum = READINT32(save_p);
|
|
ht->speed = READINT32(save_p);
|
|
ht->momx = READFIXED(save_p);
|
|
ht->momy = READFIXED(save_p);
|
|
ht->distance = READINT32(save_p);
|
|
ht->angle = READANGLE(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadPolywaypointThinker
|
|
//
|
|
// Loads a polywaypoint_t thinker
|
|
//
|
|
static inline void LoadPolywaypointThinker(actionf_p1 thinker)
|
|
{
|
|
polywaypoint_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->polyObjNum = READINT32(save_p);
|
|
ht->speed = READINT32(save_p);
|
|
ht->sequence = READINT32(save_p);
|
|
ht->pointnum = READINT32(save_p);
|
|
ht->direction = READINT32(save_p);
|
|
ht->comeback = READUINT8(save_p);
|
|
ht->wrap = READUINT8(save_p);
|
|
ht->continuous = READUINT8(save_p);
|
|
ht->stophere = READUINT8(save_p);
|
|
ht->diffx = READFIXED(save_p);
|
|
ht->diffy = READFIXED(save_p);
|
|
ht->diffz = READFIXED(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadPolyslidedoorThinker
|
|
//
|
|
// loads a polyslidedoor_t thinker
|
|
//
|
|
static inline void LoadPolyslidedoorThinker(actionf_p1 thinker)
|
|
{
|
|
polyslidedoor_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->polyObjNum = READINT32(save_p);
|
|
ht->delay = READINT32(save_p);
|
|
ht->delayCount = READINT32(save_p);
|
|
ht->initSpeed = READINT32(save_p);
|
|
ht->speed = READINT32(save_p);
|
|
ht->initDistance = READINT32(save_p);
|
|
ht->distance = READINT32(save_p);
|
|
ht->initAngle = READUINT32(save_p);
|
|
ht->angle = READUINT32(save_p);
|
|
ht->revAngle = READUINT32(save_p);
|
|
ht->momx = READFIXED(save_p);
|
|
ht->momy = READFIXED(save_p);
|
|
ht->closing = READUINT8(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadPolyswingdoorThinker
|
|
//
|
|
// Loads a polyswingdoor_t thinker
|
|
//
|
|
static inline void LoadPolyswingdoorThinker(actionf_p1 thinker)
|
|
{
|
|
polyswingdoor_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->polyObjNum = READINT32(save_p);
|
|
ht->delay = READINT32(save_p);
|
|
ht->delayCount = READINT32(save_p);
|
|
ht->initSpeed = READINT32(save_p);
|
|
ht->speed = READINT32(save_p);
|
|
ht->initDistance = READINT32(save_p);
|
|
ht->distance = READINT32(save_p);
|
|
ht->closing = READUINT8(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
//
|
|
// LoadPolydisplaceThinker
|
|
//
|
|
// Loads a polydisplace_t thinker
|
|
//
|
|
static inline void LoadPolydisplaceThinker(actionf_p1 thinker)
|
|
{
|
|
polydisplace_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
ht->polyObjNum = READINT32(save_p);
|
|
ht->controlSector = LoadSector(READUINT32(save_p));
|
|
ht->dx = READFIXED(save_p);
|
|
ht->dy = READFIXED(save_p);
|
|
ht->oldHeights = READFIXED(save_p);
|
|
P_AddThinker(&ht->thinker);
|
|
}
|
|
|
|
/*
|
|
//
|
|
// LoadWhatThinker
|
|
//
|
|
// load a what_t thinker
|
|
//
|
|
static inline void LoadWhatThinker(actionf_p1 thinker)
|
|
{
|
|
what_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
|
|
ht->thinker.function.acp1 = thinker;
|
|
}
|
|
*/
|
|
|
|
//
|
|
// P_NetUnArchiveThinkers
|
|
//
|
|
static void P_NetUnArchiveThinkers(void)
|
|
{
|
|
thinker_t *currentthinker;
|
|
thinker_t *next;
|
|
UINT8 tclass;
|
|
UINT8 restoreNum = false;
|
|
UINT32 i;
|
|
UINT32 numloaded = 0;
|
|
|
|
if (READUINT32(save_p) != ARCHIVEBLOCK_THINKERS)
|
|
I_Error("Bad $$$.sav at archive block Thinkers");
|
|
|
|
// remove all the current thinkers
|
|
currentthinker = thinkercap.next;
|
|
for (currentthinker = thinkercap.next; currentthinker != &thinkercap; currentthinker = next)
|
|
{
|
|
next = currentthinker->next;
|
|
|
|
if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
|
|
P_RemoveSavegameMobj((mobj_t *)currentthinker); // item isn't saved, don't remove it
|
|
else
|
|
Z_Free(currentthinker);
|
|
}
|
|
|
|
// we don't want the removed mobjs to come back
|
|
iquetail = iquehead = 0;
|
|
P_InitThinkers();
|
|
|
|
// clear sector thinker pointers so they don't point to non-existant thinkers for all of eternity
|
|
for (i = 0; i < numsectors; i++)
|
|
{
|
|
sectors[i].floordata = sectors[i].ceilingdata = sectors[i].lightingdata = NULL;
|
|
}
|
|
|
|
// read in saved thinkers
|
|
for (;;)
|
|
{
|
|
tclass = READUINT8(save_p);
|
|
|
|
if (tclass == tc_end)
|
|
break; // leave the saved thinker reading loop
|
|
numloaded++;
|
|
|
|
switch (tclass)
|
|
{
|
|
case tc_mobj:
|
|
LoadMobjThinker((actionf_p1)P_MobjThinker);
|
|
break;
|
|
|
|
case tc_ceiling:
|
|
LoadCeilingThinker((actionf_p1)T_MoveCeiling);
|
|
break;
|
|
|
|
case tc_crushceiling:
|
|
LoadCeilingThinker((actionf_p1)T_CrushCeiling);
|
|
break;
|
|
|
|
case tc_floor:
|
|
LoadFloormoveThinker((actionf_p1)T_MoveFloor);
|
|
break;
|
|
|
|
case tc_flash:
|
|
LoadLightflashThinker((actionf_p1)T_LightningFlash);
|
|
break;
|
|
|
|
case tc_strobe:
|
|
LoadStrobeThinker((actionf_p1)T_StrobeFlash);
|
|
break;
|
|
|
|
case tc_glow:
|
|
LoadGlowThinker((actionf_p1)T_Glow);
|
|
break;
|
|
|
|
case tc_fireflicker:
|
|
LoadFireflickerThinker((actionf_p1)T_FireFlicker);
|
|
break;
|
|
|
|
case tc_elevator:
|
|
LoadElevatorThinker((actionf_p1)T_MoveElevator, 3);
|
|
break;
|
|
|
|
case tc_continuousfalling:
|
|
LoadSpecialLevelThinker((actionf_p1)T_ContinuousFalling, 3);
|
|
break;
|
|
|
|
case tc_thwomp:
|
|
LoadSpecialLevelThinker((actionf_p1)T_ThwompSector, 3);
|
|
break;
|
|
|
|
case tc_noenemies:
|
|
LoadSpecialLevelThinker((actionf_p1)T_NoEnemiesSector, 0);
|
|
break;
|
|
|
|
case tc_eachtime:
|
|
LoadSpecialLevelThinker((actionf_p1)T_EachTimeThinker, 0);
|
|
break;
|
|
|
|
case tc_raisesector:
|
|
LoadSpecialLevelThinker((actionf_p1)T_RaiseSector, 0);
|
|
break;
|
|
|
|
/// \todo rewrite all the code that uses an elevator_t but isn't an elevator
|
|
/// \note working on it!
|
|
case tc_camerascanner:
|
|
LoadElevatorThinker((actionf_p1)T_CameraScanner, 0);
|
|
break;
|
|
|
|
case tc_bouncecheese:
|
|
LoadSpecialLevelThinker((actionf_p1)T_BounceCheese, 2);
|
|
break;
|
|
|
|
case tc_startcrumble:
|
|
LoadElevatorThinker((actionf_p1)T_StartCrumble, 1);
|
|
break;
|
|
|
|
case tc_marioblock:
|
|
LoadSpecialLevelThinker((actionf_p1)T_MarioBlock, 3);
|
|
break;
|
|
|
|
case tc_marioblockchecker:
|
|
LoadSpecialLevelThinker((actionf_p1)T_MarioBlockChecker, 0);
|
|
break;
|
|
|
|
case tc_spikesector:
|
|
LoadSpecialLevelThinker((actionf_p1)T_SpikeSector, 0);
|
|
break;
|
|
|
|
case tc_floatsector:
|
|
LoadSpecialLevelThinker((actionf_p1)T_FloatSector, 0);
|
|
break;
|
|
|
|
case tc_bridgethinker:
|
|
LoadSpecialLevelThinker((actionf_p1)T_BridgeThinker, 3);
|
|
break;
|
|
|
|
case tc_laserflash:
|
|
LoadLaserThinker((actionf_p1)T_LaserFlash);
|
|
break;
|
|
|
|
case tc_lightfade:
|
|
LoadLightlevelThinker((actionf_p1)T_LightFade);
|
|
break;
|
|
|
|
case tc_executor:
|
|
LoadExecutorThinker((actionf_p1)T_ExecutorDelay);
|
|
restoreNum = true;
|
|
break;
|
|
|
|
case tc_disappear:
|
|
LoadDisappearThinker((actionf_p1)T_Disappear);
|
|
break;
|
|
case tc_polyrotate:
|
|
LoadPolyrotatetThinker((actionf_p1)T_PolyObjRotate);
|
|
break;
|
|
|
|
case tc_polymove:
|
|
LoadPolymoveThinker((actionf_p1)T_PolyObjMove);
|
|
break;
|
|
|
|
case tc_polywaypoint:
|
|
LoadPolywaypointThinker((actionf_p1)T_PolyObjWaypoint);
|
|
break;
|
|
|
|
case tc_polyslidedoor:
|
|
LoadPolyslidedoorThinker((actionf_p1)T_PolyDoorSlide);
|
|
break;
|
|
|
|
case tc_polyswingdoor:
|
|
LoadPolyswingdoorThinker((actionf_p1)T_PolyDoorSwing);
|
|
break;
|
|
|
|
case tc_polyflag:
|
|
LoadPolymoveThinker((actionf_p1)T_PolyObjFlag);
|
|
break;
|
|
|
|
case tc_polydisplace:
|
|
LoadPolydisplaceThinker((actionf_p1)T_PolyObjDisplace);
|
|
break;
|
|
case tc_scroll:
|
|
LoadScrollThinker((actionf_p1)T_Scroll);
|
|
break;
|
|
|
|
case tc_friction:
|
|
LoadFrictionThinker((actionf_p1)T_Friction);
|
|
break;
|
|
|
|
case tc_pusher:
|
|
LoadPusherThinker((actionf_p1)T_Pusher);
|
|
break;
|
|
|
|
default:
|
|
I_Error("P_UnarchiveSpecials: Unknown tclass %d in savegame", tclass);
|
|
}
|
|
}
|
|
|
|
CONS_Debug(DBG_NETPLAY, "%u thinkers loaded\n", numloaded);
|
|
|
|
if (restoreNum)
|
|
{
|
|
executor_t *delay = NULL;
|
|
UINT32 mobjnum;
|
|
for (currentthinker = thinkercap.next; currentthinker != &thinkercap;
|
|
currentthinker = currentthinker->next)
|
|
{
|
|
if (currentthinker->function.acp1 == (actionf_p1)T_ExecutorDelay)
|
|
{
|
|
delay = (void *)currentthinker;
|
|
if ((mobjnum = (UINT32)(size_t)delay->caller))
|
|
delay->caller = P_FindNewPosition(mobjnum);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// haleyjd 03/26/06: PolyObject saving code
|
|
//
|
|
#define PD_FLAGS 0x01
|
|
#define PD_TRANS 0x02
|
|
|
|
static inline void P_ArchivePolyObj(polyobj_t *po)
|
|
{
|
|
UINT8 diff = 0;
|
|
WRITEINT32(save_p, po->id);
|
|
WRITEANGLE(save_p, po->angle);
|
|
|
|
WRITEFIXED(save_p, po->spawnSpot.x);
|
|
WRITEFIXED(save_p, po->spawnSpot.y);
|
|
|
|
if (po->flags != po->spawnflags)
|
|
diff |= PD_FLAGS;
|
|
if (po->translucency != 0)
|
|
diff |= PD_TRANS;
|
|
|
|
WRITEUINT8(save_p, diff);
|
|
|
|
if (diff & PD_FLAGS)
|
|
WRITEINT32(save_p, po->flags);
|
|
if (diff & PD_TRANS)
|
|
WRITEINT32(save_p, po->translucency);
|
|
}
|
|
|
|
static inline void P_UnArchivePolyObj(polyobj_t *po)
|
|
{
|
|
INT32 id;
|
|
UINT32 angle;
|
|
fixed_t x, y;
|
|
UINT8 diff;
|
|
|
|
// nullify all polyobject thinker pointers;
|
|
// the thinkers themselves will fight over who gets the field
|
|
// when they first start to run.
|
|
po->thinker = NULL;
|
|
|
|
id = READINT32(save_p);
|
|
|
|
angle = READANGLE(save_p);
|
|
|
|
x = READFIXED(save_p);
|
|
y = READFIXED(save_p);
|
|
|
|
diff = READUINT8(save_p);
|
|
|
|
if (diff & PD_FLAGS)
|
|
po->flags = READINT32(save_p);
|
|
if (diff & PD_TRANS)
|
|
po->translucency = READINT32(save_p);
|
|
|
|
// if the object is bad or isn't in the id hash, we can do nothing more
|
|
// with it, so return now
|
|
if (po->isBad || po != Polyobj_GetForNum(id))
|
|
return;
|
|
|
|
// rotate and translate polyobject
|
|
Polyobj_MoveOnLoad(po, angle, x, y);
|
|
}
|
|
|
|
static inline void P_ArchivePolyObjects(void)
|
|
{
|
|
INT32 i;
|
|
|
|
WRITEUINT32(save_p, ARCHIVEBLOCK_POBJS);
|
|
|
|
// save number of polyobjects
|
|
WRITEINT32(save_p, numPolyObjects);
|
|
|
|
for (i = 0; i < numPolyObjects; ++i)
|
|
P_ArchivePolyObj(&PolyObjects[i]);
|
|
}
|
|
|
|
static inline void P_UnArchivePolyObjects(void)
|
|
{
|
|
INT32 i, numSavedPolys;
|
|
|
|
if (READUINT32(save_p) != ARCHIVEBLOCK_POBJS)
|
|
I_Error("Bad $$$.sav at archive block Pobjs");
|
|
|
|
numSavedPolys = READINT32(save_p);
|
|
|
|
if (numSavedPolys != numPolyObjects)
|
|
I_Error("P_UnArchivePolyObjects: polyobj count inconsistency\n");
|
|
|
|
for (i = 0; i < numSavedPolys; ++i)
|
|
P_UnArchivePolyObj(&PolyObjects[i]);
|
|
}
|
|
//
|
|
// P_FinishMobjs
|
|
//
|
|
static inline void P_FinishMobjs(void)
|
|
{
|
|
thinker_t *currentthinker;
|
|
mobj_t *mobj;
|
|
|
|
// put info field there real value
|
|
for (currentthinker = thinkercap.next; currentthinker != &thinkercap;
|
|
currentthinker = currentthinker->next)
|
|
{
|
|
if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
|
|
{
|
|
mobj = (mobj_t *)currentthinker;
|
|
mobj->info = &mobjinfo[mobj->type];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void P_RelinkPointers(void)
|
|
{
|
|
thinker_t *currentthinker;
|
|
mobj_t *mobj;
|
|
UINT32 temp;
|
|
|
|
// use info field (value = oldposition) to relink mobjs
|
|
for (currentthinker = thinkercap.next; currentthinker != &thinkercap;
|
|
currentthinker = currentthinker->next)
|
|
{
|
|
if (currentthinker->function.acp1 == (actionf_p1)P_MobjThinker)
|
|
{
|
|
mobj = (mobj_t *)currentthinker;
|
|
|
|
if (mobj->type == MT_HOOP || mobj->type == MT_HOOPCOLLIDE || mobj->type == MT_HOOPCENTER)
|
|
continue;
|
|
|
|
if (mobj->tracer)
|
|
{
|
|
temp = (UINT32)(size_t)mobj->tracer;
|
|
mobj->tracer = NULL;
|
|
if (!P_SetTarget(&mobj->tracer, P_FindNewPosition(temp)))
|
|
CONS_Debug(DBG_GAMELOGIC, "tracer not found on %d\n", mobj->type);
|
|
}
|
|
if (mobj->target)
|
|
{
|
|
temp = (UINT32)(size_t)mobj->target;
|
|
mobj->target = NULL;
|
|
if (!P_SetTarget(&mobj->target, P_FindNewPosition(temp)))
|
|
CONS_Debug(DBG_GAMELOGIC, "target not found on %d\n", mobj->type);
|
|
}
|
|
if (mobj->hnext)
|
|
{
|
|
temp = (UINT32)(size_t)mobj->hnext;
|
|
mobj->hnext = NULL;
|
|
if (!(mobj->hnext = P_FindNewPosition(temp)))
|
|
CONS_Debug(DBG_GAMELOGIC, "hnext not found on %d\n", mobj->type);
|
|
}
|
|
if (mobj->hprev)
|
|
{
|
|
temp = (UINT32)(size_t)mobj->hprev;
|
|
mobj->hprev = NULL;
|
|
if (!(mobj->hprev = P_FindNewPosition(temp)))
|
|
CONS_Debug(DBG_GAMELOGIC, "hprev not found on %d\n", mobj->type);
|
|
}
|
|
if (mobj->player && mobj->player->capsule)
|
|
{
|
|
temp = (UINT32)(size_t)mobj->player->capsule;
|
|
mobj->player->capsule = NULL;
|
|
if (!P_SetTarget(&mobj->player->capsule, P_FindNewPosition(temp)))
|
|
CONS_Debug(DBG_GAMELOGIC, "capsule not found on %d\n", mobj->type);
|
|
}
|
|
if (mobj->player && mobj->player->axis1)
|
|
{
|
|
temp = (UINT32)(size_t)mobj->player->axis1;
|
|
mobj->player->axis1 = NULL;
|
|
if (!P_SetTarget(&mobj->player->axis1, P_FindNewPosition(temp)))
|
|
CONS_Debug(DBG_GAMELOGIC, "axis1 not found on %d\n", mobj->type);
|
|
}
|
|
if (mobj->player && mobj->player->axis2)
|
|
{
|
|
temp = (UINT32)(size_t)mobj->player->axis2;
|
|
mobj->player->axis2 = NULL;
|
|
if (!P_SetTarget(&mobj->player->axis2, P_FindNewPosition(temp)))
|
|
CONS_Debug(DBG_GAMELOGIC, "axis2 not found on %d\n", mobj->type);
|
|
}
|
|
if (mobj->player && mobj->player->awayviewmobj)
|
|
{
|
|
temp = (UINT32)(size_t)mobj->player->awayviewmobj;
|
|
mobj->player->awayviewmobj = NULL;
|
|
if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition(temp)))
|
|
CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on %d\n", mobj->type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// P_NetArchiveSpecials
|
|
//
|
|
static inline void P_NetArchiveSpecials(void)
|
|
{
|
|
size_t i, z;
|
|
|
|
WRITEUINT32(save_p, ARCHIVEBLOCK_SPECIALS);
|
|
|
|
// itemrespawn queue for deathmatch
|
|
i = iquetail;
|
|
while (iquehead != i)
|
|
{
|
|
for (z = 0; z < nummapthings; z++)
|
|
{
|
|
if (&mapthings[z] == itemrespawnque[i])
|
|
{
|
|
WRITEUINT32(save_p, z);
|
|
break;
|
|
}
|
|
}
|
|
WRITEUINT32(save_p, itemrespawntime[i]);
|
|
i = (i + 1) & (ITEMQUESIZE-1);
|
|
}
|
|
|
|
// end delimiter
|
|
WRITEUINT32(save_p, 0xffffffff);
|
|
|
|
// Sky number
|
|
WRITEINT32(save_p, globallevelskynum);
|
|
|
|
// Current global weather type
|
|
WRITEUINT8(save_p, globalweather);
|
|
|
|
if (metalplayback) // Is metal sonic running?
|
|
{
|
|
WRITEUINT8(save_p, 0x01);
|
|
G_SaveMetal(&save_p);
|
|
}
|
|
else
|
|
WRITEUINT8(save_p, 0x00);
|
|
}
|
|
|
|
//
|
|
// P_NetUnArchiveSpecials
|
|
//
|
|
static void P_NetUnArchiveSpecials(void)
|
|
{
|
|
size_t i;
|
|
INT32 j;
|
|
|
|
if (READUINT32(save_p) != ARCHIVEBLOCK_SPECIALS)
|
|
I_Error("Bad $$$.sav at archive block Specials");
|
|
|
|
// BP: added save itemrespawn queue for deathmatch
|
|
iquetail = iquehead = 0;
|
|
while ((i = READUINT32(save_p)) != 0xffffffff)
|
|
{
|
|
itemrespawnque[iquehead] = &mapthings[i];
|
|
itemrespawntime[iquehead++] = READINT32(save_p);
|
|
}
|
|
|
|
j = READINT32(save_p);
|
|
if (j != globallevelskynum)
|
|
P_SetupLevelSky(j, true);
|
|
|
|
globalweather = READUINT8(save_p);
|
|
|
|
if (globalweather)
|
|
{
|
|
if (curWeather == globalweather)
|
|
curWeather = PRECIP_NONE;
|
|
|
|
P_SwitchWeather(globalweather);
|
|
}
|
|
else // PRECIP_NONE
|
|
{
|
|
if (curWeather != PRECIP_NONE)
|
|
P_SwitchWeather(globalweather);
|
|
}
|
|
|
|
if (READUINT8(save_p) == 0x01) // metal sonic
|
|
G_LoadMetal(&save_p);
|
|
}
|
|
|
|
// =======================================================================
|
|
// Misc
|
|
// =======================================================================
|
|
static inline void P_ArchiveMisc(void)
|
|
{
|
|
if (gamecomplete)
|
|
WRITEINT16(save_p, gamemap | 8192);
|
|
else
|
|
WRITEINT16(save_p, gamemap);
|
|
|
|
lastmapsaved = gamemap;
|
|
|
|
WRITEUINT16(save_p, (botskin ? (emeralds|(1<<10)) : emeralds)+357);
|
|
WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder));
|
|
}
|
|
|
|
static inline void P_UnArchiveSPGame(INT16 mapoverride)
|
|
{
|
|
char testname[sizeof(timeattackfolder)];
|
|
|
|
gamemap = READINT16(save_p);
|
|
|
|
if (mapoverride != 0)
|
|
{
|
|
gamemap = mapoverride;
|
|
gamecomplete = true;
|
|
}
|
|
else
|
|
gamecomplete = false;
|
|
|
|
// gamemap changed; we assume that its map header is always valid,
|
|
// so make it so
|
|
if(!mapheaderinfo[gamemap-1])
|
|
P_AllocMapHeader(gamemap-1);
|
|
|
|
lastmapsaved = gamemap;
|
|
|
|
tokenlist = 0;
|
|
token = 0;
|
|
|
|
savedata.emeralds = READUINT16(save_p)-357;
|
|
if (savedata.emeralds & (1<<10))
|
|
savedata.botcolor = 0xFF;
|
|
savedata.emeralds &= 0xff;
|
|
|
|
READSTRINGN(save_p, testname, sizeof(testname));
|
|
|
|
if (strcmp(testname, timeattackfolder))
|
|
{
|
|
if (modifiedgame)
|
|
I_Error("Save game not for this modification.");
|
|
else
|
|
I_Error("This save file is for a particular mod, it cannot be used with the regular game.");
|
|
}
|
|
|
|
memset(playeringame, 0, sizeof(*playeringame));
|
|
playeringame[consoleplayer] = true;
|
|
}
|
|
|
|
static void P_NetArchiveMisc(void)
|
|
{
|
|
UINT32 pig = 0;
|
|
INT32 i;
|
|
|
|
WRITEUINT32(save_p, ARCHIVEBLOCK_MISC);
|
|
|
|
WRITEINT16(save_p, gamemap);
|
|
if (gamestate != GS_LEVEL)
|
|
WRITEINT16(save_p, GS_WAITINGPLAYERS); // nice hack to put people back into waitingplayers
|
|
else
|
|
WRITEINT16(save_p, gamestate);
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
pig |= (playeringame[i] != 0)<<i;
|
|
WRITEUINT32(save_p, pig);
|
|
|
|
WRITEUINT32(save_p, P_GetRandSeed());
|
|
|
|
WRITEUINT32(save_p, tokenlist);
|
|
|
|
WRITEUINT8(save_p, encoremode);
|
|
|
|
WRITEUINT32(save_p, leveltime);
|
|
WRITEUINT32(save_p, totalrings);
|
|
WRITEINT16(save_p, lastmap);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
WRITEINT16(save_p, votelevels[i][0]);
|
|
WRITEINT16(save_p, votelevels[i][1]);
|
|
}
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
WRITESINT8(save_p, votes[i]);
|
|
|
|
WRITESINT8(save_p, pickedvote);
|
|
|
|
WRITEUINT16(save_p, emeralds);
|
|
WRITEUINT8(save_p, stagefailed);
|
|
|
|
WRITEUINT32(save_p, token);
|
|
WRITEINT32(save_p, sstimer);
|
|
WRITEUINT32(save_p, bluescore);
|
|
WRITEUINT32(save_p, redscore);
|
|
|
|
WRITEINT16(save_p, autobalance);
|
|
WRITEINT16(save_p, teamscramble);
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
WRITEINT16(save_p, scrambleplayers[i]);
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
WRITEINT16(save_p, scrambleteams[i]);
|
|
|
|
WRITEINT16(save_p, scrambletotal);
|
|
WRITEINT16(save_p, scramblecount);
|
|
|
|
WRITEUINT32(save_p, racecountdown);
|
|
WRITEUINT32(save_p, exitcountdown);
|
|
|
|
WRITEFIXED(save_p, gravity);
|
|
WRITEFIXED(save_p, mapobjectscale);
|
|
|
|
WRITEUINT32(save_p, countdowntimer);
|
|
WRITEUINT8(save_p, countdowntimeup);
|
|
|
|
WRITEUINT32(save_p, hidetime);
|
|
|
|
// SRB2kart
|
|
WRITEINT32(save_p, numgotboxes);
|
|
|
|
WRITEUINT8(save_p, gamespeed);
|
|
WRITEUINT8(save_p, franticitems);
|
|
WRITEUINT8(save_p, comeback);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
WRITESINT8(save_p, battlewanted[i]);
|
|
|
|
WRITEUINT32(save_p, wantedcalcdelay);
|
|
WRITEUINT32(save_p, indirectitemcooldown);
|
|
WRITEUINT32(save_p, hyubgone);
|
|
WRITEUINT32(save_p, mapreset);
|
|
WRITEUINT8(save_p, nospectategrief);
|
|
WRITEUINT8(save_p, thwompsactive);
|
|
WRITESINT8(save_p, spbplace);
|
|
|
|
// Is it paused?
|
|
if (paused)
|
|
WRITEUINT8(save_p, 0x2f);
|
|
else
|
|
WRITEUINT8(save_p, 0x2e);
|
|
}
|
|
|
|
static inline boolean P_NetUnArchiveMisc(void)
|
|
{
|
|
UINT32 pig;
|
|
INT32 i;
|
|
|
|
if (READUINT32(save_p) != ARCHIVEBLOCK_MISC)
|
|
I_Error("Bad $$$.sav at archive block Misc");
|
|
|
|
gamemap = READINT16(save_p);
|
|
|
|
// gamemap changed; we assume that its map header is always valid,
|
|
// so make it so
|
|
if(!mapheaderinfo[gamemap-1])
|
|
P_AllocMapHeader(gamemap-1);
|
|
|
|
// tell the sound code to reset the music since we're skipping what
|
|
// normally sets this flag
|
|
mapmusflags |= MUSIC_RELOADRESET;
|
|
|
|
G_SetGamestate(READINT16(save_p));
|
|
|
|
pig = READUINT32(save_p);
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
playeringame[i] = (pig & (1<<i)) != 0;
|
|
// playerstate is set in unarchiveplayers
|
|
}
|
|
|
|
P_SetRandSeed(READUINT32(save_p));
|
|
|
|
tokenlist = READUINT32(save_p);
|
|
|
|
encoremode = (boolean)READUINT8(save_p);
|
|
|
|
if (!P_SetupLevel(true))
|
|
return false;
|
|
|
|
// get the time
|
|
leveltime = READUINT32(save_p);
|
|
totalrings = READUINT32(save_p);
|
|
lastmap = READINT16(save_p);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
votelevels[i][0] = READINT16(save_p);
|
|
votelevels[i][1] = READINT16(save_p);
|
|
}
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
votes[i] = READSINT8(save_p);
|
|
|
|
pickedvote = READSINT8(save_p);
|
|
|
|
emeralds = READUINT16(save_p);
|
|
stagefailed = READUINT8(save_p);
|
|
|
|
token = READUINT32(save_p);
|
|
sstimer = READINT32(save_p);
|
|
bluescore = READUINT32(save_p);
|
|
redscore = READUINT32(save_p);
|
|
|
|
autobalance = READINT16(save_p);
|
|
teamscramble = READINT16(save_p);
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
scrambleplayers[i] = READINT16(save_p);
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
scrambleteams[i] = READINT16(save_p);
|
|
|
|
scrambletotal = READINT16(save_p);
|
|
scramblecount = READINT16(save_p);
|
|
|
|
racecountdown = READUINT32(save_p);
|
|
exitcountdown = READUINT32(save_p);
|
|
|
|
gravity = READFIXED(save_p);
|
|
mapobjectscale = READFIXED(save_p);
|
|
|
|
countdowntimer = (tic_t)READUINT32(save_p);
|
|
countdowntimeup = (boolean)READUINT8(save_p);
|
|
|
|
hidetime = READUINT32(save_p);
|
|
|
|
// SRB2kart
|
|
numgotboxes = READINT32(save_p);
|
|
|
|
gamespeed = READUINT8(save_p);
|
|
franticitems = (boolean)READUINT8(save_p);
|
|
comeback = (boolean)READUINT8(save_p);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
battlewanted[i] = READSINT8(save_p);
|
|
|
|
wantedcalcdelay = READUINT32(save_p);
|
|
indirectitemcooldown = READUINT32(save_p);
|
|
hyubgone = READUINT32(save_p);
|
|
mapreset = READUINT32(save_p);
|
|
nospectategrief = READUINT8(save_p);
|
|
thwompsactive = (boolean)READUINT8(save_p);
|
|
spbplace = READSINT8(save_p);
|
|
|
|
// Is it paused?
|
|
if (READUINT8(save_p) == 0x2f)
|
|
paused = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void P_SaveGame(void)
|
|
{
|
|
P_ArchiveMisc();
|
|
P_ArchivePlayer();
|
|
|
|
WRITEUINT8(save_p, 0x1d); // consistency marker
|
|
}
|
|
|
|
void P_SaveNetGame(void)
|
|
{
|
|
thinker_t *th;
|
|
mobj_t *mobj;
|
|
INT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise
|
|
|
|
CV_SaveNetVars(&save_p, false);
|
|
P_NetArchiveMisc();
|
|
|
|
// Assign the mobjnumber for pointer tracking
|
|
if (gamestate == GS_LEVEL)
|
|
{
|
|
for (th = thinkercap.next; th != &thinkercap; th = th->next)
|
|
{
|
|
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
|
|
{
|
|
mobj = (mobj_t *)th;
|
|
if (mobj->type == MT_HOOP || mobj->type == MT_HOOPCOLLIDE || mobj->type == MT_HOOPCENTER)
|
|
continue;
|
|
mobj->mobjnum = i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
P_NetArchivePlayers();
|
|
if (gamestate == GS_LEVEL)
|
|
{
|
|
P_NetArchiveWorld();
|
|
P_ArchivePolyObjects();
|
|
P_NetArchiveThinkers();
|
|
P_NetArchiveSpecials();
|
|
}
|
|
#ifdef HAVE_BLUA
|
|
LUA_Archive();
|
|
#endif
|
|
|
|
WRITEUINT8(save_p, 0x1d); // consistency marker
|
|
}
|
|
|
|
boolean P_LoadGame(INT16 mapoverride)
|
|
{
|
|
if (gamestate == GS_INTERMISSION)
|
|
Y_EndIntermission();
|
|
if (gamestate == GS_VOTING)
|
|
Y_EndVote();
|
|
G_SetGamestate(GS_NULL); // should be changed in P_UnArchiveMisc
|
|
|
|
P_UnArchiveSPGame(mapoverride);
|
|
P_UnArchivePlayer();
|
|
|
|
// Savegame end marker
|
|
if (READUINT8(save_p) != 0x1d)
|
|
return false;
|
|
|
|
// Only do this after confirming savegame is ok
|
|
G_DeferedInitNew(false, G_BuildMapName(gamemap), savedata.skin, 0, true);
|
|
COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean P_LoadNetGame(void)
|
|
{
|
|
CV_LoadNetVars(&save_p);
|
|
if (!P_NetUnArchiveMisc())
|
|
return false;
|
|
P_NetUnArchivePlayers();
|
|
if (gamestate == GS_LEVEL)
|
|
{
|
|
P_NetUnArchiveWorld();
|
|
P_UnArchivePolyObjects();
|
|
P_NetUnArchiveThinkers();
|
|
P_NetUnArchiveSpecials();
|
|
P_RelinkPointers();
|
|
P_FinishMobjs();
|
|
}
|
|
#ifdef HAVE_BLUA
|
|
LUA_UnArchive();
|
|
#endif
|
|
|
|
// This is stupid and hacky, but maybe it'll work!
|
|
P_SetRandSeed(P_GetInitSeed());
|
|
|
|
// The precipitation would normally be spawned in P_SetupLevel, which is called by
|
|
// P_NetUnArchiveMisc above. However, that would place it up before P_NetUnArchiveThinkers,
|
|
// so the thinkers would be deleted later. Therefore, P_SetupLevel will *not* spawn
|
|
// precipitation when loading a netgame save. Instead, precip has to be spawned here.
|
|
// This is done in P_NetUnArchiveSpecials now.
|
|
|
|
return READUINT8(save_p) == 0x1d;
|
|
}
|