diff --git a/src/g_demo.c b/src/g_demo.c index 7c949a4c8..41991bdbf 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -39,24 +39,29 @@ #include "lua_hook.h" #include "md5.h" // demo checksums +#include "dehacked.h" // get_number (for ghost thok) +#include "lua_script.h" // LUA_ArchiveDemo and LUA_UnArchiveDemo + +static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; +consvar_t cv_recordmultiplayerdemos = {"netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + +static CV_PossibleValue_t netdemosyncquality_cons_t[] = {{1, "MIN"}, {35, "MAX"}, {0, NULL}}; +consvar_t cv_netdemosyncquality = {"netdemo_syncquality", "1", CV_SAVE, netdemosyncquality_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + boolean timingdemo; // if true, exit with report on completion boolean nodrawers; // for comparative timing purposes boolean noblit; // for comparative timing purposes tic_t demostarttime; // for comparative timing purposes -static char demoname[64]; -boolean demorecording; -boolean demoplayback; -boolean titledemo; // Title Screen demo can be cancelled by any key +static char demoname[128]; static UINT8 *demobuffer = NULL; static UINT8 *demo_p, *demotime_p; static UINT8 *demoend; static UINT8 demoflags; -static UINT16 demoversion; -boolean singledemo; // quit after playing a demo from cmdline -boolean demo_start; // don't start playing demo right away boolean demosynced = true; // console warning message +struct demovars_s demo; + boolean metalrecording; // recording as metal sonic mobj_t *metalplayback; static UINT8 *metalbuffer = NULL; @@ -68,15 +73,20 @@ static struct { UINT8 flags; // EZT flags // EZT_COLOR - UINT16 color, lastcolor; + UINT8 color, lastcolor; // EZT_SCALE fixed_t scale, lastscale; + // EZT_KART + INT32 kartitem, kartamount, kartbumpers; + + UINT8 desyncframes; // Don't try to resync unless we've been off for two frames, to monkeypatch a few trouble spots + // EZT_HIT UINT16 hits; mobj_t **hitlist; -} ghostext; +} ghostext[MAXPLAYERS]; // Your naming conventions are stupid and useless. // There is no conflict here. @@ -94,14 +104,21 @@ demoghost *ghosts = NULL; // DEMO RECORDING // -#define DEMOVERSION 0x000d -#define DEMOHEADER "\xF0" "SRB2Replay" "\x0F" +#define DEMOVERSION 0x0004 +#define DEMOHEADER "\xF0" "KartReplay" "\x0F" #define DF_GHOST 0x01 // This demo contains ghost data too! -#define DF_RECORDATTACK 0x02 // This demo is from record attack and contains its final completion time, score, and rings! -#define DF_NIGHTSATTACK 0x04 // This demo is from NiGHTS attack and contains its time left, score, and mares! +#define DF_TIMEATTACK 0x02 // This demo is from Time Attack and contains its final completion time & best lap! +#define DF_BREAKTHECAPSULES 0x04 // This demo is from Break the Capsules and contains its final completion time! #define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? + +#define DF_LUAVARS 0x20 // this demo contains extra lua vars; this is mostly used for backwards compability + #define DF_ATTACKSHIFT 1 +#define DF_ENCORE 0x40 +#define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode! + +#define DEMO_SPECTATOR 0x40 // For demos #define ZT_FWD 0x01 @@ -109,11 +126,24 @@ demoghost *ghosts = NULL; #define ZT_ANGLE 0x04 #define ZT_BUTTONS 0x08 #define ZT_AIMING 0x10 +#define ZT_DRIFT 0x20 +#define ZT_LATENCY 0x40 #define DEMOMARKER 0x80 // demoend + +UINT8 demo_extradata[MAXPLAYERS]; +UINT8 demo_writerng; // 0=no, 1=yes, 2=yes but on a timeout +static ticcmd_t oldcmd[MAXPLAYERS]; + #define METALDEATH 0x44 #define METALSNICE 0x69 -static ticcmd_t oldcmd; +#define DW_END 0xFF // End of extradata block +#define DW_RNG 0xFE // Check RNG seed! + +#define DW_EXTRASTUFF 0xFE // Numbers below this are reserved for writing player slot data + +// Below consts are only used for demo extrainfo sections +#define DW_STANDING 0x00 // For Metal Sonic and time attack ghosts #define GZT_XYZ 0x01 @@ -126,16 +156,12 @@ static ticcmd_t oldcmd; #define GZT_FOLLOW 0x80 // Followmobj // GZT_EXTRA flags -#define EZT_THOK 0x01 // Spawned a thok object -#define EZT_SPIN 0x02 // Because one type of thok object apparently wasn't enough -#define EZT_REV 0x03 // And two types wasn't enough either yet -#define EZT_THOKMASK 0x03 -#define EZT_COLOR 0x04 // Changed color (Super transformation, Mario fireflowers/invulnerability, etc.) -#define EZT_FLIP 0x08 // Reversed gravity -#define EZT_SCALE 0x10 // Changed size -#define EZT_HIT 0x20 // Damaged a mobj -#define EZT_SPRITE 0x40 // Changed sprite set completely out of PLAY (NiGHTS, SOCs, whatever) -#define EZT_HEIGHT 0x80 // Changed height +#define EZT_COLOR 0x001 // Changed color (Super transformation, Mario fireflowers/invulnerability, etc.) +#define EZT_FLIP 0x002 // Reversed gravity +#define EZT_SCALE 0x004 // Changed size +#define EZT_HIT 0x008 // Damaged a mobj +#define EZT_SPRITE 0x010 // Changed sprite set completely out of PLAY (NiGHTS, SOCs, whatever) +#define EZT_KART 0x020 // SRB2Kart: Changed current held item/quantity and bumpers for battle // GZT_FOLLOW flags #define FZT_SPAWNED 0x01 // just been spawned @@ -162,28 +188,318 @@ void G_LoadMetal(UINT8 **buffer) metal_p = metalbuffer + READUINT32(*buffer); } +// Finds a skin with the closest stats if the expected skin doesn't exist. +static INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight) +{ + INT32 i, closest_skin = 0; + UINT8 closest_stats = UINT8_MAX, stat_diff; + + for (i = 0; i < numskins; i++) + { + stat_diff = abs(skins[i].kartspeed - kartspeed) + abs(skins[i].kartweight - kartweight); + if (stat_diff < closest_stats) + { + closest_stats = stat_diff; + closest_skin = i; + } + } + + return closest_skin; +} + +static void FindClosestSkinForStats(UINT32 p, UINT8 kartspeed, UINT8 kartweight) +{ + INT32 closest_skin = GetSkinNumClosestToStats(kartspeed, kartweight); + + //CONS_Printf("Using %s instead...\n", skins[closest_skin].name); + SetPlayerSkinByNum(p, closest_skin); +} + +void G_ReadDemoExtraData(void) +{ + INT32 p, extradata, i; + char name[17]; + + if (leveltime > starttime) + { + rewind_t *rewind = CL_SaveRewindPoint(demo_p - demobuffer); + if (rewind) + { + memcpy(rewind->oldcmd, oldcmd, sizeof (oldcmd)); + memcpy(rewind->oldghost, oldghost, sizeof (oldghost)); + } + } + + memset(name, '\0', 17); + + p = READUINT8(demo_p); + + while (p < DW_EXTRASTUFF) + { + extradata = READUINT8(demo_p); + + if (extradata & DXD_RESPAWN) + { + if (players[p].mo) + { + // Is this how this should work..? + K_DoIngameRespawn(&players[p]); + } + } + if (extradata & DXD_SKIN) + { + UINT8 kartspeed, kartweight; + + // Skin + M_Memcpy(name, demo_p, 16); + demo_p += 16; + SetPlayerSkin(p, name); + + kartspeed = READUINT8(demo_p); + kartweight = READUINT8(demo_p); + + if (stricmp(skins[players[p].skin].name, name) != 0) + FindClosestSkinForStats(p, kartspeed, kartweight); + + players[p].kartspeed = kartspeed; + players[p].kartweight = kartweight; + } + if (extradata & DXD_COLOR) + { + // Color + M_Memcpy(name, demo_p, 16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i], name)) // SRB2kart + { + players[p].skincolor = i; + if (players[p].mo) + players[p].mo->color = i; + break; + } + } + if (extradata & DXD_NAME) + { + // Name + M_Memcpy(player_names[p],demo_p,16); + demo_p += 16; + } + if (extradata & DXD_FOLLOWER) + { + // Set our follower + M_Memcpy(name, demo_p, 16); + demo_p += 16; + SetPlayerFollower(p, name); + + // Follower's color + M_Memcpy(name, demo_p, 16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i], name)) // SRB2kart + { + players[p].followercolor = i; + break; + } + + + } + if (extradata & DXD_PLAYSTATE) + { + extradata = READUINT8(demo_p); + + switch (extradata) { + case DXD_PST_PLAYING: + players[p].pflags |= PF_WANTSTOJOIN; // fuck you + break; + + case DXD_PST_SPECTATING: + players[p].pflags &= ~PF_WANTSTOJOIN; // double-fuck you + if (!playeringame[p]) + { + CL_ClearPlayer(p); + playeringame[p] = true; + G_AddPlayer(p); + players[p].spectator = true; + + // There's likely an off-by-one error in timing recording or playback of joins. This hacks around it so I don't have to find out where that is. \o/ + if (oldcmd[p].forwardmove) + P_RandomByte(); + } + else + { + players[p].spectator = true; + if (players[p].mo) + P_DamageMobj(players[p].mo, NULL, NULL, 10000); + else + players[p].playerstate = PST_REBORN; + } + break; + + case DXD_PST_LEFT: + CL_RemovePlayer(p, 0); + break; + } + + G_ResetViews(); + + // maybe these are necessary? + if (G_BattleGametype()) + K_CheckBumpers(); // SRB2Kart + else if (G_RaceGametype()) + P_CheckRacers(); // also SRB2Kart + } + + + p = READUINT8(demo_p); + } + + while (p != DW_END) + { + UINT32 rng; + + switch (p) + { + case DW_RNG: + rng = READUINT32(demo_p); + if (P_GetRandSeed() != rng) + { + P_SetRandSeed(rng); + + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + } + } + + p = READUINT8(demo_p); + } + + if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } +} + +void G_WriteDemoExtraData(void) +{ + INT32 i; + char name[16]; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (demo_extradata[i]) + { + WRITEUINT8(demo_p, i); + WRITEUINT8(demo_p, demo_extradata[i]); + + //if (demo_extradata[i] & DXD_RESPAWN) has no extra data + if (demo_extradata[i] & DXD_SKIN) + { + // Skin + memset(name, 0, 16); + strncpy(name, skins[players[i].skin].name, 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + + WRITEUINT8(demo_p, skins[players[i].skin].kartspeed); + WRITEUINT8(demo_p, skins[players[i].skin].kartweight); + + } + if (demo_extradata[i] & DXD_COLOR) + { + // Color + memset(name, 0, 16); + strncpy(name, KartColor_Names[players[i].skincolor], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + } + if (demo_extradata[i] & DXD_NAME) + { + // Name + memset(name, 0, 16); + strncpy(name, player_names[i], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + } + if (demo_extradata[i] & DXD_FOLLOWER) + { + // write follower + memset(name, 0, 16); + strncpy(name, followers[players[i].followerskin].skinname, 16); + M_Memcpy(demo_p, name, 16); + demo_p += 16; + + // write follower color + memset(name, 0, 16); + strncpy(name, Followercolor_cons_t[players[i].followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" + M_Memcpy(demo_p,name,16); + demo_p += 16; + + } + if (demo_extradata[i] & DXD_PLAYSTATE) + { + demo_writerng = 1; + if (!playeringame[i]) + WRITEUINT8(demo_p, DXD_PST_LEFT); + else if ( + players[i].spectator && + !(players[i].pflags & PF_WANTSTOJOIN) // <= fuck you specifically + ) + WRITEUINT8(demo_p, DXD_PST_SPECTATING); + else + WRITEUINT8(demo_p, DXD_PST_PLAYING); + } + } + + demo_extradata[i] = 0; + } + + // May not be necessary, but might as well play it safe... + if ((leveltime & 255) == 128) + demo_writerng = 1; + + { + static UINT8 timeout = 0; + + if (timeout) timeout--; + + if (demo_writerng == 1 || (demo_writerng == 2 && timeout == 0)) + { + demo_writerng = 0; + timeout = 16; + WRITEUINT8(demo_p, DW_RNG); + WRITEUINT32(demo_p, P_GetRandSeed()); + } + } + + WRITEUINT8(demo_p, DW_END); +} void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) { UINT8 ziptic; (void)playernum; - if (!demo_p || !demo_start) + if (!demo_p || !demo.deferstart) return; ziptic = READUINT8(demo_p); if (ziptic & ZT_FWD) - oldcmd.forwardmove = READSINT8(demo_p); - if (ziptic & ZT_SIDE) - oldcmd.sidemove = READSINT8(demo_p); + oldcmd[playernum].forwardmove = READSINT8(demo_p); if (ziptic & ZT_ANGLE) - oldcmd.angleturn = READINT16(demo_p); + oldcmd[playernum].angleturn = READINT16(demo_p); if (ziptic & ZT_BUTTONS) - oldcmd.buttons = (oldcmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) | (READUINT16(demo_p) & ~(BT_CAMLEFT|BT_CAMRIGHT)); + oldcmd[playernum].buttons = READUINT16(demo_p); if (ziptic & ZT_AIMING) - oldcmd.aiming = READINT16(demo_p); + oldcmd[playernum].aiming = READINT16(demo_p); + if (ziptic & ZT_DRIFT) + oldcmd[playernum].driftturn = READINT16(demo_p); + if (ziptic & ZT_LATENCY) + oldcmd[playernum].latency = READUINT8(demo_p); - G_CopyTiccmd(cmd, &oldcmd, 1); + G_CopyTiccmd(cmd, &oldcmd[playernum], 1); if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) { @@ -203,41 +519,48 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) return; ziptic_p = demo_p++; // the ziptic, written at the end of this function - if (cmd->forwardmove != oldcmd.forwardmove) + if (cmd->forwardmove != oldcmd[playernum].forwardmove) { WRITEUINT8(demo_p,cmd->forwardmove); - oldcmd.forwardmove = cmd->forwardmove; + oldcmd[playernum].forwardmove = cmd->forwardmove; ziptic |= ZT_FWD; } - if (cmd->sidemove != oldcmd.sidemove) - { - WRITEUINT8(demo_p,cmd->sidemove); - oldcmd.sidemove = cmd->sidemove; - ziptic |= ZT_SIDE; - } - - if (cmd->angleturn != oldcmd.angleturn) + if (cmd->angleturn != oldcmd[playernum].angleturn) { WRITEINT16(demo_p,cmd->angleturn); - oldcmd.angleturn = cmd->angleturn; + oldcmd[playernum].angleturn = cmd->angleturn; ziptic |= ZT_ANGLE; } - if (cmd->buttons != oldcmd.buttons) + if (cmd->buttons != oldcmd[playernum].buttons) { WRITEUINT16(demo_p,cmd->buttons); - oldcmd.buttons = cmd->buttons; + oldcmd[playernum].buttons = cmd->buttons; ziptic |= ZT_BUTTONS; } - if (cmd->aiming != oldcmd.aiming) + if (cmd->aiming != oldcmd[playernum].aiming) { WRITEINT16(demo_p,cmd->aiming); - oldcmd.aiming = cmd->aiming; + oldcmd[playernum].aiming = cmd->aiming; ziptic |= ZT_AIMING; } + if (cmd->driftturn != oldcmd[playernum].driftturn) + { + WRITEINT16(demo_p,cmd->driftturn); + oldcmd[playernum].driftturn = cmd->driftturn; + ziptic |= ZT_DRIFT; + } + + if (cmd->latency != oldcmd[playernum].latency) + { + WRITEUINT8(demo_p,cmd->latency); + oldcmd[playernum].latency = cmd->latency; + ziptic |= ZT_LATENCY; + } + *ziptic_p = ziptic; // attention here for the ticcmd size! @@ -249,71 +572,93 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) } } -void G_GhostAddThok(void) +void G_GhostAddThok(INT32 playernum) { - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + if (!metalrecording && (!demo.recording || !(demoflags & DF_GHOST))) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_THOK; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_THOK; } -void G_GhostAddSpin(void) +void G_GhostAddSpin(INT32 playernum) { - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + if (!metalrecording && (!demo.recording || !(demoflags & DF_GHOST))) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_SPIN; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_SPIN; } -void G_GhostAddRev(void) +void G_GhostAddRev(INT32 playernum) { - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + if (!metalrecording && (!demo.recording || !(demoflags & DF_GHOST))) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_REV; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_REV; } -void G_GhostAddFlip(void) +void G_GhostAddFlip(INT32 playernum) { - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + if (!metalrecording && (!demo.recording || !(demoflags & DF_GHOST))) return; - ghostext.flags |= EZT_FLIP; + ghostext[playernum].flags |= EZT_FLIP; } -void G_GhostAddColor(ghostcolor_t color) +void G_GhostAddColor(INT32 playernum, ghostcolor_t color) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - if (ghostext.lastcolor == (UINT16)color) + if (ghostext[playernum].lastcolor == (UINT16)color) { - ghostext.flags &= ~EZT_COLOR; + ghostext[playernum].flags &= ~EZT_COLOR; return; } - ghostext.flags |= EZT_COLOR; - ghostext.color = (UINT16)color; + ghostext[playernum].flags |= EZT_COLOR; + ghostext[playernum].color = (UINT16)color; } -void G_GhostAddScale(fixed_t scale) +void G_GhostAddScale(INT32 playernum, fixed_t scale) { - if (!metalrecording && (!demorecording || !(demoflags & DF_GHOST))) + if (!metalrecording && (!demo.recording || !(demoflags & DF_GHOST))) return; - if (ghostext.lastscale == scale) + if (ghostext[playernum].lastscale == scale) { - ghostext.flags &= ~EZT_SCALE; + ghostext[playernum].flags &= ~EZT_SCALE; return; } - ghostext.flags |= EZT_SCALE; - ghostext.scale = scale; + ghostext[playernum].flags |= EZT_SCALE; + ghostext[playernum].scale = scale; } -void G_GhostAddHit(mobj_t *victim) +void G_GhostAddHit(INT32 playernum, mobj_t *victim) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - ghostext.flags |= EZT_HIT; - ghostext.hits++; - ghostext.hitlist = Z_Realloc(ghostext.hitlist, ghostext.hits * sizeof(mobj_t *), PU_LEVEL, NULL); - ghostext.hitlist[ghostext.hits-1] = victim; + ghostext[playernum].flags |= EZT_HIT; + ghostext[playernum].hits++; + ghostext[playernum].hitlist = Z_Realloc(ghostext[playernum].hitlist, ghostext[playernum].hits * sizeof(mobj_t *), PU_LEVEL, NULL); + ghostext[playernum].hitlist[ghostext[playernum].hits-1] = victim; } -void G_WriteGhostTic(mobj_t *ghost) +void G_WriteAllGhostTics(void) +{ + INT32 i, counter = leveltime; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!players[i].mo) + continue; + + counter++; + + if (counter % cv_netdemosyncquality.value != 0) // Only write 1 in this many ghost datas per tic to cut down on multiplayer replay size. + continue; + + WRITEUINT8(demo_p, i); + G_WriteGhostTic(players[i].mo, i); + } + WRITEUINT8(demo_p, 0xFF); +} + +void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) { char ziptic = 0; UINT8 *ziptic_p; @@ -327,40 +672,41 @@ void G_WriteGhostTic(mobj_t *ghost) ziptic_p = demo_p++; // the ziptic, written at the end of this function - #define MAXMOM (0xFFFF<<8) +#define MAXMOM (0xFFFF<<8) // GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic. - if (abs(ghost->x-oldghost.x) > MAXMOM + if (abs(ghost->x-oldghost[playernum].x) > MAXMOM || abs(ghost->y-oldghost.y) > MAXMOM - || abs(ghost->z-oldghost.z) > MAXMOM) + || abs(ghost->z-oldghost.z) > MAXMOM + || ((UINT8)(leveltime & 255) > 0 && (UINT8)(leveltime & 255) <= (UINT8)cv_netdemosyncquality.value)) // Hack to enable slightly nicer resyncing { - oldghost.x = ghost->x; - oldghost.y = ghost->y; - oldghost.z = ghost->z; + oldghost[playernum].x = ghost->x; + oldghost[playernum].y = ghost->y; + oldghost[playernum].z = ghost->z; ziptic |= GZT_XYZ; - WRITEFIXED(demo_p,oldghost.x); - WRITEFIXED(demo_p,oldghost.y); - WRITEFIXED(demo_p,oldghost.z); + WRITEFIXED(demo_p,oldghost[playernum].x); + WRITEFIXED(demo_p,oldghost[playernum].y); + WRITEFIXED(demo_p,oldghost[playernum].z); } else { // For moving normally: // Store one full byte of movement, plus one byte of fractional movement. - INT16 momx = (INT16)((ghost->x-oldghost.x)>>8); - INT16 momy = (INT16)((ghost->y-oldghost.y)>>8); - if (momx != oldghost.momx - || momy != oldghost.momy) + INT16 momx = (INT16)((ghost->x-oldghost[playernum].x + (1<<4))>>8); + INT16 momy = (INT16)((ghost->y-oldghost[playernum].y + (1<<4))>>8); + if (momx != oldghost[playernum].momx + || momy != oldghost[playernum].momy) { - oldghost.momx = momx; - oldghost.momy = momy; + oldghost[playernum].momx = momx; + oldghost[playernum].momy = momy; ziptic |= GZT_MOMXY; WRITEINT16(demo_p,momx); WRITEINT16(demo_p,momy); } - momx = (INT16)((ghost->z-oldghost.z)>>8); - if (momx != oldghost.momz) + momx = (INT16)((ghost->z-oldghost[playernum].z + (1<<4))>>8); + if (momx != oldghost[playernum].momz) { - oldghost.momz = momx; + oldghost[playernum].momz = momx; ziptic |= GZT_MOMZ; WRITEINT16(demo_p,momx); } @@ -368,78 +714,88 @@ void G_WriteGhostTic(mobj_t *ghost) // This SHOULD set oldghost.x/y/z to match ghost->x/y/z // but it keeps the fractional loss of one byte, // so it will hopefully be made up for in future tics. - oldghost.x += oldghost.momx<<8; - oldghost.y += oldghost.momy<<8; - oldghost.z += oldghost.momz<<8; + oldghost[playernum].x += oldghost[playernum].momx<<8; + oldghost[playernum].y += oldghost[playernum].momy<<8; + oldghost[playernum].z += oldghost[playernum].momz<<8; } - #undef MAXMOM +#undef MAXMOM // Only store the 8 most relevant bits of angle // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites // and it does not affect this mode of movement at all anyway. - if (ghost->player && ghost->player->drawangle>>24 != oldghost.angle) + if (ghost->player && ghost->player->drawangle>>24 != oldghost[playernum].angle) { - oldghost.angle = ghost->player->drawangle>>24; + oldghost[playernum].angle = ghost->player->drawangle>>24; ziptic |= GZT_ANGLE; - WRITEUINT8(demo_p,oldghost.angle); + WRITEUINT8(demo_p,oldghost[playernum].angle); } // Store the sprite frame. - if ((ghost->frame & FF_FRAMEMASK) != oldghost.frame) + if ((ghost->frame & FF_FRAMEMASK) != oldghost[playernum].frame) { - oldghost.frame = (ghost->frame & FF_FRAMEMASK); + oldghost[playernum].frame = (ghost->frame & FF_FRAMEMASK); ziptic |= GZT_FRAME; - WRITEUINT8(demo_p,oldghost.frame); + WRITEUINT8(demo_p,oldghost[playernum].frame); } if (ghost->sprite == SPR_PLAY - && ghost->sprite2 != oldghost.sprite2) + && ghost->sprite2 != oldghost[playernum].sprite2) { - oldghost.sprite2 = ghost->sprite2; + oldghost[playernum].sprite2 = ghost->sprite2; ziptic |= GZT_SPR2; - WRITEUINT8(demo_p,oldghost.sprite2); + WRITEUINT8(demo_p,oldghost[playernum].sprite2); } // Check for sprite set changes - if (ghost->sprite != oldghost.sprite) + if (ghost->sprite != oldghost[playernum].sprite) { - oldghost.sprite = ghost->sprite; - ghostext.flags |= EZT_SPRITE; + oldghost[playernum].sprite = ghost->sprite; + ghostext[playernum].flags |= EZT_SPRITE; } - if ((height = FixedDiv(ghost->height, ghost->scale)) != oldghost.height) - { - oldghost.height = height; - ghostext.flags |= EZT_HEIGHT; - } - - if (ghostext.flags) + if (ghostext[playernum].flags) { ziptic |= GZT_EXTRA; - if (ghostext.color == ghostext.lastcolor) - ghostext.flags &= ~EZT_COLOR; - if (ghostext.scale == ghostext.lastscale) - ghostext.flags &= ~EZT_SCALE; - - WRITEUINT8(demo_p,ghostext.flags); - if (ghostext.flags & EZT_COLOR) + if (ghost->player) { - WRITEUINT16(demo_p,ghostext.color); - ghostext.lastcolor = ghostext.color; - } - if (ghostext.flags & EZT_SCALE) - { - WRITEFIXED(demo_p,ghostext.scale); - ghostext.lastscale = ghostext.scale; - } - if (ghostext.flags & EZT_HIT) - { - WRITEUINT16(demo_p,ghostext.hits); - for (i = 0; i < ghostext.hits; i++) + if ( + ghostext[playernum].kartitem != ghost->player->kartstuff[k_itemtype] || + ghostext[playernum].kartamount != ghost->player->kartstuff[k_itemamount] || + ghostext[playernum].kartbumpers != ghost->player->kartstuff[k_bumper] + ) { - mobj_t *mo = ghostext.hitlist[i]; + ghostext[playernum].flags |= EZT_KART; + ghostext[playernum].kartitem = ghost->player->kartstuff[k_itemtype]; + ghostext[playernum].kartamount = ghost->player->kartstuff[k_itemamount]; + ghostext[playernum].kartbumpers = ghost->player->kartstuff[k_bumper]; + + } + } + + if (ghostext[playernum].color == ghostext[playernum].lastcolor) + ghostext[playernum].flags &= ~EZT_COLOR; + if (ghostext[playernum].scale == ghostext[playernum].lastscale) + ghostext[playernum].flags &= ~EZT_SCALE; + + WRITEUINT8(demo_p,ghostext[playernum].flags); + if (ghostext[playernum].flags & EZT_COLOR) + { + WRITEUINT16(demo_p,ghostext[playernum].color); + ghostext[playernum].lastcolor = ghostext[playernum].color; + } + if (ghostext[playernum].flags & EZT_SCALE) + { + WRITEFIXED(demo_p,ghostext[playernum].scale); + ghostext[playernum].lastscale = ghostext[playernum].scale; + } + if (ghostext[playernum].flags & EZT_HIT) + { + WRITEUINT16(demo_p,ghostext[playernum].hits); + for (i = 0; i < ghostext[playernum].hits; i++) + { + mobj_t *mo = ghostext[playernum].hitlist[i]; //WRITEUINT32(demo_p,UINT32_MAX); // reserved for some method of determining exactly which mobj this is. (mobjnum doesn't work here.) WRITEUINT32(demo_p,mo->type); WRITEUINT16(demo_p,(UINT16)mo->health); @@ -448,18 +804,20 @@ void G_WriteGhostTic(mobj_t *ghost) WRITEFIXED(demo_p,mo->z); WRITEANGLE(demo_p,mo->angle); } - Z_Free(ghostext.hitlist); - ghostext.hits = 0; - ghostext.hitlist = NULL; + Z_Free(ghostext[playernum].hitlist); + ghostext[playernum].hits = 0; + ghostext[playernum].hitlist = NULL; } - if (ghostext.flags & EZT_SPRITE) - WRITEUINT16(demo_p,oldghost.sprite); - if (ghostext.flags & EZT_HEIGHT) + if (ghostext[playernum].flags & EZT_SPRITE) + WRITEUINT16(demo_p,oldghost[playernum].sprite); + if (ghostext[playernum].flags & EZT_KART) { - height >>= FRACBITS; - WRITEINT16(demo_p, height); + WRITEINT32(demo_p, ghostext[playernum].kartitem); + WRITEINT32(demo_p, ghostext[playernum].kartamount); + WRITEINT32(demo_p, ghostext[playernum].kartbumpers); } - ghostext.flags = 0; + + ghostext[playernum].flags = 0; } if (ghost->player && ghost->player->followmobj && !(ghost->player->followmobj->sprite == SPR_NULL || (ghost->player->followmobj->flags2 & MF2_DONTDRAW))) // bloats tails runs but what can ya do @@ -473,7 +831,7 @@ void G_WriteGhostTic(mobj_t *ghost) if (ghost->player->followmobj->skin) followtic |= FZT_SKIN; - if (!(oldghost.flags2 & MF2_AMBUSH)) + if (!(oldghost[playernum].flags2 & MF2_AMBUSH)) { followtic |= FZT_SPAWNED; WRITEINT16(demo_p,ghost->player->followmobj->info->height>>FRACBITS); @@ -483,7 +841,7 @@ void G_WriteGhostTic(mobj_t *ghost) followtic |= FZT_COLORIZED; if (followtic & FZT_SKIN) WRITEUINT8(demo_p,(UINT8)(((skin_t *)(ghost->player->followmobj->skin))-skins)); - oldghost.flags2 |= MF2_AMBUSH; + oldghost[playernum].flags2 |= MF2_AMBUSH; } if (ghost->player->followmobj->scale != ghost->scale) @@ -507,7 +865,7 @@ void G_WriteGhostTic(mobj_t *ghost) *followtic_p = followtic; } else - oldghost.flags2 &= ~MF2_AMBUSH; + oldghost[playernum].flags2 &= ~MF2_AMBUSH; *ziptic_p = ziptic; @@ -520,41 +878,62 @@ void G_WriteGhostTic(mobj_t *ghost) } } +void G_ConsAllGhostTics(void) +{ + UINT8 p = READUINT8(demo_p); + + while (p != 0xFF) + { + G_ConsGhostTic(p); + p = READUINT8(demo_p); + } + + if (*demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } +} + // Uses ghost data to do consistency checks on your position. // This fixes desynchronising demos when fighting eggman. -void G_ConsGhostTic(void) +void G_ConsGhostTic(INT32 playernum) { UINT8 ziptic; - UINT16 px,py,pz,gx,gy,gz; + UINT32 px,py,pz,gx,gy,gz; mobj_t *testmo; + UINT32 syncleeway; - if (!demo_p || !demo_start) + if (!demo_p || !demo.deferstart) return; if (!(demoflags & DF_GHOST)) return; // No ghost data to use. - testmo = players[0].mo; + testmo = players[playernum].mo; // Grab ghost data. ziptic = READUINT8(demo_p); if (ziptic & GZT_XYZ) { - oldghost.x = READFIXED(demo_p); - oldghost.y = READFIXED(demo_p); - oldghost.z = READFIXED(demo_p); + oldghost[playernum].x = READFIXED(demo_p); + oldghost[playernum].y = READFIXED(demo_p); + oldghost[playernum].z = READFIXED(demo_p); + syncleeway = 0; } else { if (ziptic & GZT_MOMXY) { - oldghost.momx = READINT16(demo_p)<<8; - oldghost.momy = READINT16(demo_p)<<8; + oldghost[playernum].momx = READINT16(demo_p)<<8; + oldghost[playernum].momy = READINT16(demo_p)<<8; } if (ziptic & GZT_MOMZ) - oldghost.momz = READINT16(demo_p)<<8; - oldghost.x += oldghost.momx; - oldghost.y += oldghost.momy; - oldghost.z += oldghost.momz; + oldghost[playernum].momz = READINT16(demo_p)<<8; + oldghost[playernum].x += oldghost[playernum].momx; + oldghost[playernum].y += oldghost[playernum].momy; + oldghost[playernum].z += oldghost[playernum].momz; + syncleeway = FRACUNIT; } if (ziptic & GZT_ANGLE) demo_p++; @@ -567,7 +946,7 @@ void G_ConsGhostTic(void) { // But wait, there's more! UINT8 xziptic = READUINT8(demo_p); if (xziptic & EZT_COLOR) - demo_p += (demoversion==0x000c) ? 1 : sizeof(UINT16); + demo_p += sizeof(UINT16); if (xziptic & EZT_SCALE) demo_p += sizeof(fixed_t); if (xziptic & EZT_HIT) @@ -612,8 +991,12 @@ void G_ConsGhostTic(void) } if (xziptic & EZT_SPRITE) demo_p += sizeof(UINT16); - if (xziptic & EZT_HEIGHT) - demo_p += sizeof(INT16); + if (xziptic & EZT_KART) + { + ghostext[playernum].kartitem = READINT32(demo_p); + ghostext[playernum].kartamount = READINT32(demo_p); + ghostext[playernum].kartbumpers = READINT32(demo_p); + } } if (ziptic & GZT_FOLLOW) @@ -634,28 +1017,56 @@ void G_ConsGhostTic(void) demo_p++; demo_p += sizeof(UINT16); demo_p++; - demo_p += (demoversion==0x000c) ? 1 : sizeof(UINT16); + demo_p += sizeof(UINT16); } - // Re-synchronise - px = testmo->x>>FRACBITS; - py = testmo->y>>FRACBITS; - pz = testmo->z>>FRACBITS; - gx = oldghost.x>>FRACBITS; - gy = oldghost.y>>FRACBITS; - gz = oldghost.z>>FRACBITS; - - if (px != gx || py != gy || pz != gz) + if (testmo) { - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; + // Re-synchronise + px = testmo->x; + py = testmo->y; + pz = testmo->z; + gx = oldghost.x; + gy = oldghost.y; + gz = oldghost.z; - P_UnsetThingPosition(testmo); - testmo->x = oldghost.x; - testmo->y = oldghost.y; - P_SetThingPosition(testmo); - testmo->z = oldghost.z; + if (abs(px-gx) > syncleeway || abs(py-gy) > syncleeway || abs(pz-gz) > syncleeway) + { + ghostext[playernum].desyncframes++; + + if (ghostext[playernum].desyncframes >= 2) + { + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + + P_UnsetThingPosition(testmo); + testmo->x = oldghost[playernum].x; + testmo->y = oldghost[playernum].y; + P_SetThingPosition(testmo); + testmo->z = oldghost[playernum].z; + + if (abs(testmo->z - testmo->floorz) < 4*FRACUNIT) + testmo->z = testmo->floorz; // Sync players to the ground when they're likely supposed to be there... + + ghostext[playernum].desyncframes = 2; + } + } + else + ghostext[playernum].desyncframes = 0; + + if (players[playernum].kartstuff[k_itemtype] != ghostext[playernum].kartitem + || players[playernum].kartstuff[k_itemamount] != ghostext[playernum].kartamount + || players[playernum].kartstuff[k_bumper] != ghostext[playernum].kartbumpers) + { + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + + players[playernum].kartstuff[k_itemtype] = ghostext[playernum].kartitem; + players[playernum].kartstuff[k_itemamount] = ghostext[playernum].kartamount; + players[playernum].kartstuff[k_bumper] = ghostext[playernum].kartbumpers; + } } if (*demo_p == DEMOMARKER) @@ -674,19 +1085,53 @@ void G_GhostTicker(void) // Skip normal demo data. UINT8 ziptic = READUINT8(g->p); UINT8 xziptic = 0; + + while (ziptic != DW_END) // Get rid of extradata stuff + { + if (ziptic == 0) // Only support player 0 info for now + { + ziptic = READUINT8(g->p); + if (ziptic & DXD_SKIN) + g->p += 18; // We _could_ read this info, but it shouldn't change anything in record attack... + if (ziptic & DXD_COLOR) + g->p += 16; // Same tbh + if (ziptic & DXD_NAME) + g->p += 16; // yea + if (ziptic & DXD_FOLLOWER) + g->p += 32; // ok (32 because there's both the skin and the colour) + if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING) + I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + } + else if (ziptic == DW_RNG) + g->p += 4; // RNG seed + else + I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + + ziptic = READUINT8(g->p); + } + if (ziptic & ZT_FWD) g->p++; - if (ziptic & ZT_SIDE) - g->p++; if (ziptic & ZT_ANGLE) g->p += 2; if (ziptic & ZT_BUTTONS) g->p += 2; if (ziptic & ZT_AIMING) g->p += 2; + if (ziptic & ZT_DRIFT) + g->p += 2; + if (ziptic & ZT_LATENCY) + g->p += 1; // Grab ghost data. ziptic = READUINT8(g->p); + + if (ziptic == 0xFF) + goto skippedghosttic; // Didn't write ghost info this frame + else if (ziptic != 0) + I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + ziptic = READUINT8(g->p); + if (ziptic & GZT_XYZ) { g->oldmo.x = READFIXED(g->p); @@ -719,6 +1164,7 @@ void G_GhostTicker(void) g->mo->y = g->oldmo.y; g->mo->z = g->oldmo.z; P_SetThingPosition(g->mo); + g->mo->angle = g->oldmo.angle; g->mo->frame = g->oldmo.frame | tr_trans30<fadein) { @@ -732,7 +1178,7 @@ void G_GhostTicker(void) xziptic = READUINT8(g->p); if (xziptic & EZT_COLOR) { - g->color = (g->version==0x000c) ? READUINT8(g->p) : READUINT16(g->p); + g->color = READUINT16(g->p); switch(g->color) { default: @@ -749,9 +1195,6 @@ void G_GhostTicker(void) case GHC_FIREFLOWER: // Fireflower g->mo->color = SKINCOLOR_WHITE; break; - case GHC_NIGHTSSKIN: // not actually a colour - g->mo->skin = &skins[DEFAULTNIGHTSSKIN]; - break; } } if (xziptic & EZT_FLIP) @@ -762,59 +1205,6 @@ void G_GhostTicker(void) if (g->mo->destscale != g->mo->scale) P_SetScale(g->mo, g->mo->destscale); } - if (xziptic & EZT_THOKMASK) - { // Let's only spawn ONE of these per frame, thanks. - mobj_t *mobj; - INT32 type = -1; - if (g->mo->skin) - { - skin_t *skin = (skin_t *)g->mo->skin; - switch (xziptic & EZT_THOKMASK) - { - case EZT_THOK: - type = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem; - break; - case EZT_SPIN: - type = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem; - break; - case EZT_REV: - type = skin->revitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem; - break; - } - } - if (type != MT_NULL) - { - if (type == MT_GHOST) - { - mobj = P_SpawnGhostMobj(g->mo); // does a large portion of the work for us - mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<mo, 0, 0, -FixedDiv(FixedMul(g->mo->info->height, g->mo->scale) - g->mo->height,3*FRACUNIT), MT_THOK); - mobj->sprite = states[mobjinfo[type].spawnstate].sprite; - mobj->frame = (states[mobjinfo[type].spawnstate].frame & FF_FRAMEMASK) | tr_trans60<color = g->mo->color; - mobj->skin = g->mo->skin; - P_SetScale(mobj, (mobj->destscale = g->mo->scale)); - - if (type == MT_THOK) // spintrail-specific modification for MT_THOK - { - mobj->frame = FF_TRANS80; - mobj->fuse = mobj->tics; - } - mobj->tics = -1; // nope. - } - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_UnsetThingPosition(mobj); - mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... - P_SetThingPosition(mobj); - if (!mobj->fuse) - mobj->fuse = 8; - P_SetTarget(&mobj->target, g->mo); - } - } if (xziptic & EZT_HIT) { // Spawn hit poofs for killing things! UINT16 i, count = READUINT16(g->p), health; @@ -844,13 +1234,14 @@ void G_GhostTicker(void) } if (xziptic & EZT_SPRITE) g->mo->sprite = READUINT16(g->p); - if (xziptic & EZT_HEIGHT) - { - fixed_t temp = READINT16(g->p)<mo->height = FixedMul(temp, g->mo->scale); - } + if (xziptic & EZT_KART) + g->p += 12; // kartitem, kartamount, kartbumpers } + if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here. + I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + +skippedghosttic: // Tick ghost colors (Super and Mario Invincibility flashing) switch(g->color) { @@ -919,7 +1310,7 @@ void G_GhostTicker(void) follow->sprite = READUINT16(g->p); follow->frame = (READUINT8(g->p)) | (g->mo->frame & FF_TRANSMASK); follow->angle = g->mo->angle; - follow->color = (g->version==0x000c) ? READUINT8(g->p) : READUINT16(g->p); + follow->color = READUINT16(g->p); if (!(followtic & FZT_SPAWNED)) { @@ -961,6 +1352,202 @@ void G_GhostTicker(void) } } +// Demo rewinding functions +typedef struct rewindinfo_s { + tic_t leveltime; + + struct { + boolean ingame; + player_t player; + mobj_t mobj; + } playerinfo[MAXPLAYERS]; + + struct rewindinfo_s *prev; +} rewindinfo_t; + +static tic_t currentrewindnum; +static rewindinfo_t *rewindhead = NULL; // Reverse chronological order + +void G_InitDemoRewind(void) +{ + CL_ClearRewinds(); + + while (rewindhead) + { + rewindinfo_t *p = rewindhead->prev; + Z_Free(rewindhead); + rewindhead = p; + } + + currentrewindnum = 0; +} + +void G_StoreRewindInfo(void) +{ + static UINT8 timetolog = 8; + rewindinfo_t *info; + size_t i; + + if (timetolog-- > 0) + return; + timetolog = 8; + + info = Z_Calloc(sizeof(rewindinfo_t), PU_STATIC, NULL); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + info->playerinfo[i].ingame = false; + continue; + } + + info->playerinfo[i].ingame = true; + memcpy(&info->playerinfo[i].player, &players[i], sizeof(player_t)); + if (players[i].mo) + memcpy(&info->playerinfo[i].mobj, players[i].mo, sizeof(mobj_t)); + } + + info->leveltime = leveltime; + info->prev = rewindhead; + rewindhead = info; +} + +void G_PreviewRewind(tic_t previewtime) +{ + SINT8 i; + size_t j; + fixed_t tweenvalue = 0; + rewindinfo_t *info = rewindhead, *next_info = rewindhead; + + if (!info) + return; + + while (info->leveltime > previewtime && info->prev) + { + next_info = info; + info = info->prev; + } + if (info != next_info) + tweenvalue = FixedDiv(previewtime - info->leveltime, next_info->leveltime - info->leveltime); + + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + if (info->playerinfo[i].player.mo) + { + //@TODO spawn temp object to act as a player display + } + + continue; + } + + if (!info->playerinfo[i].ingame || !info->playerinfo[i].player.mo) + { + if (players[i].mo) + players[i].mo->drawflags |= MFD_DONTDRAW; + + continue; + } + + if (!players[i].mo) + continue; //@TODO spawn temp object to act as a player display + + players[i].mo->drawflags &= ~MFD_DONTDRAW; + + P_UnsetThingPosition(players[i].mo); +#define TWEEN(pr) info->playerinfo[i].mobj.pr + FixedMul((INT32) (next_info->playerinfo[i].mobj.pr - info->playerinfo[i].mobj.pr), tweenvalue) + players[i].mo->x = TWEEN(x); + players[i].mo->y = TWEEN(y); + players[i].mo->z = TWEEN(z); + players[i].mo->angle = TWEEN(angle); +#undef TWEEN + P_SetThingPosition(players[i].mo); + + players[i].frameangle = info->playerinfo[i].player.frameangle + FixedMul((INT32) (next_info->playerinfo[i].player.frameangle - info->playerinfo[i].player.frameangle), tweenvalue); + + players[i].mo->sprite = info->playerinfo[i].mobj.sprite; + players[i].mo->frame = info->playerinfo[i].mobj.frame; + + players[i].realtime = info->playerinfo[i].player.realtime; + for (j = 0; j < NUMKARTSTUFF; j++) + players[i].kartstuff[j] = info->playerinfo[i].player.kartstuff[j]; + } + + for (i = splitscreen; i >= 0; i--) + P_ResetCamera(&players[displayplayers[i]], &camera[i]); +} + +void G_ConfirmRewind(tic_t rewindtime) +{ + SINT8 i; + tic_t j; + boolean oldmenuactive = menuactive, oldsounddisabled = sound_disabled; + + INT32 olddp1 = displayplayers[0], olddp2 = displayplayers[1], olddp3 = displayplayers[2], olddp4 = displayplayers[3]; + UINT8 oldss = splitscreen; + + menuactive = false; // Prevent loops + + CV_StealthSetValue(&cv_renderview, 0); + + if (rewindtime <= starttime) + { + demo.rewinding = false; + G_DoPlayDemo(NULL); // Restart the current demo + } + else + { + rewind_t *rewind; + sound_disabled = true; // Prevent sound spam + demo.rewinding = true; + + rewind = CL_RewindToTime(rewindtime); + + if (rewind) + { + demo_p = demobuffer + rewind->demopos; + memcpy(oldcmd, rewind->oldcmd, sizeof (oldcmd)); + memcpy(oldghost, rewind->oldghost, sizeof (oldghost)); + paused = false; + } + else + { + demo.rewinding = true; + G_DoPlayDemo(NULL); // Restart the current demo + } + } + + for (j = 0; j < rewindtime && leveltime < rewindtime; j++) + { + G_Ticker((j % NEWTICRATERATIO) == 0); + } + + demo.rewinding = false; + menuactive = oldmenuactive; // Bring the menu back up + sound_disabled = oldsounddisabled; // Re-enable SFX + + wipegamestate = gamestate; // No fading back in! + + COM_BufInsertText("renderview on\n"); + + if (demo.freecam) + return; // don't touch from there + + splitscreen = oldss; + displayplayers[0] = olddp1; + displayplayers[1] = olddp2; + displayplayers[2] = olddp3; + displayplayers[3] = olddp4; + R_ExecuteSetViewSize(); + G_ResetViews(); + + for (i = splitscreen; i >= 0; i--) + P_ResetCamera(&players[displayplayers[i]], &camera[i]); +} + void G_ReadMetalTic(mobj_t *metal) { UINT8 ziptic; @@ -1159,7 +1746,7 @@ void G_ReadMetalTic(mobj_t *metal) follow->sprite = READUINT16(metal_p); follow->frame = READUINT32(metal_p); // NOT & FF_FRAMEMASK here, so 32 bits follow->angle = metal->angle; - follow->color = (metalversion==0x000c) ? READUINT8(metal_p) : READUINT16(metal_p); + follow->color = READUINT16(metal_p); if (!(followtic & FZT_SPAWNED)) { @@ -1368,7 +1955,8 @@ void G_RecordDemo(const char *name) strcpy(demoname, name); strcat(demoname, ".lmp"); - maxsize = 1024*1024; + //@TODO make a maxdemosize cvar + maxsize = 1024*1024*2; if (M_CheckParm("-maxdemo") && M_IsNextParm()) maxsize = atoi(M_GetNextParm()) * 1024; // if (demobuffer) @@ -1394,16 +1982,25 @@ void G_RecordMetal(void) void G_BeginRecording(void) { - UINT8 i; + UINT8 i, p; char name[MAXCOLORNAME+1]; player_t *player = &players[consoleplayer]; + char *filename; + UINT8 totalfiles; + UINT8 *m; + if (demo_p) return; memset(name,0,sizeof(name)); demo_p = demobuffer; - demoflags = DF_GHOST|(modeattacking<important) + { + nameonly(( filename = va("%s", wadfiles[i]->filename) )); + WRITESTRINGN(demo_p, filename, 64); + WRITEMEM(demo_p, wadfiles[i]->md5sum, 16); + + totalfiles++; + } + + WRITEUINT8(m, totalfiles); + switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - WRITEUINT32(demo_p,0); // score - WRITEUINT16(demo_p,0); // rings - break; - case ATTACKING_NIGHTS: // 2 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - WRITEUINT32(demo_p,0); // score - break; - default: // 3 - break; + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + demotime_p = demo_p; + WRITEUINT32(demo_p,UINT32_MAX); // time + WRITEUINT32(demo_p,UINT32_MAX); // lap + break; + case ATTACKING_CAPSULES: // 2 + demotime_p = demo_p; + WRITEUINT32(demo_p,UINT32_MAX); // time + break; + default: // 3 + break; } + // Reserved for extrainfo location from start of file + demoinfo_p = demo_p; + WRITEUINT32(demo_p, 0); + + // Save netvars + CV_SaveNetVars(&demo_p, true); + + // Now store some info for each in-game player + + // Lat' 12/05/19: Do note that for the first game you load, everything that gets saved here is total garbage; + // The name will always be Player , the skin sonic, the color None and the follower 0. This is only correct on subsequent games. + // In the case of said first game, the skin and the likes are updated with Got_NameAndColor, which are then saved in extradata for the demo with DXD_SKIN in r_things.c for instance. + + + for (p = 0; p < MAXPLAYERS; p++) { + if (playeringame[p]) { + player = &players[p]; + + WRITEUINT8(demo_p, p | (player->spectator ? DEMO_SPECTATOR : 0)); + + // Name + memset(name, 0, 16); + strncpy(name, player_names[p], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Skin + memset(name, 0, 16); + strncpy(name, skins[player->skin].name, 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Color + memset(name, 0, 16); + strncpy(name, KartColor_Names[player->skincolor], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Save follower's skin name + // PS: We must check for 'follower' to determine if the followerskin is valid. It's going to be 0 if we don't have a follower, but 0 is also absolutely a valid follower! + // Doesn't really matter if the follower mobj is valid so long as it exists in a way or another. + + memset(name, 0, 16); + if (player->follower) + strncpy(name, followers[player->followerskin].skinname, 16); + else + strncpy(name, "None", 16); // Say we don't have one, then. + + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Save follower's colour + memset(name, 0, 16); + strncpy(name, Followercolor_cons_t[player->followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" + M_Memcpy(demo_p, name, 16); + demo_p += 16; + + // Score, since Kart uses this to determine where you start on the map + WRITEUINT32(demo_p, player->score); + + // Power Levels + WRITEUINT16(demo_p, clientpowerlevels[p][G_BattleGametype() ? PWRLV_BATTLE : PWRLV_RACE]); + + // Kart speed and weight + WRITEUINT8(demo_p, skins[player->skin].kartspeed); + WRITEUINT8(demo_p, skins[player->skin].kartweight); + + // And mobjtype_t is best with UINT32 too... + WRITEUINT32(demo_p, player->followitem); + } + } + + WRITEUINT8(demo_p, 0xFF); // Denote the end of the player listing + + // player lua vars, always saved even if empty + LUA_ArchiveDemo(); + WRITEUINT32(demo_p,P_GetInitSeed()); - // Name - for (i = 0; i < 16 && cv_playername.string[i]; i++) - name[i] = cv_playername.string[i]; - for (; i < 16; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,16); - demo_p += 16; - - // Skin - for (i = 0; i < 16 && cv_skin.string[i]; i++) - name[i] = cv_skin.string[i]; - for (; i < 16; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,16); - demo_p += 16; - - // Color - for (i = 0; i < MAXCOLORNAME && cv_playercolor.string[i]; i++) - name[i] = cv_playercolor.string[i]; - for (; i < MAXCOLORNAME; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,MAXCOLORNAME); - demo_p += MAXCOLORNAME; - - // Stats - WRITEUINT8(demo_p,player->charability); - WRITEUINT8(demo_p,player->charability2); - WRITEUINT8(demo_p,player->actionspd>>FRACBITS); - WRITEUINT8(demo_p,player->mindash>>FRACBITS); - WRITEUINT8(demo_p,player->maxdash>>FRACBITS); - WRITEUINT8(demo_p,player->normalspeed>>FRACBITS); - WRITEUINT8(demo_p,player->runspeed>>FRACBITS); - WRITEUINT8(demo_p,player->thrustfactor); - WRITEUINT8(demo_p,player->accelstart); - WRITEUINT8(demo_p,player->acceleration); - WRITEUINT8(demo_p,player->height>>FRACBITS); - WRITEUINT8(demo_p,player->spinheight>>FRACBITS); - WRITEUINT8(demo_p,player->camerascale>>FRACBITS); - WRITEUINT8(demo_p,player->shieldscale>>FRACBITS); - - // Trying to convert it back to % causes demo desync due to precision loss. - // Don't do it. - WRITEFIXED(demo_p, player->jumpfactor); - - // And mobjtype_t is best with UINT32 too... - WRITEUINT32(demo_p, player->followitem); - - // Save pflag data - see SendWeaponPref() - { - UINT8 buf = 0; - pflags_t pflags = 0; - if (cv_flipcam.value) - { - buf |= 0x01; - pflags |= PF_FLIPCAM; - } - if (cv_analog[0].value) - { - buf |= 0x02; - pflags |= PF_ANALOGMODE; - } - if (cv_directionchar[0].value) - { - buf |= 0x04; - pflags |= PF_DIRECTIONCHAR; - } - if (cv_autobrake.value) - { - buf |= 0x08; - pflags |= PF_AUTOBRAKE; - } - if (cv_usejoystick.value) - buf |= 0x10; - CV_SetValue(&cv_showinputjoy, !!(cv_usejoystick.value)); - - WRITEUINT8(demo_p,buf); - player->pflags = pflags; - } - - // Save netvar data - CV_SaveNetVars(&demo_p); - memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldghost,0,sizeof(oldghost)); memset(&ghostext,0,sizeof(ghostext)); - ghostext.lastcolor = ghostext.color = GHC_NORMAL; - ghostext.lastscale = ghostext.scale = FRACUNIT; - - if (player->mo) + + for (i = 0; i < MAXPLAYERS; i++) { - oldghost.x = player->mo->x; - oldghost.y = player->mo->y; - oldghost.z = player->mo->z; - oldghost.angle = player->mo->angle>>24; + ghostext[i].lastcolor = ghostext[i].color = GHC_NORMAL; + ghostext[i].lastscale = ghostext[i].scale = FRACUNIT; - // preticker started us gravity flipped - if (player->mo->eflags & MFE_VERTICALFLIP) - ghostext.flags |= EZT_FLIP; + if (players[i].mo) + { + oldghost[i].x = players[i].mo->x; + oldghost[i].y = players[i].mo->y; + oldghost[i].z = players[i].mo->z; + oldghost[i].angle = players[i].mo->angle; + + // preticker started us gravity flipped + if (players[i].mo->eflags & MFE_VERTICALFLIP) + ghostext[i].flags |= EZT_FLIP; + } } } @@ -1575,25 +2192,218 @@ void G_BeginMetal(void) oldmetal.angle = mo->angle>>24; } -void G_SetDemoTime(UINT32 ptime, UINT32 pscore, UINT16 prings) +void G_WriteStanding(UINT8 ranking, char *name, INT32 skinnum, UINT8 color, UINT32 val) { - if (!demorecording || !demotime_p) + char temp[16]; + + if (demoinfo_p && *(UINT32 *)demoinfo_p == 0) + { + WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker + *(UINT32 *)demoinfo_p = demo_p - demobuffer; + } + + WRITEUINT8(demo_p, DW_STANDING); + WRITEUINT8(demo_p, ranking); + + // Name + memset(temp, 0, 16); + strncpy(temp, name, 16); + M_Memcpy(demo_p,temp,16); + demo_p += 16; + + // Skin + memset(temp, 0, 16); + strncpy(temp, skins[skinnum].name, 16); + M_Memcpy(demo_p,temp,16); + demo_p += 16; + + // Color + memset(temp, 0, 16); + strncpy(temp, KartColor_Names[color], 16); + M_Memcpy(demo_p,temp,16); + demo_p += 16; + + // Score/time/whatever + WRITEUINT32(demo_p, val); +} + +void G_SetDemoTime(UINT32 ptime, UINT32 plap) +{ + if (!demo.recording || !demotime_p) return; - if (demoflags & DF_RECORDATTACK) + if (demoflags & DF_TIMEATTACK) { WRITEUINT32(demotime_p, ptime); - WRITEUINT32(demotime_p, pscore); - WRITEUINT16(demotime_p, prings); + WRITEUINT32(demotime_p, plap); demotime_p = NULL; } - else if (demoflags & DF_NIGHTSATTACK) + else if (demoflags & DF_BREAKTHECAPSULES) { WRITEUINT32(demotime_p, ptime); - WRITEUINT32(demotime_p, pscore); + (void)plap; demotime_p = NULL; } } +static void G_LoadDemoExtraFiles(UINT8 **pp) +{ + UINT8 totalfiles; + char filename[MAX_WADPATH]; + UINT8 md5sum[16]; + filestatus_t ncs; + boolean toomany = false; + boolean alreadyloaded; + UINT8 i, j; + + totalfiles = READUINT8((*pp)); + for (i = 0; i < totalfiles; ++i) + { + if (toomany) + SKIPSTRING((*pp)); + else + { + strlcpy(filename, (char *)(*pp), sizeof filename); + SKIPSTRING((*pp)); + } + READMEM((*pp), md5sum, 16); + + if (!toomany) + { + alreadyloaded = false; + + for (j = 0; j < numwadfiles; ++j) + { + if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0) + { + alreadyloaded = true; + break; + } + } + + if (alreadyloaded) + continue; + + if (numwadfiles >= MAX_WADFILES) + toomany = true; + else + ncs = findfile(filename, md5sum, false); + + if (toomany) + { + CONS_Alert(CONS_WARNING, M_GetText("Too many files loaded to add anymore for demo playback\n")); + if (!CON_Ready()) + M_StartMessage(M_GetText("There are too many files loaded to add this demo's addons.\n\nDemo playback may desync.\n\nPress ESC\n"), NULL, MM_NOTHING); + } + else if (ncs != FS_FOUND) + { + if (ncs == FS_NOTFOUND) + CONS_Alert(CONS_NOTICE, M_GetText("You do not have a copy of %s\n"), filename); + else if (ncs == FS_MD5SUMBAD) + CONS_Alert(CONS_NOTICE, M_GetText("Checksum mismatch on %s\n"), filename); + else + CONS_Alert(CONS_NOTICE, M_GetText("Unknown error finding file %s\n"), filename); + + if (!CON_Ready()) + M_StartMessage(M_GetText("There were errors trying to add this demo's addons. Check the console for more information.\n\nDemo playback may desync.\n\nPress ESC\n"), NULL, MM_NOTHING); + } + else + { + P_AddWadFile(filename); + } + } + } +} + +static void G_SkipDemoExtraFiles(UINT8 **pp) +{ + UINT8 totalfiles; + UINT8 i; + + totalfiles = READUINT8((*pp)); + for (i = 0; i < totalfiles; ++i) + { + SKIPSTRING((*pp));// file name + (*pp) += 16;// md5 + } +} + +// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's. +// Enabling quick prevents filesystem checks to see if needed files are available to load. +static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick) +{ + UINT8 totalfiles, filesloaded, nmusfilecount; + char filename[MAX_WADPATH]; + UINT8 md5sum[16]; + boolean toomany = false; + boolean alreadyloaded; + UINT8 i, j; + UINT8 error = 0; + + totalfiles = READUINT8((*pp)); + filesloaded = 0; + for (i = 0; i < totalfiles; ++i) + { + if (toomany) + SKIPSTRING((*pp)); + else + { + strlcpy(filename, (char *)(*pp), sizeof filename); + SKIPSTRING((*pp)); + } + READMEM((*pp), md5sum, 16); + + if (!toomany) + { + alreadyloaded = false; + nmusfilecount = 0; + + for (j = 0; j < numwadfiles; ++j) + { + if (wadfiles[j]->important && j > mainwads) + nmusfilecount++; + else + continue; + + if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0) + { + alreadyloaded = true; + + if (i != nmusfilecount-1 && error < DFILE_ERROR_OUTOFORDER) + error |= DFILE_ERROR_OUTOFORDER; + + break; + } + } + + if (alreadyloaded) + { + filesloaded++; + continue; + } + + if (numwadfiles >= MAX_WADFILES) + error = DFILE_ERROR_CANNOTLOAD; + else if (!quick && findfile(filename, md5sum, false) != FS_FOUND) + error = DFILE_ERROR_CANNOTLOAD; + else if (error < DFILE_ERROR_INCOMPLETEOUTOFORDER) + error |= DFILE_ERROR_NOTLOADED; + } else + error = DFILE_ERROR_CANNOTLOAD; + } + + // Get final file count + nmusfilecount = 0; + + for (j = 0; j < numwadfiles; ++j) + if (wadfiles[j]->important && j > mainwads) + nmusfilecount++; + + if (!error && filesloaded < nmusfilecount) + error = DFILE_ERROR_EXTRAFILES; + + return error; +} + // Returns bitfield: // 1 == new demo has lower time // 2 == new demo has higher score @@ -1602,12 +2412,13 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) { UINT8 *buffer,*p; UINT8 flags; - UINT32 oldtime, newtime, oldscore, newscore; - UINT16 oldrings, newrings, oldversion; + UINT32 oldtime, newtime, oldlap, newlap; + UINT16 oldversion; size_t bufsize ATTRUNUSED; UINT8 c; UINT16 s ATTRUNUSED; UINT8 aflags = 0; + boolean uselaps = false; // load the new file FIL_DefaultExtension(newname, ".lmp"); @@ -1623,30 +2434,28 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) c = READUINT8(p); // SUBVERSION I_Assert(c == SUBVERSION); s = READUINT16(p); - I_Assert(s >= 0x000c); + I_Assert(s == DEMOVERSION); + p += 64; // full demo title p += 16; // demo checksum I_Assert(!memcmp(p, "PLAY", 4)); p += 4; // PLAY p += 2; // gamemap p += 16; // map md5 flags = READUINT8(p); // demoflags + p++; // gametype + G_SkipDemoExtraFiles(&p); aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK); I_Assert(aflags); - if (flags & DF_RECORDATTACK) - { - newtime = READUINT32(p); - newscore = READUINT32(p); - newrings = READUINT16(p); - } - else if (flags & DF_NIGHTSATTACK) - { - newtime = READUINT32(p); - newscore = READUINT32(p); - newrings = 0; - } - else // appease compiler - return 0; + + if (flags & DF_TIMEATTACK) + uselaps = true; // get around uninitalized error + + newtime = READUINT32(p); + if (uselaps) + newlap = READUINT32(p); + else + newlap = UINT32_MAX; Z_Free(buffer); @@ -1672,7 +2481,6 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) switch(oldversion) // demoversion { case DEMOVERSION: // latest always supported - case 0x000c: // all that changed between then and now was longer color name break; // too old, cannot support. default: @@ -1680,6 +2488,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) Z_Free(buffer); return UINT8_MAX; } + p += 64; // full demo title p += 16; // demo checksum if (memcmp(p, "PLAY", 4)) { @@ -1687,48 +2496,212 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) Z_Free(buffer); return UINT8_MAX; } p += 4; // "PLAY" - if (oldversion <= 0x0008) - p++; // gamemap - else - p += 2; // gamemap + p += 2; // gamemap p += 16; // mapmd5 flags = READUINT8(p); + p++; // gametype + G_SkipDemoExtraFiles(&p); if (!(flags & aflags)) { CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname); Z_Free(buffer); return UINT8_MAX; } - if (flags & DF_RECORDATTACK) - { - oldtime = READUINT32(p); - oldscore = READUINT32(p); - oldrings = READUINT16(p); - } - else if (flags & DF_NIGHTSATTACK) - { - oldtime = READUINT32(p); - oldscore = READUINT32(p); - oldrings = 0; - } - else // appease compiler - return UINT8_MAX; + + oldtime = READUINT32(p); + if (uselaps) + oldlap = READUINT32(p); + else + oldlap = 0; Z_Free(buffer); c = 0; - if (newtime < oldtime - || (newtime == oldtime && (newscore > oldscore || newrings > oldrings))) - c |= 1; // Better time - if (newscore > oldscore - || (newscore == oldscore && newtime < oldtime)) - c |= 1<<1; // Better score - if (newrings > oldrings - || (newrings == oldrings && newtime < oldtime)) - c |= 1<<2; // Better rings + + if (uselaps) + { + if (newtime < oldtime + || (newtime == oldtime && (newlap < oldlap))) + c |= 1; // Better time + if (newlap < oldlap + || (newlap == oldlap && newtime < oldtime)) + c |= 1<<1; // Better lap time + } + else + { + if (newtime < oldtime) + c |= 1; // Better time + } + return c; } +void G_LoadDemoInfo(menudemo_t *pdemo) +{ + UINT8 *infobuffer, *info_p, *extrainfo_p; + UINT8 version, subversion, pdemoflags; + UINT16 pdemoversion, count; + + if (!FIL_ReadFile(pdemo->filepath, &infobuffer)) + { + CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + + return; + } + + info_p = infobuffer; + + if (memcmp(info_p, DEMOHEADER, 12)) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is not a SRB2Kart replay file.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + Z_Free(infobuffer); + return; + } + + pdemo->type = MD_LOADED; + + info_p += 12; // DEMOHEADER + + version = READUINT8(info_p); + subversion = READUINT8(info_p); + pdemoversion = READUINT16(info_p); + + switch(pdemoversion) + { + case DEMOVERSION: // latest always supported + // demo title + M_Memcpy(pdemo->title, info_p, 64); + info_p += 64; + + break; + // too old, cannot support. + default: + CONS_Alert(CONS_ERROR, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + Z_Free(infobuffer); + return; + } + + if (version != VERSION || subversion != SUBVERSION) + pdemo->type = MD_OUTDATED; + + info_p += 16; // demo checksum + if (memcmp(info_p, "PLAY", 4)) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + Z_Free(infobuffer); + return; + } + info_p += 4; // "PLAY" + pdemo->map = READINT16(info_p); + info_p += 16; // mapmd5 + + pdemoflags = READUINT8(info_p); + + // temp? + if (!(pdemoflags & DF_MULTIPLAYER)) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is not a multiplayer replay and can't be listed on this menu fully yet.\n"), pdemo->filepath); + Z_Free(infobuffer); + return; + } + + pdemo->gametype = READUINT8(info_p); + + pdemo->addonstatus = G_CheckDemoExtraFiles(&info_p, true); + info_p += 4; // RNG seed + + extrainfo_p = infobuffer + READUINT32(info_p); + + // Pared down version of CV_LoadNetVars to find the kart speed + pdemo->kartspeed = KARTSPEED_NORMAL; // Default to normal speed + count = READUINT16(info_p); + while (count--) + { + UINT16 netid; + char *svalue; + + netid = READUINT16(info_p); + svalue = (char *)info_p; + SKIPSTRING(info_p); + info_p++; // stealth + + if (netid == cv_kartspeed.netid) + { + UINT8 j; + for (j = 0; kartspeed_cons_t[j].strvalue; j++) + if (!stricmp(kartspeed_cons_t[j].strvalue, svalue)) + pdemo->kartspeed = kartspeed_cons_t[j].value; + } + else if (netid == cv_basenumlaps.netid && pdemo->gametype == GT_RACE) + pdemo->numlaps = atoi(svalue); + } + + if (pdemoflags & DF_ENCORE) + pdemo->kartspeed |= DF_ENCORE; + + /*// Temporary info until this is actually present in replays. + (void)extrainfo_p; + sprintf(pdemo->winnername, "transrights420"); + pdemo->winnerskin = 1; + pdemo->winnercolor = SKINCOLOR_MOONSET; + pdemo->winnertime = 6666;*/ + + // Read standings! + count = 0; + + while (READUINT8(extrainfo_p) == DW_STANDING) // Assume standings are always first in the extrainfo + { + INT32 i; + char temp[16]; + + pdemo->standings[count].ranking = READUINT8(extrainfo_p); + + // Name + M_Memcpy(pdemo->standings[count].name, extrainfo_p, 16); + extrainfo_p += 16; + + // Skin + M_Memcpy(temp,extrainfo_p,16); + extrainfo_p += 16; + pdemo->standings[count].skin = UINT8_MAX; + for (i = 0; i < numskins; i++) + if (stricmp(skins[i].name, temp) == 0) + { + pdemo->standings[count].skin = i; + break; + } + + // Color + M_Memcpy(temp,extrainfo_p,16); + extrainfo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i],temp)) // SRB2kart + { + pdemo->standings[count].color = i; + break; + } + + // Score/time/whatever + pdemo->standings[count].timeorscore = READUINT32(extrainfo_p); + + count++; + + if (count >= MAXPLAYERS) + break; //@TODO still cycle through the rest of these if extra demo data is ever used + } + + // I think that's everything we need? + Z_Free(infobuffer); +} + // // G_PlayDemo // @@ -1736,66 +2709,92 @@ void G_DeferedPlayDemo(const char *name) { COM_BufAddText("playdemo \""); COM_BufAddText(name); - COM_BufAddText("\"\n"); + COM_BufAddText("\" -addfiles\n"); } // // Start a demo from a .LMP file or from a wad resource // + +#define SKIPERRORS + void G_DoPlayDemo(char *defdemoname) { UINT8 i; lumpnum_t l; - char skin[17],color[MAXCOLORNAME+1],*n,*pdemoname; - UINT8 version,subversion,charability,charability2,thrustfactor,accelstart,acceleration,cnamelen; + char skin[17],color[MAXCOLORNAME+1],follower[17],*n,*pdemoname; + UINT8 version,subversion; pflags_t pflags; UINT32 randseed, followitem; - fixed_t camerascale,shieldscale,actionspd,mindash,maxdash,normalspeed,runspeed,jumpfactor,height,spinheight; char msg[1024]; + boolean spectator; + UINT8 slots[MAXPLAYERS], kartspeed[MAXPLAYERS], kartweight[MAXPLAYERS], numslots = 0; +#if defined(SKIPERRORS) && !defined(DEVELOP) + boolean skiperrors = false; +#endif + + G_InitDemoRewind(); skin[16] = '\0'; + follower[16] = '\0'; color[MAXCOLORNAME] = '\0'; - n = defdemoname+strlen(defdemoname); - while (*n != '/' && *n != '\\' && n != defdemoname) - n--; - if (n != defdemoname) - n++; - pdemoname = ZZ_Alloc(strlen(n)+1); - strcpy(pdemoname,n); - - // Internal if no extension, external if one exists - if (FIL_CheckExtension(defdemoname)) + // No demo name means we're restarting the current demo + if (defdemoname == NULL) { - //FIL_DefaultExtension(defdemoname, ".lmp"); - if (!FIL_ReadFile(defdemoname, &demobuffer)) + demo_p = demobuffer; + pdemoname = ZZ_Alloc(1); // Easier than adding checks for this everywhere it's freed + } + else + { + n = defdemoname+strlen(defdemoname); + while (*n != '/' && *n != '\\' && n != defdemoname) + n--; + if (n != defdemoname) + n++; + pdemoname = ZZ_Alloc(strlen(n)+1); + strcpy(pdemoname,n); + + M_SetPlaybackMenuPointer(); + + // Internal if no extension, external if one exists + if (FIL_CheckExtension(defdemoname)) { - snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); + //FIL_DefaultExtension(defdemoname, ".lmp"); + if (!FIL_ReadFile(defdemoname, &demobuffer)) + { + snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + gameaction = ga_nothing; + M_StartMessage(msg, NULL, MM_NOTHING); + return; + } + demo_p = demobuffer; + } + // load demo resource from WAD + else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) + { + snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); CONS_Alert(CONS_ERROR, "%s", msg); gameaction = ga_nothing; M_StartMessage(msg, NULL, MM_NOTHING); return; } - demo_p = demobuffer; + else // it's an internal demo + { + demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC); +#if defined(SKIPERRORS) && !defined(DEVELOP) + skiperrors = true; // SRB2Kart: Don't print warnings for staff ghosts, since they'll inevitably happen when we make bugfixes/changes... +#endif + } } - // load demo resource from WAD - else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) - { - snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - else // it's an internal demo - demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC); // read demo header gameaction = ga_nothing; - demoplayback = true; + demo.playback = true; if (memcmp(demo_p, DEMOHEADER, 12)) { - snprintf(msg, 1024, M_GetText("%s is not a SRB2 replay file.\n"), pdemoname); + snprintf(msg, 1024, M_GetText("%s is not a SRB2Kart replay file.\n"), pdemoname); CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); @@ -1808,15 +2807,10 @@ void G_DoPlayDemo(char *defdemoname) version = READUINT8(demo_p); subversion = READUINT8(demo_p); - demoversion = READUINT16(demo_p); - switch(demoversion) + demo.version = READUINT16(demo_p); + switch(demo.version) { case DEMOVERSION: // latest always supported - cnamelen = MAXCOLORNAME; - break; - // all that changed between then and now was longer color name - case 0x000c: - cnamelen = 16; break; // too old, cannot support. default: @@ -1829,7 +2823,13 @@ void G_DoPlayDemo(char *defdemoname) titledemo = false; return; } + + // demo title + M_Memcpy(demo.titlename, demo_p, 64); + demo_p += 64; + demo_p += 16; // demo checksum + if (memcmp(demo_p, "PLAY", 4)) { snprintf(msg, 1024, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemoname); @@ -1846,76 +2846,103 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 16; // mapmd5 demoflags = READUINT8(demo_p); + gametype = READUINT8(demo_p); + + if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running. + G_SkipDemoExtraFiles(&demo_p); + else if (demo.loadfiles) + G_LoadDemoExtraFiles(&demo_p); + else if (demo.ignorefiles) + G_SkipDemoExtraFiles(&demo_p); + else + { + UINT8 error = G_CheckDemoExtraFiles(&demo_p, false); + + if (error) + { + switch (error) + { + case DFILE_ERROR_NOTLOADED: + snprintf(msg, 1024, + M_GetText("Required files for this demo are not loaded.\n\nUse\n\"playdemo %s -addfiles\"\nto load them and play the demo.\n"), + pdemoname); + break; + + case DFILE_ERROR_OUTOFORDER: + snprintf(msg, 1024, + M_GetText("Required files for this demo are loaded out of order.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + snprintf(msg, 1024, + M_GetText("Required files for this demo are not loaded, and some are out of order.\n\nUse\n\"playdemo %s -addfiles\"\nto load needed files and play the demo.\n"), + pdemoname); + break; + + case DFILE_ERROR_CANNOTLOAD: + snprintf(msg, 1024, + M_GetText("Required files for this demo cannot be loaded.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + + case DFILE_ERROR_EXTRAFILES: + snprintf(msg, 1024, + M_GetText("You have additional files loaded beyond the demo's file list.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + } + + CONS_Alert(CONS_ERROR, "%s", msg); + if (!CON_Ready()) // In the console they'll just see the notice there! No point pulling them out. + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + } + modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT; + multiplayer = !!(demoflags & DF_MULTIPLAYER); CON_ToggleOff(); - hu_demoscore = 0; hu_demotime = UINT32_MAX; - hu_demorings = 0; + hu_demolap = UINT32_MAX; switch (modeattacking) { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - hu_demotime = READUINT32(demo_p); - hu_demoscore = READUINT32(demo_p); - hu_demorings = READUINT16(demo_p); - break; - case ATTACKING_NIGHTS: // 2 - hu_demotime = READUINT32(demo_p); - hu_demoscore = READUINT32(demo_p); - break; - default: // 3 - modeattacking = ATTACKING_NONE; - break; + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + hu_demotime = READUINT32(demo_p); + hu_demolap = READUINT32(demo_p); + break; + case ATTACKING_CAPSULES: // 2 + hu_demotime = READUINT32(demo_p); + break; + default: // 3 + modeattacking = ATTACKING_NONE; + break; } // Random seed randseed = READUINT32(demo_p); - // Player name - M_Memcpy(player_names[0],demo_p,16); - demo_p += 16; + demo_p += 4; // Extrainfo location - // Skin - M_Memcpy(skin,demo_p,16); - demo_p += 16; - - // Color - M_Memcpy(color,demo_p,cnamelen); - demo_p += cnamelen; - - charability = READUINT8(demo_p); - charability2 = READUINT8(demo_p); - actionspd = (fixed_t)READUINT8(demo_p)< NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->menuflags & LF2_EXISTSHACK)) { - UINT8 buf = READUINT8(demo_p); - pflags = 0; - if (buf & 0x01) - pflags |= PF_FLIPCAM; - if (buf & 0x02) - pflags |= PF_ANALOGMODE; - if (buf & 0x04) - pflags |= PF_DIRECTIONCHAR; - if (buf & 0x08) - pflags |= PF_AUTOBRAKE; - CV_SetValue(&cv_showinputjoy, !!(buf & 0x10)); + snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; } // net var data @@ -1929,8 +2956,8 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); - demoplayback = false; - titledemo = false; + demo.playback = false; + demo.title = false; return; } @@ -1938,64 +2965,171 @@ void G_DoPlayDemo(char *defdemoname) memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldghost,0,sizeof(oldghost)); + memset(&ghostext,0,sizeof(ghostext)); +#if defined(SKIPERRORS) && !defined(DEVELOP) + if ((VERSION != version || SUBVERSION != subversion) && !skiperrors) +#else if (VERSION != version || SUBVERSION != subversion) +#endif CONS_Alert(CONS_WARNING, M_GetText("Demo version does not match game version. Desyncs may occur.\n")); - // didn't start recording right away. - demo_start = false; - - // Set skin - SetPlayerSkin(0, skin); - -#ifdef HAVE_BLUA - LUAh_MapChange(gamemap); + // console warning messages +#if defined(SKIPERRORS) && !defined(DEVELOP) + demosynced = (!skiperrors); +#else + demosynced = true; #endif - displayplayer = consoleplayer = 0; - memset(playeringame,0,sizeof(playeringame)); - playeringame[0] = true; - P_SetRandSeed(randseed); - G_InitNew(false, G_BuildMapName(gamemap), true, true, false); - // Set color - players[0].skincolor = skins[players[0].skin].prefcolor; - for (i = 0; i < numskincolors; i++) - if (!stricmp(skincolors[i].name,color)) - { - players[0].skincolor = i; - break; - } - CV_StealthSetValue(&cv_playercolor, players[0].skincolor); - if (players[0].mo) + // didn't start recording right away. + demo.deferstart = false; + + displayplayers[0] = consoleplayer = 0; + memset(playeringame,0,sizeof(playeringame)); + + // Load players that were in-game when the map started + p = READUINT8(demo_p); + + for (i = 1; i < MAXSPLITSCREENPLAYERS; i++) + displayplayers[i] = INT32_MAX; + + while (p != 0xFF) { - players[0].mo->color = players[0].skincolor; - oldghost.x = players[0].mo->x; - oldghost.y = players[0].mo->y; - oldghost.z = players[0].mo->z; + spectator = false; + if (p & DEMO_SPECTATOR) + { + spectator = true; + p &= ~DEMO_SPECTATOR; + + if (modeattacking) + { + snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with spectators, and is thus invalid.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + } + slots[numslots] = p; numslots++; + + if (modeattacking && numslots > 1) + { + snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with multiple players, and is thus invalid.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + + if (!playeringame[displayplayers[0]] || players[displayplayers[0]].spectator) + displayplayers[0] = consoleplayer = serverplayer = p; + + playeringame[p] = true; + players[p].spectator = spectator; + + // Name + M_Memcpy(player_names[p],demo_p,16); + demo_p += 16; + + // Skin + M_Memcpy(skin,demo_p,16); + demo_p += 16; + SetPlayerSkin(p, skin); + + // Color + M_Memcpy(color,demo_p,16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i],color)) // SRB2kart + { + players[p].skincolor = i; + break; + } + + // Follower + M_Memcpy(follower, demo_p, 16); + demo_p += 16; + SetPlayerFollower(p, follower); + + // Follower colour + M_Memcpy(color, demo_p, 16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS +2; i++) // +2 because of Match and Opposite + { + if (!stricmp(Followercolor_cons_t[i].strvalue, color)) + { + players[p].followercolor = i; + break; + } + } + + // Score, since Kart uses this to determine where you start on the map + players[p].score = READUINT32(demo_p); + + // Power Levels + clientpowerlevels[p][G_BattleGametype() ? PWRLV_BATTLE : PWRLV_RACE] = READUINT16(demo_p); + + // Kart stats, temporarily + kartspeed[p] = READUINT8(demo_p); + kartweight[p] = READUINT8(demo_p); + + if (stricmp(skins[players[p].skin].name, skin) != 0) + FindClosestSkinForStats(p, kartspeed[p], kartweight[p]); + + // Look for the next player + p = READUINT8(demo_p); } - // Set saved attribute values - // No cheat checking here, because even if they ARE wrong... - // it would only break the replay if we clipped them. - players[0].camerascale = camerascale; - players[0].shieldscale = shieldscale; - players[0].charability = charability; - players[0].charability2 = charability2; - players[0].actionspd = actionspd; - players[0].mindash = mindash; - players[0].maxdash = maxdash; - players[0].normalspeed = normalspeed; - players[0].runspeed = runspeed; - players[0].thrustfactor = thrustfactor; - players[0].accelstart = accelstart; - players[0].acceleration = acceleration; - players[0].height = height; - players[0].spinheight = spinheight; - players[0].jumpfactor = jumpfactor; - players[0].followitem = followitem; - players[0].pflags = pflags; + // end of player read (the 0xFF marker) + // so this is where we are to read our lua variables (if possible!) + if (demoflags & DF_LUAVARS) // again, used for compability, lua shit will be saved to replays regardless of if it's even been loaded + { + if (!gL) // No Lua state! ...I guess we'll just start one... + LUA_ClearState(); - demo_start = true; + LUA_UnArchiveDemo(); + } + + splitscreen = 0; + + if (demo.title) + { + splitscreen = M_RandomKey(6)-1; + splitscreen = min(min(3, numslots-1), splitscreen); // Bias toward 1p and 4p views + + for (p = 0; p <= splitscreen; p++) + G_ResetView(p+1, slots[M_RandomKey(numslots)], false); + } + + R_ExecuteSetViewSize(); + + P_SetRandSeed(randseed); + G_InitNew(demoflags & DF_ENCORE, G_BuildMapName(gamemap), true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer. + + for (i = 0; i < MAXPLAYERS; i++) + { + if (players[i].mo) + { + players[i].mo->color = players[i].skincolor; + oldghost[i].x = players[i].mo->x; + oldghost[i].y = players[i].mo->y; + oldghost[i].z = players[i].mo->z; + } + + // Set saved attribute values + // No cheat checking here, because even if they ARE wrong... + // it would only break the replay if we clipped them. + players[i].kartspeed = kartspeed[i]; + players[i].kartweight = kartweight[i]; + } + + demo.deferstart = true; } void G_AddGhost(char *defdemoname) @@ -2003,12 +3137,13 @@ void G_AddGhost(char *defdemoname) INT32 i; lumpnum_t l; char name[17],skin[17],color[MAXCOLORNAME+1],*n,*pdemoname,md5[16]; - UINT8 cnamelen; demoghost *gh; UINT8 flags; UINT8 *buffer,*p; mapthing_t *mthing; UINT16 count, ghostversion; + skin_t *ghskin = &skins[0]; + UINT8 kartspeed = UINT8_MAX, kartweight = UINT8_MAX; name[16] = '\0'; skin[16] = '\0'; @@ -2052,17 +3187,14 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } p += 12; // DEMOHEADER + p++; // VERSION p++; // SUBVERSION + ghostversion = READUINT16(p); switch(ghostversion) { case DEMOVERSION: // latest always supported - cnamelen = MAXCOLORNAME; - break; - // all that changed between then and now was longer color name - case 0x000c: - cnamelen = 16; break; // too old, cannot support. default: @@ -2071,7 +3203,10 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } + + p += 64; // title M_Memcpy(md5, p, 16); p += 16; // demo checksum + for (gh = ghosts; gh; gh = gh->next) if (!memcmp(md5, gh->checksum, 16)) // another ghost in the game already has this checksum? { // Don't add another one, then! @@ -2080,6 +3215,7 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } + if (memcmp(p, "PLAY", 4)) { CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo format unacceptable.\n"), pdemoname); @@ -2087,11 +3223,11 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } p += 4; // "PLAY" - if (ghostversion <= 0x0008) - p++; // gamemap - else - p += 2; // gamemap + + + p += 2; // gamemap p += 16; // mapmd5 (possibly check for consistency?) + flags = READUINT8(p); if (!(flags & DF_GHOST)) { @@ -2100,53 +3236,26 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } + + p++; // gametype + G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts. + switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - p += 10; // demo time, score, and rings - break; - case ATTACKING_NIGHTS: // 2 - p += 8; // demo time left, score - break; - default: // 3 - break; + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + p += 8; // demo time, lap + break; + case ATTACKING_CAPSULES: // 2 + p += 4; // demo time + break; + default: // 3 + break; } p += 4; // random seed - - // Player name (TODO: Display this somehow if it doesn't match cv_playername!) - M_Memcpy(name, p,16); - p += 16; - - // Skin - M_Memcpy(skin, p,16); - p += 16; - - // Color - M_Memcpy(color, p,cnamelen); - p += cnamelen; - - // Ghosts do not have a player structure to put this in. - p++; // charability - p++; // charability2 - p++; // actionspd - p++; // mindash - p++; // maxdash - p++; // normalspeed - p++; // runspeed - p++; // thrustfactor - p++; // accelstart - p++; // acceleration - p++; // height - p++; // spinheight - p++; // camerascale - p++; // shieldscale - p += 4; // jumpfactor - p += 4; // followitem - - p++; // pflag data + p += 4; // Extra data location reference // net var data count = READUINT16(p); @@ -2165,6 +3274,59 @@ void G_AddGhost(char *defdemoname) return; } + if (READUINT8(p) != 0) + { + CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } + + // Player name (TODO: Display this somehow if it doesn't match cv_playername!) + M_Memcpy(name, p, 16); + p += 16; + + // Skin + M_Memcpy(skin, p, 16); + p += 16; + + // Color + M_Memcpy(color, p, 16); + p += 16; + + // Follower data was here, skip it, we don't care about it for ghosts. + p += 32; // followerskin (16) + followercolor (16) + + p += 4; // score + p += 2; // powerlevel + + kartspeed = READUINT8(p); + kartweight = READUINT8(p); + + if (READUINT8(p) != 0xFF) + { + CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } + + for (i = 0; i < numskins; i++) + if (!stricmp(skins[i].name,skin)) + { + ghskin = &skins[i]; + break; + } + + if (i == numskins) + { + if (kartspeed != UINT8_MAX && kartweight != UINT8_MAX) + ghskin = &skins[GetSkinNumClosestToStats(kartspeed, kartweight)]; + + CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Invalid character. Falling back to %s.\n"), pdemoname, ghskin->name); + } + + gh = Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL); gh->next = ghosts; gh->buffer = buffer; @@ -2202,15 +3364,16 @@ void G_AddGhost(char *defdemoname) gh->oldmo.y = gh->mo->y; gh->oldmo.z = gh->mo->z; + gh->mo->state = states + S_KART_STILL; + gh->mo->sprite = gh->mo->state->sprite; + gh->mo->sprite2 = (gh->mo->state->frame & FF_FRAMEMASK); + //gh->mo->frame = tr_trans30<mo->flags2 |= MF2_DONTDRAW; + gh->fadein = (9-3)*6; // fade from invisible to trans30 over as close to 35 tics as possible + gh->mo->tics = -1; + // Set skin - gh->mo->skin = &skins[0]; - for (i = 0; i < numskins; i++) - if (!stricmp(skins[i].name,skin)) - { - gh->mo->skin = &skins[i]; - break; - } - gh->oldmo.skin = gh->mo->skin; + gh->mo->skin = gh->oldmo.skin = ghskin; // Set color gh->mo->color = ((skin_t*)gh->mo->skin)->prefcolor; @@ -2222,14 +3385,6 @@ void G_AddGhost(char *defdemoname) } gh->oldmo.color = gh->mo->color; - gh->mo->state = states+S_PLAY_STND; - gh->mo->sprite = gh->mo->state->sprite; - gh->mo->sprite2 = (gh->mo->state->frame & FF_FRAMEMASK); - //gh->mo->frame = tr_trans30<mo->flags2 |= MF2_DONTDRAW; - gh->fadein = (9-3)*6; // fade from invisible to trans30 over as close to 35 tics as possible - gh->mo->tics = -1; - CONS_Printf(M_GetText("Added ghost %s from %s\n"), name, pdemoname); Z_Free(pdemoname); } @@ -2246,6 +3401,96 @@ void G_FreeGhosts(void) ghosts = NULL; } +// A simplified version of G_AddGhost... +void G_UpdateStaffGhostName(lumpnum_t l) +{ + UINT8 *buffer,*p; + UINT16 ghostversion; + UINT8 flags; + + buffer = p = W_CacheLumpNum(l, PU_CACHE); + + // read demo header + if (memcmp(p, DEMOHEADER, 12)) + { + goto fail; + } + + p += 12; // DEMOHEADER + p++; // VERSION + p++; // SUBVERSION + + ghostversion = READUINT16(p); + switch(ghostversion) + { + case DEMOVERSION: // latest always supported + break; + + // too old, cannot support. + default: + goto fail; + } + + p += 64; // full demo title + p += 16; // demo checksum + + if (memcmp(p, "PLAY", 4)) + { + goto fail; + } + + p += 4; // "PLAY" + p += 2; // gamemap + p += 16; // mapmd5 (possibly check for consistency?) + + flags = READUINT8(p); + if (!(flags & DF_GHOST)) + { + goto fail; // we don't NEED to do it here, but whatever + } + + p++; // Gametype + + G_SkipDemoExtraFiles(&p); + + switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) + { + case ATTACKING_NONE: // 0 + break; + case ATTACKING_RECORD: // 1 + p += 8; // demo time, lap + break; + case ATTACKING_CAPSULES: // 2 + p += 4; // demo time + break; + default: // 3 + break; + } + + p += 4; // random seed + p += 4; // Extrainfo location marker + + // Ehhhh don't need ghostversion here (?) so I'll reuse the var here + ghostversion = READUINT16(p); + while (ghostversion--) + { + p += 2; + SKIPSTRING(p); + p++; // stealth + } + + // Assert first player is in and then read name + if (READUINT8(p) != 0) + goto fail; + M_Memcpy(dummystaffname, p,16); + dummystaffname[16] = '\0'; + + // Ok, no longer any reason to care, bye +fail: + Z_Free(buffer); + return; +} + // // G_TimeDemo // NOTE: name is a full filename for external demos @@ -2259,7 +3504,7 @@ void G_TimeDemo(const char *name) restorecv_vidwait = cv_vidwait.value; if (cv_vidwait.value) CV_Set(&cv_vidwait, "0"); - timingdemo = true; + demo.timing = true; singletics = true; framecount = 0; demostarttime = I_GetTime(); @@ -2301,14 +3546,13 @@ void G_DoPlayMetal(void) } // read demo header - metal_p += 12; // DEMOHEADER + metal_p += 12; // DEMOHEADER metal_p++; // VERSION metal_p++; // SUBVERSION metalversion = READUINT16(metal_p); switch(metalversion) { case DEMOVERSION: // latest always supported - case 0x000c: // all that changed between then and now was longer color name break; // too old, cannot support. default: @@ -2384,7 +3628,6 @@ static void G_StopDemoRecording(void) // Stops metal sonic's demo. Separate from other functions because metal + replays can coexist void G_StopMetalDemo(void) { - // Metal Sonic finishing doesn't end the game, dammit. Z_Free(metalbuffer); metalbuffer = NULL; @@ -2398,6 +3641,7 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill) boolean saved = false; if (demo_p) { + UINT8 *p = demobuffer+16; // checksum position WRITEUINT8(demo_p, (kill) ? METALDEATH : DEMOMARKER); // add the demo end (or metal death) marker WriteDemoChecksum(); saved = FIL_WriteFile(va("%sMS.LMP", G_BuildMapName(gamemap)), demobuffer, demo_p - demobuffer); // finally output the file. @@ -2472,14 +3716,28 @@ void G_StopDemo(void) { Z_Free(demobuffer); demobuffer = NULL; - demoplayback = false; - titledemo = false; - timingdemo = false; + demo.playback = false; + if (demo.title) + modeattacking = false; + demo.title = false; + demo.timing = false; singletics = false; + demo.freecam = false; + // reset democam shit too: + democam.cam = NULL; + democam.soundmobj = NULL; + democam.localangle = 0; + democam.localaiming = 0; + democam.turnheld = false; + democam.keyboardlook = false; + if (gamestate == GS_INTERMISSION) Y_EndIntermission(); // cleanup + if (gamestate == GS_VOTING) + Y_EndVote(); + G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; SV_StopServer(); @@ -2492,31 +3750,170 @@ boolean G_CheckDemoStatus(void) // DO NOT end metal sonic demos here - if (timingdemo) + if (demo.timing) { G_StopTimingDemo(); return true; } - if (demoplayback) + if (demo.playback) { - if (singledemo) + f (demo.quitafterplaying) I_Quit(); - G_StopDemo(); - if (modeattacking) - M_EndModeAttackRun(); + if (multiplayer && !demo.title) + G_ExitLevel(); else - D_AdvanceDemo(); + { + G_StopDemo(); + + if (modeattacking) + M_EndModeAttackRun(); + else + D_AdvanceDemo(); + } return true; } - if (demorecording) + if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) { - G_StopDemoRecording(); + G_SaveDemo(); return true; } + demo.recording = false; return false; } + +void G_SaveDemo(void) +{ + UINT8 *p = demobuffer+16; // after version + UINT32 length; +#ifdef NOMD5 + UINT8 i; +#endif + + // Ensure extrainfo pointer is always available, even if no info is present. + if (demoinfo_p && *(UINT32 *)demoinfo_p == 0) + { + WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker + *(UINT32 *)demoinfo_p = demo_p - demobuffer; + } + WRITEUINT8(demo_p, DW_END); // Mark end of demo extra data. + + M_Memcpy(p, demo.titlename, 64); // Write demo title here + p += 64; + + if (multiplayer) + { + // Change the demo's name to be a slug of the title + char demo_slug[128]; + char *writepoint; + size_t i, strindex = 0; + boolean dash = true; + + for (i = 0; demo.titlename[i] && i < 127; i++) + { + if ((demo.titlename[i] >= 'a' && demo.titlename[i] <= 'z') || + (demo.titlename[i] >= '0' && demo.titlename[i] <= '9')) + { + demo_slug[strindex] = demo.titlename[i]; + strindex++; + dash = false; + } + else if (demo.titlename[i] >= 'A' && demo.titlename[i] <= 'Z') + { + demo_slug[strindex] = demo.titlename[i] + 'a' - 'A'; + strindex++; + dash = false; + } + else if (!dash) + { + demo_slug[strindex] = '-'; + strindex++; + dash = true; + } + } + + demo_slug[strindex] = 0; + if (dash) demo_slug[strindex-1] = 0; + + writepoint = strstr(demoname, "-") + 1; + demo_slug[128 - (writepoint - demoname) - 4] = 0; + sprintf(writepoint, "%s.lmp", demo_slug); + } + + length = *(UINT32 *)demoinfo_p; + WRITEUINT32(demoinfo_p, length); +#ifdef NOMD5 + for (i = 0; i < 16; i++, p++) + *p = M_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. +#else + // Make a checksum of everything after the checksum in the file up to the end of the standard data. Extrainfo is freely modifiable. + md5_buffer((char *)p+16, (demobuffer + length) - (p+16), p); +#endif + + + if (FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer)) // finally output the file. + demo.savemode = DSM_SAVED; + free(demobuffer); + demo.recording = false; + + if (!modeattacking) + { + if (demo.savemode == DSM_SAVED) + CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); + else + CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname); + } +} + +boolean G_DemoTitleResponder(event_t *ev) +{ + size_t len; + INT32 ch; + + if (ev->type != ev_keydown) + return false; + + ch = (INT32)ev->data1; + + // Only ESC and non-keyboard keys abort connection + if (ch == KEY_ESCAPE) + { + demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING; + return true; + } + + if (ch == KEY_ENTER || ch >= KEY_MOUSE1) + { + demo.savemode = DSM_WILLSAVE; + return true; + } + + if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART]) + || ch == ' ') // Allow spaces, of course + { + len = strlen(demo.titlename); + if (len < 64) + { + demo.titlename[len+1] = 0; + demo.titlename[len] = CON_ShiftChar(ch); + } + } + else if (ch == KEY_BACKSPACE) + { + if (shiftdown) + memset(demo.titlename, 0, sizeof(demo.titlename)); + else + { + len = strlen(demo.titlename); + + if (len > 0) + demo.titlename[len-1] = 0; + } + } + + return true; +} diff --git a/src/g_demo.h b/src/g_demo.h index df25042c4..c57878424 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -18,18 +18,71 @@ #include "doomstat.h" #include "d_event.h" +extern UINT8 *demo_p; + // ====================================== // DEMO playback/recording related stuff. // ====================================== // demoplaying back and demo recording -extern boolean demoplayback, titledemo, demorecording, timingdemo; -extern tic_t demostarttime; +extern consvar_t cv_recordmultiplayerdemos, cv_netdemosyncquality; + +// Publicly-accessible demo vars +struct demovars_s { + char titlename[65]; + boolean recording, playback, timing; + UINT16 version; // Current file format of the demo being played + boolean title; // Title Screen demo can be cancelled by any key + boolean rewinding; // Rewind in progress + + boolean loadfiles, ignorefiles; // Demo file loading options + boolean fromtitle; // SRB2Kart: Don't stop the music + boolean inreplayhut; // Go back to replayhut after demos + boolean quitafterplaying; // quit after playing a demo from cmdline + boolean deferstart; // don't start playing demo right away + + tic_t savebutton; // Used to determine when the local player can choose to save the replay while the race is still going + enum { + DSM_NOTSAVING, + DSM_WILLAUTOSAVE, + DSM_TITLEENTRY, + DSM_WILLSAVE, + DSM_SAVED + } savemode; + + boolean freecam; + +}; + +extern struct demovars_s demo; + +typedef enum { + MD_NOTLOADED, + MD_LOADED, + MD_SUBDIR, + MD_OUTDATED, + MD_INVALID +} menudemotype_e; + +typedef struct menudemo_s { + char filepath[256]; + menudemotype_e type; + + char title[65]; // Null-terminated for string prints + UINT16 map; + UINT8 addonstatus; // What do we need to do addon-wise to play this demo? + UINT8 gametype; + SINT8 kartspeed; // Add OR DF_ENCORE for encore mode, idk + UINT8 numlaps; + + struct { + UINT8 ranking; + char name[17]; + UINT8 skin, color; + UINT32 timeorscore; + } standings[MAXPLAYERS]; +} menudemo_t; -// Quit after playing a demo from cmdline. -extern boolean singledemo; -extern boolean demo_start; -extern boolean demosynced; extern mobj_t *metalplayback; @@ -40,7 +93,8 @@ void G_BeginRecording(void); void G_BeginMetal(void); // Only called by shutdown code. -void G_SetDemoTime(UINT32 ptime, UINT32 pscore, UINT16 prings); +void G_WriteStanding(UINT8 ranking, char *name, INT32 skinnum, UINT8 color, UINT32 val); +void G_SetDemoTime(UINT32 ptime, UINT32 plap); UINT8 G_CmpDemoTime(char *oldname, char *newname); typedef enum @@ -49,38 +103,84 @@ typedef enum GHC_SUPER, GHC_FIREFLOWER, GHC_INVINCIBLE, - GHC_NIGHTSSKIN, // not actually a colour - GHC_RETURNSKIN // ditto + GHC_RETURNSKIN // not actually a colour } ghostcolor_t; +extern UINT8 demo_extradata[MAXPLAYERS]; +extern UINT8 demo_writerng; + +#define DXD_RESPAWN 0x01 // "respawn" command in console +#define DXD_SKIN 0x02 // skin changed +#define DXD_NAME 0x04 // name changed +#define DXD_COLOR 0x08 // color changed +#define DXD_PLAYSTATE 0x10 // state changed between playing, spectating, or not in-game + +#define DXD_PST_PLAYING 0x01 +#define DXD_PST_SPECTATING 0x02 +#define DXD_PST_LEFT 0x03 + // Record/playback tics +void G_ReadDemoExtraData(void); +void G_WriteDemoExtraData(void); void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum); void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum); -void G_GhostAddThok(void); -void G_GhostAddSpin(void); -void G_GhostAddRev(void); -void G_GhostAddColor(ghostcolor_t color); -void G_GhostAddFlip(void); -void G_GhostAddScale(fixed_t scale); -void G_GhostAddHit(mobj_t *victim); -void G_WriteGhostTic(mobj_t *ghost); -void G_ConsGhostTic(void); +void G_GhostAddThok(INT32 playernum); +void G_GhostAddSpin(INT32 playernum); +void G_GhostAddRev(INT32 playernum); +void G_GhostAddColor(INT32 playernum, ghostcolor_t color); +void G_GhostAddFlip(INT32 playernum); +void G_GhostAddScale(INT32 playernum, fixed_t scale); +void G_GhostAddHit(INT32 playernum, mobj_t *victim); +void G_WriteAllGhostTics(void); +void G_WriteGhostTic(mobj_t *ghost, INT32 playernum); +void G_ConsAllGhostTics(void); +void G_ConsGhostTic(INT32 playernum); void G_GhostTicker(void); + +void G_InitDemoRewind(void); +void G_StoreRewindInfo(void); +void G_PreviewRewind(tic_t previewtime); +void G_ConfirmRewind(tic_t rewindtime); + void G_ReadMetalTic(mobj_t *metal); void G_WriteMetalTic(mobj_t *metal); void G_SaveMetal(UINT8 **buffer); void G_LoadMetal(UINT8 **buffer); +// Your naming conventions are stupid and useless. +// There is no conflict here. +typedef struct demoghost { + UINT8 checksum[16]; + UINT8 *buffer, *p, color; + UINT16 version; + mobj_t oldmo, *mo; + struct demoghost *next; +} demoghost; +extern demoghost *ghosts; + +// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's. +#define DFILE_ERROR_NOTLOADED 0x01 // Files are not loaded, but can be without a restart. +#define DFILE_ERROR_OUTOFORDER 0x02 // Files are loaded, but out of order. +#define DFILE_ERROR_INCOMPLETEOUTOFORDER 0x03 // Some files are loaded out of order, but others are not. +#define DFILE_ERROR_CANNOTLOAD 0x04 // Files are missing and cannot be loaded. +#define DFILE_ERROR_EXTRAFILES 0x05 // Extra files outside of the replay's file list are loaded. + void G_DeferedPlayDemo(const char *demo); void G_DoPlayDemo(char *defdemoname); void G_TimeDemo(const char *name); void G_AddGhost(char *defdemoname); +void G_UpdateStaffGhostName(lumpnum_t l); void G_FreeGhosts(void); -void G_DoPlayMetal(void); void G_DoneLevelLoad(void); + +void G_DoPlayMetal(void); void G_StopMetalDemo(void); ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill); + void G_StopDemo(void); boolean G_CheckDemoStatus(void); +void G_LoadDemoInfo(menudemo_t *pdemo); +void G_DeferedPlayDemo(const char *demo); + #endif // __G_DEMO__ diff --git a/src/g_game.c b/src/g_game.c index 1670e0ba3..94f098ecb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -44,41 +44,24 @@ #include "r_skins.h" #include "y_inter.h" #include "v_video.h" -<<<<<<< HEAD -#include "dehacked.h" // get_number (for ghost thok) -#include "lua_script.h" // LUA_ArchiveDemo and LUA_UnArchiveDemo -======= ->>>>>>> srb2/next #include "lua_hook.h" #include "lua_libs.h" // gL (Lua state) #include "k_bot.h" #include "m_cond.h" // condition sets -<<<<<<< HEAD -#include "md5.h" // demo checksums -#include "k_kart.h" // SRB2kart +#include "lua_hud.h" + +// SRB2kart +#include "k_kart.h" #include "k_battle.h" #include "k_pwrlv.h" #include "k_color.h" -======= -#include "lua_hud.h" ->>>>>>> srb2/next gameaction_t gameaction; gamestate_t gamestate = GS_NULL; UINT8 ultimatemode = false; -<<<<<<< HEAD -======= -boolean botingame; -UINT8 botskin; -UINT16 botcolor; - ->>>>>>> srb2/next -JoyType_t Joystick; -JoyType_t Joystick2; -JoyType_t Joystick3; -JoyType_t Joystick4; +JoyType_t Joystick[MAXSPLITSCREENPLAYERS]; // 1024 bytes is plenty for a savegame #define SAVEGAMESIZE (1024) @@ -100,11 +83,10 @@ UINT32 mapmusposition; // Position to jump to UINT32 mapmusresume; INT16 gamemap = 1; -<<<<<<< HEAD -INT16 maptol; +UINT32 maptol; UINT8 globalweather = 0; -UINT8 curWeather = PRECIP_NONE; +INT32 curWeather = PRECIP_NONE; precipprops_t precipprops[MAXPRECIP] = { @@ -117,16 +99,9 @@ precipprops_t precipprops[MAXPRECIP] = {MT_RAIN, PRECIPFX_THUNDER} // PRECIP_STORM_NOSTRIKES }; -INT32 cursaveslot = -1; // Auto-save 1p savegame slot -INT16 lastmapsaved = 0; // Last map we auto-saved at -======= -UINT32 maptol; -UINT8 globalweather = 0; -INT32 curWeather = PRECIP_NONE; INT32 cursaveslot = 0; // Auto-save 1p savegame slot //INT16 lastmapsaved = 0; // Last map we auto-saved at INT16 lastmaploaded = 0; // Last map the game loaded ->>>>>>> srb2/next boolean gamecomplete = false; UINT8 numgameovers = 0; // for startinglives balance @@ -143,8 +118,7 @@ boolean runemeraldmanager = false; UINT16 emeraldspawndelay = 60*TICRATE; // menu demo things -<<<<<<< HEAD -UINT8 numDemos = 0; //3; -- i'm FED UP of losing my skincolour to a broken demo. change this back when we make new ones +UINT8 numDemos = 0; UINT32 demoDelayTime = 15*TICRATE; UINT32 demoIdleTime = 3*TICRATE; @@ -152,12 +126,6 @@ boolean nodrawers; // for comparative timing purposes boolean noblit; // for comparative timing purposes static tic_t demostarttime; // for comparative timing purposes -======= -UINT8 numDemos = 0; -UINT32 demoDelayTime = 15*TICRATE; -UINT32 demoIdleTime = 3*TICRATE; - ->>>>>>> srb2/next boolean netgame; // only true if packets are broadcast boolean multiplayer; boolean playeringame[MAXPLAYERS]; @@ -189,22 +157,12 @@ INT32 tutorialfreelook = 0; // store cv_alwaysfreelook user value INT32 tutorialmousemove = 0; // store cv_mousemove user value INT32 tutorialanalog = 0; // store cv_analog[0] user value -<<<<<<< HEAD boolean looptitle = true; -boolean useNightsSS = false; - -UINT8 skincolor_redteam = SKINCOLOR_RED; -UINT8 skincolor_blueteam = SKINCOLOR_BLUE; -UINT8 skincolor_redring = SKINCOLOR_RED; -UINT8 skincolor_bluering = SKINCOLOR_STEEL; -======= -boolean looptitle = false; UINT16 skincolor_redteam = SKINCOLOR_RED; UINT16 skincolor_blueteam = SKINCOLOR_BLUE; -UINT16 skincolor_redring = SKINCOLOR_SALMON; +UINT16 skincolor_redring = SKINCOLOR_RASPBERRY; UINT16 skincolor_bluering = SKINCOLOR_CORNFLOWER; ->>>>>>> srb2/next tic_t countdowntimer = 0; boolean countdowntimeup = false; @@ -280,9 +238,9 @@ UINT16 spacetimetics = 11*TICRATE + (TICRATE/2); UINT16 extralifetics = 4*TICRATE; UINT16 nightslinktics = 2*TICRATE; -INT32 gameovertics = 11*TICRATE; +INT32 gameovertics = 15*TICRATE; +UINT8 ammoremovaltics = 2*TICRATE; -<<<<<<< HEAD // SRB2kart tic_t introtime = 108+5; // plus 5 for white fade tic_t starttime = 6*TICRATE + (3*TICRATE/4); @@ -301,11 +259,6 @@ INT32 wantedreduce = 5*TICRATE; INT32 wantedfrequency = 10*TICRATE; INT32 flameseg = TICRATE/4; -INT32 gameovertics = 15*TICRATE; -======= -UINT8 ammoremovaltics = 2*TICRATE; ->>>>>>> srb2/next - UINT8 use1upSound = 0; UINT8 maxXtraLife = 2; // Max extra lives from rings UINT8 useContinues = 0; // Set to 1 to enable continues outside of no-save scenarioes @@ -369,89 +322,23 @@ UINT32 timesBeaten; UINT32 timesBeatenWithEmeralds; //UINT32 timesBeatenUltimate; -<<<<<<< HEAD -//@TODO put these all in a struct for namespacing purposes? -static char demoname[128]; -static UINT8 *demobuffer = NULL; -static UINT8 *demotime_p, *demoinfo_p; -UINT8 *demo_p; -static UINT8 *demoend; -static UINT8 demoflags; -static boolean demosynced = true; // console warning message - -struct demovars_s demo; - -boolean metalrecording; // recording as metal sonic -mobj_t *metalplayback; -static UINT8 *metalbuffer = NULL; -static UINT8 *metal_p; -static UINT16 metalversion; - -// extra data stuff (events registered this frame while recording) -static struct { - UINT8 flags; // EZT flags - - // EZT_COLOR - UINT8 color, lastcolor; - - // EZT_SCALE - fixed_t scale, lastscale; - - // EZT_KART - INT32 kartitem, kartamount, kartbumpers; - - UINT8 desyncframes; // Don't try to resync unless we've been off for two frames, to monkeypatch a few trouble spots - - // EZT_HIT - UINT16 hits; - mobj_t **hitlist; -} ghostext[MAXPLAYERS]; - -// Your naming conventions are stupid and useless. -// There is no conflict here. -demoghost *ghosts = NULL; -======= typedef struct joystickvector2_s { INT32 xaxis; INT32 yaxis; } joystickvector2_t; ->>>>>>> srb2/next boolean precache = true; // if true, load all graphics at start INT16 prevmap, nextmap; -static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; -consvar_t cv_recordmultiplayerdemos = {"netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - -static CV_PossibleValue_t netdemosyncquality_cons_t[] = {{1, "MIN"}, {35, "MAX"}, {0, NULL}}; -consvar_t cv_netdemosyncquality = {"netdemo_syncquality", "1", CV_SAVE, netdemosyncquality_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - static UINT8 *savebuffer; -// Analog Control -static void UserAnalog_OnChange(void); -static void UserAnalog2_OnChange(void); -static void UserAnalog3_OnChange(void); -static void UserAnalog4_OnChange(void); -static void Analog_OnChange(void); -static void Analog2_OnChange(void); -<<<<<<< HEAD -static void Analog3_OnChange(void); -static void Analog4_OnChange(void); -======= -static void DirectionChar_OnChange(void); -static void DirectionChar2_OnChange(void); -static void AutoBrake_OnChange(void); -static void AutoBrake2_OnChange(void); ->>>>>>> srb2/next void SendWeaponPref(void); void SendWeaponPref2(void); void SendWeaponPref3(void); void SendWeaponPref4(void); -//static CV_PossibleValue_t crosshair_cons_t[] = {{0, "Off"}, {1, "Cross"}, {2, "Angle"}, {3, "Point"}, {0, NULL}}; static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, {1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"}, #if JOYAXISSET > 1 @@ -472,20 +359,12 @@ static CV_PossibleValue_t deadzone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0 // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler. -// it automatically becomes compact with 20+ players, but if you like it, I guess you can turn that on! -// SRB2Kart: irrelevant for us. -//consvar_t cv_compactscoreboard= {"compactscoreboard", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; - // chat timer thingy static CV_PossibleValue_t chattime_cons_t[] = {{5, "MIN"}, {999, "MAX"}, {0, NULL}}; consvar_t cv_chattime = {"chattime", "8", CV_SAVE, chattime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // chatwidth -<<<<<<< HEAD static CV_PossibleValue_t chatwidth_cons_t[] = {{64, "MIN"}, {150, "MAX"}, {0, NULL}}; -======= -static CV_PossibleValue_t chatwidth_cons_t[] = {{64, "MIN"}, {300, "MAX"}, {0, NULL}}; ->>>>>>> srb2/next consvar_t cv_chatwidth = {"chatwidth", "150", CV_SAVE, chatwidth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // chatheight @@ -511,68 +390,7 @@ consvar_t cv_pauseifunfocused = {"pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, N // Display song credits consvar_t cv_songcredits = {"songcredits", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -/*consvar_t cv_crosshair = {"crosshair", "Off", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_crosshair2 = {"crosshair2", "Off", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_crosshair3 = {"crosshair3", "Off", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_crosshair4 = {"crosshair4", "Off", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};*/ consvar_t cv_invertmouse = {"invertmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -<<<<<<< HEAD -consvar_t cv_invertmouse2 = {"invertmouse2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -/*consvar_t cv_alwaysfreelook = {"alwaysmlook", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_alwaysfreelook2 = {"alwaysmlook2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chasefreelook = {"chasemlook", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chasefreelook2 = {"chasemlook2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mousemove = {"mousemove", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mousemove2 = {"mousemove2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};*/ -consvar_t cv_analog = {"analog", "Off", CV_CALL, CV_OnOff, Analog_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_analog2 = {"analog2", "Off", CV_CALL, CV_OnOff, Analog2_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_analog3 = {"analog3", "Off", CV_CALL, CV_OnOff, Analog3_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_analog4 = {"analog4", "Off", CV_CALL, CV_OnOff, Analog4_OnChange, 0, NULL, NULL, 0, 0, NULL}; -#ifdef DC -consvar_t cv_useranalog = {"useranalog", "On", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_useranalog2 = {"useranalog2", "On", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog2_OnChange, 0, NULL, NULL, 0, 0, NULL}; -#else -consvar_t cv_useranalog = {"useranalog", "Off", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_useranalog2 = {"useranalog2", "Off", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog2_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_useranalog3 = {"useranalog3", "Off", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog3_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_useranalog4 = {"useranalog4", "Off", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog4_OnChange, 0, NULL, NULL, 0, 0, NULL}; -#endif - -consvar_t cv_turnaxis = {"joyaxis_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_moveaxis = {"joyaxis_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_brakeaxis = {"joyaxis_brake", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_aimaxis = {"joyaxis_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lookaxis = {"joyaxis_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fireaxis = {"joyaxis_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_driftaxis = {"joyaxis_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_deadzone = {"joy_deadzone", "0.5", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - -consvar_t cv_turnaxis2 = {"joyaxis2_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_moveaxis2 = {"joyaxis2_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_brakeaxis2 = {"joyaxis2_brake", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_aimaxis2 = {"joyaxis2_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lookaxis2 = {"joyaxis2_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fireaxis2 = {"joyaxis2_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_driftaxis2 = {"joyaxis2_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_deadzone2 = {"joy2_deadzone", "0.5", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - -consvar_t cv_turnaxis3 = {"joyaxis3_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_moveaxis3 = {"joyaxis3_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_brakeaxis3 = {"joyaxis3_brake", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_aimaxis3 = {"joyaxis3_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lookaxis3 = {"joyaxis3_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fireaxis3 = {"joyaxis3_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_driftaxis3 = {"joyaxis3_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_deadzone3 = {"joy3_deadzone", "0.5", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; - -consvar_t cv_turnaxis4 = {"joyaxis4_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_moveaxis4 = {"joyaxis4_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_brakeaxis4 = {"joyaxis4_brake", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_aimaxis4 = {"joyaxis4_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lookaxis4 = {"joyaxis4_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fireaxis4 = {"joyaxis4_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_driftaxis4 = {"joyaxis4_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_deadzone4 = {"joy4_deadzone", "0.5", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_invincmusicfade = {"invincmusicfade", "300", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_growmusicfade = {"growmusicfade", "500", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -581,165 +399,93 @@ consvar_t cv_resetspecialmusic = {"resetspecialmusic", "Yes", CV_SAVE, CV_YesNo, consvar_t cv_resume = {"resume", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; -#if MAXPLAYERS > 16 -#error "please update player_name table using the new value for MAXPLAYERS" -#endif -======= -consvar_t cv_alwaysfreelook = {"alwaysmlook", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_invertmouse2 = {"invertmouse2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_alwaysfreelook2 = {"alwaysmlook2", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chasefreelook = {"chasemlook", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_chasefreelook2 = {"chasemlook2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mousemove = {"mousemove", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_mousemove2 = {"mousemove2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; - -// previously "analog", "analog2", "useranalog", and "useranalog2", invalidating 2.1-era copies of config.cfg -// changed because it'd be nice to see people try out our actually good controls with gamepads now autobrake exists -consvar_t cv_analog[2] = { - {"sessionanalog", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, Analog_OnChange, 0, NULL, NULL, 0, 0, NULL}, - {"sessionanalog2", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, Analog2_OnChange, 0, NULL, NULL, 0, 0, NULL} -}; -consvar_t cv_useranalog[2] = { - {"configanalog", "Off", CV_SAVE|CV_CALL|CV_NOSHOWHELP, CV_OnOff, UserAnalog_OnChange, 0, NULL, NULL, 0, 0, NULL}, - {"configanalog2", "Off", CV_SAVE|CV_CALL|CV_NOSHOWHELP, CV_OnOff, UserAnalog2_OnChange, 0, NULL, NULL, 0, 0, NULL} -}; - -// deez New User eXperiences -static CV_PossibleValue_t directionchar_cons_t[] = {{0, "Camera"}, {1, "Movement"}, {2, "Simple Locked"}, {0, NULL}}; -consvar_t cv_directionchar[2] = { - {"directionchar", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar_OnChange, 0, NULL, NULL, 0, 0, NULL}, - {"directionchar2", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar2_OnChange, 0, NULL, NULL, 0, 0, NULL} -}; -consvar_t cv_autobrake = {"autobrake", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_autobrake2 = {"autobrake2", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake2_OnChange, 0, NULL, NULL, 0, 0, NULL}; - -// hi here's some new controls -static CV_PossibleValue_t zerotoone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}}; -consvar_t cv_cam_shiftfacing[2] = { - {"cam_shiftfacingchar", "0.33", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_shiftfacingchar", "0.33", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, -}; -consvar_t cv_cam_turnfacing[2] = { - {"cam_turnfacingchar", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_turnfacingchar", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, -}; -consvar_t cv_cam_turnfacingability[2] = { - {"cam_turnfacingability", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_turnfacingability", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, -}; -consvar_t cv_cam_turnfacingspindash[2] = { - {"cam_turnfacingspindash", "0.5", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_turnfacingspindash", "0.5", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, -}; -consvar_t cv_cam_turnfacinginput[2] = { - {"cam_turnfacinginput", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_turnfacinginput", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, -}; - -static CV_PossibleValue_t centertoggle_cons_t[] = {{0, "Hold"}, {1, "Toggle"}, {2, "Sticky Hold"}, {0, NULL}}; -consvar_t cv_cam_centertoggle[2] = { - {"cam_centertoggle", "Hold", CV_SAVE, centertoggle_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_centertoggle", "Hold", CV_SAVE, centertoggle_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, -}; - -static CV_PossibleValue_t lockedinput_cons_t[] = {{0, "Strafe"}, {1, "Turn"}, {0, NULL}}; -consvar_t cv_cam_lockedinput[2] = { - {"cam_lockedinput", "Strafe", CV_SAVE, lockedinput_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_lockedinput", "Strafe", CV_SAVE, lockedinput_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, -}; - -static CV_PossibleValue_t lockedassist_cons_t[] = { - {0, "Off"}, - {LOCK_BOSS, "Bosses"}, - {LOCK_BOSS|LOCK_ENEMY, "Enemies"}, - {LOCK_BOSS|LOCK_INTERESTS, "Interests"}, - {LOCK_BOSS|LOCK_ENEMY|LOCK_INTERESTS, "Full"}, - {0, NULL} -}; -consvar_t cv_cam_lockonboss[2] = { - {"cam_lockaimassist", "Bosses", CV_SAVE, lockedassist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, - {"cam2_lockaimassist", "Bosses", CV_SAVE, lockedassist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, -}; - typedef enum { AXISNONE = 0, + AXISTURN, AXISMOVE, + AXISBRAKE, + AXISAIM, AXISLOOK, - AXISSTRAFE, AXISDIGITAL, // axes below this use digital deadzone - AXISJUMP, - AXISSPIN, - AXISFIRE, - AXISFIRENORMAL, + AXISFIRE = AXISDIGITAL, + AXISDRIFT, + AXISLOOKBACK, } axis_input_e; -consvar_t cv_turnaxis = {"joyaxis_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_moveaxis = {"joyaxis_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_sideaxis = {"joyaxis_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lookaxis = {"joyaxis_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_jumpaxis = {"joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_spinaxis = {"joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fireaxis = {"joyaxis_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_firenaxis = {"joyaxis_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_deadzone = {"joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_digitaldeadzone = {"joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_turnaxis[MAXSPLITSCREENPLAYERS] = { + {"joyaxis_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis2_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis3_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis4_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL} +}; -consvar_t cv_turnaxis2 = {"joyaxis2_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_moveaxis2 = {"joyaxis2_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_sideaxis2 = {"joyaxis2_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_lookaxis2 = {"joyaxis2_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_jumpaxis2 = {"joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_spinaxis2 = {"joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_fireaxis2 = {"joyaxis2_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_firenaxis2 = {"joyaxis2_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_deadzone2 = {"joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_digitaldeadzone2 = {"joy_digdeadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; ->>>>>>> srb2/next +consvar_t cv_moveaxis[MAXSPLITSCREENPLAYERS] = { + {"joyaxis_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis_move2", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis_move3", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis_move4", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, +}; + +consvar_t cv_brakeaxis[MAXSPLITSCREENPLAYERS] = { + {"joyaxis_brake", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis2_brake", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis3_brake", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis4_brake", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, +}; + +consvar_t cv_aimaxis[MAXSPLITSCREENPLAYERS] = { + {"joyaxis_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis2_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis3_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis4_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, +}; + +consvar_t cv_lookaxis[MAXSPLITSCREENPLAYERS] = { + {"joyaxis_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis2_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis3_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis4_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, +}; + +consvar_t cv_fireaxis[MAXSPLITSCREENPLAYERS] = { + {"joyaxis_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis_fire2", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis_fire3", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis_fire4", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, +}; + +consvar_t cv_driftaxis[MAXSPLITSCREENPLAYERS] = { + {"joyaxis_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis2_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis3_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joyaxis4_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, +}; + +consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS] = { + {"joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joy2_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joy3_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joy4_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, +}; + +consvar_t cv_digitaldeadzone[MAXSPLITSCREENPLAYERS] = { + {"joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joy2_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joy3_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, + {"joy4_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}, +}; #ifdef SEENAMES player_t *seenplayer; // player we're aiming at right now #endif -<<<<<<< HEAD -char player_names[MAXPLAYERS][MAXPLAYERNAME+1] = -{ - "Player 1", - "Player 2", - "Player 3", - "Player 4", - "Player 5", - "Player 6", - "Player 7", - "Player 8", - "Player 9", - "Player 10", - "Player 11", - "Player 12", - "Player 13", - "Player 14", - "Player 15", - "Player 16" -}; // SRB2kart - removed Players 17 through 32 -======= // now automatically allocated in D_RegisterClientCommands // so that it doesn't have to be updated depending on the value of MAXPLAYERS char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; ->>>>>>> srb2/next - -INT16 rw_maximums[NUM_WEAPONS] = -{ - 800, // MAX_INFINITY - 400, // MAX_AUTOMATIC - 100, // MAX_BOUNCE - 50, // MAX_SCATTER - 100, // MAX_GRENADE - 50, // MAX_EXPLOSION - 50 // MAX_RAIL -}; // Allocation for time and nights data void G_AllocMainRecordData(INT16 i) @@ -749,13 +495,6 @@ void G_AllocMainRecordData(INT16 i) memset(mainrecords[i], 0, sizeof(recorddata_t)); } -/*void G_AllocNightsRecordData(INT16 i) -{ - if (!nightsrecords[i]) - nightsrecords[i] = Z_Malloc(sizeof(nightsdata_t), PU_STATIC, NULL); - memset(nightsrecords[i], 0, sizeof(nightsdata_t)); -}*/ - // MAKE SURE YOU SAVE DATA BEFORE CALLING THIS void G_ClearRecords(void) { @@ -776,14 +515,6 @@ void G_ClearRecords(void) } // For easy retrieval of records -/*UINT32 G_GetBestScore(INT16 map) -{ - if (!mainrecords[map-1]) - return 0; - - return mainrecords[map-1]->score; -}*/ - tic_t G_GetBestTime(INT16 map) { if (!mainrecords[map-1] || mainrecords[map-1]->time <= 0) @@ -793,59 +524,15 @@ tic_t G_GetBestTime(INT16 map) } // Not needed -/*tic_t G_GetBestLap(INT16 map) +/* +tic_t G_GetBestLap(INT16 map) { if (!mainrecords[map-1] || mainrecords[map-1]->lap <= 0) return (tic_t)UINT32_MAX; return mainrecords[map-1]->lap; -}*/ - -/*UINT16 G_GetBestRings(INT16 map) -{ - if (!mainrecords[map-1]) - return 0; - - return mainrecords[map-1]->rings; -}*/ - -// No NiGHTS records for SRB2Kart -/*UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare) -{ - if (!nightsrecords[map-1]) - return 0; - - return nightsrecords[map-1]->score[mare]; -} - -tic_t G_GetBestNightsTime(INT16 map, UINT8 mare) -{ - if (!nightsrecords[map-1] || nightsrecords[map-1]->time[mare] <= 0) - return (tic_t)UINT32_MAX; - - return nightsrecords[map-1]->time[mare]; -} - -UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare) -{ - if (!nightsrecords[map-1]) - return 0; - - return nightsrecords[map-1]->grade[mare]; -} - -// For easy adding of NiGHTS records -void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare) -{ - ntemprecords.score[mare] = pscore; - ntemprecords.grade[mare] = P_GetGrade(pscore, gamemap, mare - 1); - ntemprecords.time[mare] = ptime; - - // Update nummares - // Note that mare "0" is overall, mare "1" is the first real mare - if (ntemprecords.nummares < mare) - ntemprecords.nummares = mare; } +*/ // // G_UpdateRecordReplays @@ -934,124 +621,6 @@ static void G_UpdateRecordReplays(void) Nextmap_OnChange(); } -void G_SetNightsRecords(void) -{ - INT32 i; - UINT32 totalscore = 0; - tic_t totaltime = 0; - UINT8 earnedEmblems; - - const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; - char *gpath; - char lastdemo[256], bestdemo[256]; - - if (!ntemprecords.nummares) - return; - - // Set overall - { - UINT8 totalrank = 0, realrank = 0; - - for (i = 1; i <= ntemprecords.nummares; ++i) - { - totalscore += ntemprecords.score[i]; - totalrank += ntemprecords.grade[i]; - totaltime += ntemprecords.time[i]; - } - - // Determine overall grade - realrank = (UINT8)((FixedDiv((fixed_t)totalrank << FRACBITS, ntemprecords.nummares << FRACBITS) + (FRACUNIT/2)) >> FRACBITS); - - // You need ALL rainbow As to get a rainbow A overall - if (realrank == GRADE_S && (totalrank / ntemprecords.nummares) != GRADE_S) - realrank = GRADE_A; - - ntemprecords.score[0] = totalscore; - ntemprecords.grade[0] = realrank; - ntemprecords.time[0] = totaltime; - } - - // Now take all temp records and put them in the actual records - { - nightsdata_t *maprecords; - - if (!nightsrecords[gamemap-1]) - G_AllocNightsRecordData(gamemap-1); - maprecords = nightsrecords[gamemap-1]; - - if (maprecords->nummares != ntemprecords.nummares) - maprecords->nummares = ntemprecords.nummares; - - for (i = 0; i < ntemprecords.nummares + 1; ++i) - { - if (maprecords->score[i] < ntemprecords.score[i]) - maprecords->score[i] = ntemprecords.score[i]; - if (maprecords->grade[i] < ntemprecords.grade[i]) - maprecords->grade[i] = ntemprecords.grade[i]; - if (!maprecords->time[i] || maprecords->time[i] > ntemprecords.time[i]) - maprecords->time[i] = ntemprecords.time[i]; - } - } - - memset(&ntemprecords, 0, sizeof(nightsdata_t)); - - // Save demo! - bestdemo[255] = '\0'; - lastdemo[255] = '\0'; - G_SetDemoTime(totaltime, totalscore); - G_CheckDemoStatus(); - - I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755); - I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755); - - if ((gpath = malloc(glen)) == NULL) - I_Error("Out of memory for replay filepath\n"); - - sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap)); - snprintf(lastdemo, 255, "%s-last.lmp", gpath); - - if (FIL_FileExists(lastdemo)) - { - UINT8 *buf; - size_t len = FIL_ReadFile(lastdemo, &buf); - - snprintf(bestdemo, 255, "%s-time-best.lmp", gpath); - if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1) - { // Better time, save this demo. - if (FIL_FileExists(bestdemo)) - remove(bestdemo); - FIL_WriteFile(bestdemo, buf, len); - CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo); - } - - snprintf(bestdemo, 255, "%s-score-best.lmp", gpath); - if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1))) - { // Better score, save this demo. - if (FIL_FileExists(bestdemo)) - remove(bestdemo); - FIL_WriteFile(bestdemo, buf, len); - CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo); - } - - //CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo); - - Z_Free(buf); - } - free(gpath); - - if ((earnedEmblems = M_CheckLevelEmblems())) - CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : ""); - - // If the mare count changed, this will update the score display -<<<<<<< HEAD - CV_AddValue(&cv_nextmap, 1); - CV_AddValue(&cv_nextmap, -1); -}*/ -======= - Nextmap_OnChange(); -} ->>>>>>> srb2/next - // for consistency among messages: this modifies the game and removes savemoddata. void G_SetGameModified(boolean silent, boolean major) { @@ -1149,7 +718,7 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } -static INT32 Joy1Axis(axis_input_e axissel) +static INT32 JoyAxis(axis_input_e axissel, UINT8 player) { INT32 retaxis; INT32 axisval; @@ -1159,25 +728,25 @@ static INT32 Joy1Axis(axis_input_e axissel) switch (axissel) { case AXISTURN: - axisval = cv_turnaxis.value; + axisval = cv_turnaxis[player].value; break; case AXISMOVE: - axisval = cv_moveaxis.value; + axisval = cv_moveaxis[player].value; break; case AXISBRAKE: - axisval = cv_brakeaxis.value; + axisval = cv_brakeaxis[player].value; break; case AXISAIM: - axisval = cv_aimaxis.value; + axisval = cv_aimaxis[player].value; break; case AXISLOOK: - axisval = cv_lookaxis.value; + axisval = cv_lookaxis[player].value; break; case AXISFIRE: - axisval = cv_fireaxis.value; + axisval = cv_fireaxis[player].value; break; case AXISDRIFT: - axisval = cv_driftaxis.value; + axisval = cv_driftaxis[player].value; break; default: return 0; @@ -1194,13 +763,13 @@ static INT32 Joy1Axis(axis_input_e axissel) if (axisval%2) { axisval /= 2; - retaxis = joyxmove[axisval]; + retaxis = joyxmove[player][axisval]; } else { axisval--; axisval /= 2; - retaxis = joyymove[axisval]; + retaxis = joyymove[player][axisval]; } if (retaxis < (-JOYAXISRANGE)) @@ -1208,251 +777,9 @@ static INT32 Joy1Axis(axis_input_e axissel) if (retaxis > (+JOYAXISRANGE)) retaxis = +JOYAXISRANGE; - if (!Joystick.bGamepadStyle && axissel > AXISDIGITAL) + if (!Joystick.bGamepadStyle && axissel >= AXISDIGITAL) { -<<<<<<< HEAD - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone.value) >> FRACBITS; - if (abs(retaxis) <= jdeadzone) -======= - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone.value) >> FRACBITS; - if (-jdeadzone < retaxis && retaxis < jdeadzone) ->>>>>>> srb2/next - return 0; - } - - if (flp) retaxis = -retaxis; //flip it around - return retaxis; -} - -static INT32 Joy2Axis(axis_input_e axissel) -{ - INT32 retaxis; - INT32 axisval; - boolean flp = false; - - //find what axis to get - switch (axissel) - { - case AXISTURN: - axisval = cv_turnaxis2.value; - break; - case AXISMOVE: - axisval = cv_moveaxis2.value; - break; - case AXISBRAKE: - axisval = cv_brakeaxis2.value; - break; - case AXISAIM: - axisval = cv_aimaxis2.value; - break; - case AXISLOOK: - axisval = cv_lookaxis2.value; - break; - case AXISFIRE: - axisval = cv_fireaxis2.value; - break; - case AXISDRIFT: - axisval = cv_driftaxis2.value; - break; - default: - return 0; - } - - - if (axisval < 0) //odd -axises - { - axisval = -axisval; - flp = true; - } - - if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None - return 0; - - if (axisval%2) - { - axisval /= 2; - retaxis = joy2xmove[axisval]; - } - else - { - axisval--; - axisval /= 2; - retaxis = joy2ymove[axisval]; - } - - if (retaxis < (-JOYAXISRANGE)) - retaxis = -JOYAXISRANGE; - if (retaxis > (+JOYAXISRANGE)) - retaxis = +JOYAXISRANGE; - - if (!Joystick2.bGamepadStyle && axissel > AXISDIGITAL) - { -<<<<<<< HEAD - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone2.value) >> FRACBITS; - if (-jdeadzone < retaxis && retaxis < jdeadzone) - return 0; - } - if (flp) retaxis = -retaxis; //flip it around - return retaxis; -} - -static INT32 Joy3Axis(axis_input_e axissel) -{ - INT32 retaxis; - INT32 axisval; - boolean flp = false; - - //find what axis to get - switch (axissel) - { - case AXISTURN: - axisval = cv_turnaxis3.value; - break; - case AXISMOVE: - axisval = cv_moveaxis3.value; - break; - case AXISBRAKE: - axisval = cv_brakeaxis3.value; - break; - case AXISAIM: - axisval = cv_aimaxis3.value; - break; - case AXISLOOK: - axisval = cv_lookaxis3.value; - break; - case AXISFIRE: - axisval = cv_fireaxis3.value; - break; - case AXISDRIFT: - axisval = cv_driftaxis3.value; - break; - default: - return 0; - } - - - if (axisval < 0) //odd -axises - { - axisval = -axisval; - flp = true; - } -#ifdef _arch_dreamcast - if (axisval == 7) // special case - { - retaxis = joy3xmove[1] - joy3ymove[1]; - goto skipDC; - } - else -#endif - if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None - return 0; - - if (axisval%2) - { - axisval /= 2; - retaxis = joy3xmove[axisval]; - } - else - { - axisval--; - axisval /= 2; - retaxis = joy3ymove[axisval]; - } - -#ifdef _arch_dreamcast - skipDC: -#endif - - if (retaxis < (-JOYAXISRANGE)) - retaxis = -JOYAXISRANGE; - if (retaxis > (+JOYAXISRANGE)) - retaxis = +JOYAXISRANGE; - if (!Joystick3.bGamepadStyle && axissel < AXISDEAD) - { - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone3.value) >> FRACBITS; - if (-jdeadzone < retaxis && retaxis < jdeadzone) - return 0; - } - if (flp) retaxis = -retaxis; //flip it around - return retaxis; -} - -static INT32 Joy4Axis(axis_input_e axissel) -{ - INT32 retaxis; - INT32 axisval; - boolean flp = false; - - //find what axis to get - switch (axissel) - { - case AXISTURN: - axisval = cv_turnaxis4.value; - break; - case AXISMOVE: - axisval = cv_moveaxis4.value; - break; - case AXISBRAKE: - axisval = cv_brakeaxis4.value; - break; - case AXISAIM: - axisval = cv_aimaxis4.value; - break; - case AXISLOOK: - axisval = cv_lookaxis4.value; - break; - case AXISFIRE: - axisval = cv_fireaxis4.value; - break; - case AXISDRIFT: - axisval = cv_driftaxis4.value; - break; - default: - return 0; - } - - if (axisval < 0) //odd -axises - { - axisval = -axisval; - flp = true; - } -#ifdef _arch_dreamcast - if (axisval == 7) // special case - { - retaxis = joy4xmove[1] - joy4ymove[1]; - goto skipDC; - } - else -#endif - if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None - return 0; - - if (axisval%2) - { - axisval /= 2; - retaxis = joy4xmove[axisval]; - } - else - { - axisval--; - axisval /= 2; - retaxis = joy4ymove[axisval]; - } - -#ifdef _arch_dreamcast - skipDC: -#endif - - if (retaxis < (-JOYAXISRANGE)) - retaxis = -JOYAXISRANGE; - if (retaxis > (+JOYAXISRANGE)) - retaxis = +JOYAXISRANGE; - if (!Joystick4.bGamepadStyle && axissel < AXISDEAD) - { - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone4.value) >> FRACBITS; -======= - const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone2.value) >> FRACBITS; ->>>>>>> srb2/next + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone[player].value) >> FRACBITS; if (-jdeadzone < retaxis && retaxis < jdeadzone) return 0; } @@ -1461,38 +788,6 @@ static INT32 Joy4Axis(axis_input_e axissel) return retaxis; } -boolean InputDown(INT32 gc, UINT8 p) -{ - switch (p) - { - case 2: - return PLAYER2INPUTDOWN(gc); - case 3: - return PLAYER3INPUTDOWN(gc); - case 4: - return PLAYER4INPUTDOWN(gc); - default: - return PLAYER1INPUTDOWN(gc); - } -} - -INT32 JoyAxis(axis_input_e axissel, UINT8 p) -{ - switch (p) - { - case 2: - return Joy2Axis(axissel); - case 3: - return Joy3Axis(axissel); - case 4: - return Joy4Axis(axissel); - default: - return Joy1Axis(axissel); - } -} - -#define PlayerJoyAxis(p, ax) ((p) == 1 ? JoyAxis(ax) : Joy2Axis(ax)) - // Take a magnitude of two axes, and adjust it to take out the deadzone // Will return a value between 0 and JOYAXISRANGE static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone) @@ -1518,14 +813,8 @@ static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone) // Get the actual sensible radial value for a joystick axis when accounting for a deadzone static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvector) { - INT32 gamepadStyle = Joystick.bGamepadStyle; - fixed_t deadZone = cv_deadzone.value; - - if (splitnum == 1) - { - gamepadStyle = Joystick2.bGamepadStyle; - deadZone = cv_deadzone2.value; - } + INT32 gamepadStyle = Joystick[splitnum].bGamepadStyle; + fixed_t deadZone = cv_deadzone[splitnum].value; // When gamepadstyle is "true" the values are just -1, 0, or 1. This is done in the interface code. if (!gamepadStyle) @@ -1572,7 +861,6 @@ static fixed_t forwardmove[2] = {25<>16, 50<>16}; static fixed_t sidemove[2] = {2<>16, 4<>16}; static fixed_t angleturn[3] = {KART_FULLTURN/2, KART_FULLTURN, KART_FULLTURN/4}; // + slow turn -<<<<<<< HEAD void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { INT32 laim, th, tspeed, forward, side, axis; //i @@ -1682,124 +970,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } if (gamepadjoystickmove && axis != 0) -======= -boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player -mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object? -void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) -{ - boolean forcestrafe = false; - boolean forcefullinput = false; - INT32 tspeed, forward, side, axis, strafeaxis, moveaxis, turnaxis, lookaxis, i; - - joystickvector2_t movejoystickvector, lookjoystickvector; - - const INT32 speed = 1; - // these ones used for multiple conditions - boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming; - boolean strafeisturn; // Simple controls only - player_t *player = &players[ssplayer == 2 ? secondarydisplayplayer : consoleplayer]; - camera_t *thiscam = ((ssplayer == 1 || player->bot == 2) ? &camera : &camera2); - angle_t *myangle = (ssplayer == 1 ? &localangle : &localangle2); - INT32 *myaiming = (ssplayer == 1 ? &localaiming : &localaiming2); - - angle_t drawangleoffset = (player->powers[pw_carry] == CR_ROLLOUT) ? ANGLE_180 : 0; - INT32 chasecam, chasefreelook, alwaysfreelook, usejoystick, invertmouse, turnmultiplier, mousemove; - controlstyle_e controlstyle = G_ControlStyle(ssplayer); - INT32 *mx; INT32 *my; INT32 *mly; - - static INT32 turnheld[2]; // for accelerative turning - static boolean keyboard_look[2]; // true if lookup/down using keyboard - static boolean resetdown[2]; // don't cam reset every frame - static boolean joyaiming[2]; // check the last frame's value if we need to reset the camera - - // simple mode vars - static boolean zchange[2]; // only switch z targets once per press - static fixed_t tta_factor[2] = {FRACUNIT, FRACUNIT}; // disables turn-to-angle when manually turning camera until movement happens - boolean centerviewdown = false; - - UINT8 forplayer = ssplayer-1; - - if (ssplayer == 1) - { - chasecam = cv_chasecam.value; - chasefreelook = cv_chasefreelook.value; - alwaysfreelook = cv_alwaysfreelook.value; - usejoystick = cv_usejoystick.value; - invertmouse = cv_invertmouse.value; - turnmultiplier = cv_cam_turnmultiplier.value; - mousemove = cv_mousemove.value; - mx = &mousex; - my = &mousey; - mly = &mlooky; - G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver - } - else - { - chasecam = cv_chasecam2.value; - chasefreelook = cv_chasefreelook2.value; - alwaysfreelook = cv_alwaysfreelook2.value; - usejoystick = cv_usejoystick2.value; - invertmouse = cv_invertmouse2.value; - turnmultiplier = cv_cam2_turnmultiplier.value; - mousemove = cv_mousemove2.value; - mx = &mouse2x; - my = &mouse2y; - mly = &mlook2y; - G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver - } - - strafeisturn = controlstyle == CS_SIMPLE && ticcmd_centerviewdown[forplayer] && - ((cv_cam_lockedinput[forplayer].value && !ticcmd_ztargetfocus[forplayer]) || (player->pflags & PF_STARTDASH)) && - !player->climbing && player->powers[pw_carry] != CR_MINECART; - - // why build a ticcmd if we're paused? - // Or, for that matter, if we're being reborn. - // ...OR if we're blindfolded. No looking into the floor. - if (paused || P_AutoPause() || (gamestate == GS_LEVEL && (player->playerstate == PST_REBORN || ((gametyperules & GTR_TAG) - && (leveltime < hidetime * TICRATE) && (player->pflags & PF_TAGIT))))) - {//@TODO splitscreen player - cmd->angleturn = (INT16)(*myangle >> 16); - cmd->aiming = G_ClipAimingPitch(myaiming); - return; - } - - turnright = PLAYERINPUTDOWN(ssplayer, gc_turnright); - turnleft = PLAYERINPUTDOWN(ssplayer, gc_turnleft); - - straferkey = PLAYERINPUTDOWN(ssplayer, gc_straferight); - strafelkey = PLAYERINPUTDOWN(ssplayer, gc_strafeleft); - movefkey = PLAYERINPUTDOWN(ssplayer, gc_forward); - movebkey = PLAYERINPUTDOWN(ssplayer, gc_backward); - - if (strafeisturn) - { - turnright |= straferkey; - turnleft |= strafelkey; - straferkey = strafelkey = false; - } - - mouseaiming = (PLAYERINPUTDOWN(ssplayer, gc_mouseaiming)) ^ - ((chasecam && !player->spectator) ? chasefreelook : alwaysfreelook); - analogjoystickmove = usejoystick && !Joystick.bGamepadStyle; - gamepadjoystickmove = usejoystick && Joystick.bGamepadStyle; - - thisjoyaiming = (chasecam && !player->spectator) ? chasefreelook : alwaysfreelook; - - // Reset the vertical look if we're no longer joyaiming - if (!thisjoyaiming && joyaiming[forplayer]) - *myaiming = 0; - joyaiming[forplayer] = thisjoyaiming; - - turnaxis = PlayerJoyAxis(ssplayer, AXISTURN); - if (strafeisturn) - turnaxis += PlayerJoyAxis(ssplayer, AXISSTRAFE); - lookaxis = PlayerJoyAxis(ssplayer, AXISLOOK); - lookjoystickvector.xaxis = turnaxis; - lookjoystickvector.yaxis = lookaxis; - G_HandleAxisDeadZone(forplayer, &lookjoystickvector); - - if (gamepadjoystickmove && lookjoystickvector.xaxis != 0) ->>>>>>> srb2/next { turnright = turnright || (lookjoystickvector.xaxis > 0); turnleft = turnleft || (lookjoystickvector.xaxis < 0); @@ -1809,19 +979,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // use two stage accelerative turning // on the keyboard and joystick if (turnleft || turnright) -<<<<<<< HEAD - th += realtics; - else - th = 0; - - if (th < SLOWTURNTICS) -======= turnheld[forplayer] += realtics; else turnheld[forplayer] = 0; if (turnheld[forplayer] < SLOWTURNTICS) ->>>>>>> srb2/next tspeed = 2; // slow turn else tspeed = speed; @@ -1829,7 +991,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->driftturn = 0; // let movement keys cancel each other out -<<<<<<< HEAD if (turnright && !(turnleft)) { cmd->angleturn = (INT16)(cmd->angleturn - (angleturn[tspeed])); @@ -1890,113 +1051,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) axis = JoyAxis(AXISBRAKE, ssplayer); if (InputDown(gc_brake, ssplayer) || (gamepadjoystickmove && axis > 0)) -======= - if (controlstyle == CS_LMAOGALOG) // Analog - { - if (turnright) - cmd->angleturn = (INT16)(cmd->angleturn - angleturn[tspeed]); - if (turnleft) - cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]); - } - if (twodlevel - || (player->mo && (player->mo->flags2 & MF2_TWOD)) - || (!demoplayback && (player->pflags & PF_SLIDING))) - forcefullinput = true; - if (twodlevel - || (player->mo && (player->mo->flags2 & MF2_TWOD)) - || (!demoplayback && ((player->powers[pw_carry] == CR_NIGHTSMODE) - || (player->pflags & (PF_SLIDING|PF_FORCESTRAFE))))) // Analog - forcestrafe = true; - if (forcestrafe) - { - if (turnright) - side += sidemove[speed]; - if (turnleft) - side -= sidemove[speed]; - - if (analogjoystickmove && lookjoystickvector.xaxis != 0) - { - // JOYAXISRANGE is supposed to be 1023 (divide by 1024) - side += ((lookjoystickvector.xaxis * sidemove[1]) >> 10); - } - } - else if (controlstyle == CS_LMAOGALOG) // Analog - { - if (turnright) - cmd->buttons |= BT_CAMRIGHT; - if (turnleft) - cmd->buttons |= BT_CAMLEFT; - } - else - { - if (turnright && turnleft); - else if (turnright) - cmd->angleturn = (INT16)(cmd->angleturn - ((angleturn[tspeed] * turnmultiplier)>>FRACBITS)); - else if (turnleft) - cmd->angleturn = (INT16)(cmd->angleturn + ((angleturn[tspeed] * turnmultiplier)>>FRACBITS)); - - if (analogjoystickmove && lookjoystickvector.xaxis != 0) - { - // JOYAXISRANGE should be 1023 (divide by 1024) - cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 10) * turnmultiplier)>>FRACBITS)); // ANALOG! - } - - if (turnright || turnleft || abs(cmd->angleturn) > angleturn[2]) - tta_factor[forplayer] = 0; // suspend turn to angle - } - - strafeaxis = strafeisturn ? 0 : PlayerJoyAxis(ssplayer, AXISSTRAFE); - moveaxis = PlayerJoyAxis(ssplayer, AXISMOVE); - movejoystickvector.xaxis = strafeaxis; - movejoystickvector.yaxis = moveaxis; - G_HandleAxisDeadZone(forplayer, &movejoystickvector); - - if (gamepadjoystickmove && movejoystickvector.xaxis != 0) - { - if (movejoystickvector.xaxis > 0) - side += sidemove[speed]; - else if (movejoystickvector.xaxis < 0) - side -= sidemove[speed]; - } - else if (analogjoystickmove && movejoystickvector.xaxis != 0) - { - // JOYAXISRANGE is supposed to be 1023 (divide by 1024) - side += ((movejoystickvector.xaxis * sidemove[1]) >> 10); - } - - // forward with key or button - if (movefkey || (gamepadjoystickmove && movejoystickvector.yaxis < 0) - || ((player->powers[pw_carry] == CR_NIGHTSMODE) - && (PLAYERINPUTDOWN(ssplayer, gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)))) - forward = forwardmove[speed]; - if (movebkey || (gamepadjoystickmove && movejoystickvector.yaxis > 0) - || ((player->powers[pw_carry] == CR_NIGHTSMODE) - && (PLAYERINPUTDOWN(ssplayer, gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)))) - forward -= forwardmove[speed]; - - if (analogjoystickmove && movejoystickvector.yaxis != 0) - forward -= ((movejoystickvector.yaxis * forwardmove[1]) >> 10); // ANALOG! - - // some people strafe left & right with mouse buttons - // those people are weird - if (straferkey) - side += sidemove[speed]; - if (strafelkey) - side -= sidemove[speed]; - - if (PLAYERINPUTDOWN(ssplayer, gc_weaponnext)) - cmd->buttons |= BT_WEAPONNEXT; // Next Weapon - if (PLAYERINPUTDOWN(ssplayer, gc_weaponprev)) - cmd->buttons |= BT_WEAPONPREV; // Previous Weapon - -#if NUM_WEAPONS > 10 -"Add extra inputs to g_input.h/gamecontrols_e" -#endif - //use the four avaliable bits to determine the weapon. - cmd->buttons &= ~BT_WEAPONMASK; - for (i = 0; i < NUM_WEAPONS; ++i) - if (PLAYERINPUTDOWN(ssplayer, gc_wepslot1 + i)) ->>>>>>> srb2/next { cmd->buttons |= BT_BRAKE; if (cmd->buttons & BT_ACCELERATE || cmd->forwardmove <= 0) @@ -2010,7 +1064,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) forward -= ((axis * forwardmove[0]) >> 10); } -<<<<<<< HEAD // But forward/backward IS used for aiming. axis = JoyAxis(AXISAIM, ssplayer); if (InputDown(gc_aimforward, ssplayer) || (usejoystick && axis < 0)) @@ -2051,151 +1104,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } else rd = false; -======= - // fire with any button/key - axis = PlayerJoyAxis(ssplayer, AXISFIRE); - if (PLAYERINPUTDOWN(ssplayer, gc_fire) || (usejoystick && axis > 0)) - cmd->buttons |= BT_ATTACK; - - // fire normal with any button/key - axis = PlayerJoyAxis(ssplayer, AXISFIRENORMAL); - if (PLAYERINPUTDOWN(ssplayer, gc_firenormal) || (usejoystick && axis > 0)) - cmd->buttons |= BT_FIRENORMAL; - - if (PLAYERINPUTDOWN(ssplayer, gc_tossflag)) - cmd->buttons |= BT_TOSSFLAG; - - // Lua scriptable buttons - if (PLAYERINPUTDOWN(ssplayer, gc_custom1)) - cmd->buttons |= BT_CUSTOM1; - if (PLAYERINPUTDOWN(ssplayer, gc_custom2)) - cmd->buttons |= BT_CUSTOM2; - if (PLAYERINPUTDOWN(ssplayer, gc_custom3)) - cmd->buttons |= BT_CUSTOM3; - - // use with any button/key - axis = PlayerJoyAxis(ssplayer, AXISSPIN); - if (PLAYERINPUTDOWN(ssplayer, gc_use) || (usejoystick && axis > 0)) - cmd->buttons |= BT_USE; - - // Centerview can be a toggle in simple mode! - { - static boolean last_centerviewdown[2], centerviewhold[2]; // detect taps for toggle behavior - boolean down = PLAYERINPUTDOWN(ssplayer, gc_centerview); - - if (!(controlstyle == CS_SIMPLE && cv_cam_centertoggle[forplayer].value)) - centerviewdown = down; - else - { - if (down && !last_centerviewdown[forplayer]) - centerviewhold[forplayer] = !centerviewhold[forplayer]; - last_centerviewdown[forplayer] = down; - - if (cv_cam_centertoggle[forplayer].value == 2 && !down && !ticcmd_ztargetfocus[forplayer]) - centerviewhold[forplayer] = false; - - centerviewdown = centerviewhold[forplayer]; - } - } - - if (centerviewdown) - { - if (controlstyle == CS_SIMPLE && !ticcmd_centerviewdown[forplayer] && !G_RingSlingerGametype()) - { - CV_SetValue(&cv_directionchar[forplayer], 2); - *myangle = player->mo->angle; - *myaiming = 0; - - if (cv_cam_lockonboss[forplayer].value) - P_SetTarget(&ticcmd_ztargetfocus[forplayer], P_LookForFocusTarget(player, NULL, 0, cv_cam_lockonboss[forplayer].value)); - } - - ticcmd_centerviewdown[forplayer] = true; - } - else if (ticcmd_centerviewdown[forplayer]) - { - if (controlstyle == CS_SIMPLE) - { - P_SetTarget(&ticcmd_ztargetfocus[forplayer], NULL); - CV_SetValue(&cv_directionchar[forplayer], 1); - } - - ticcmd_centerviewdown[forplayer] = false; - } - - if (ticcmd_ztargetfocus[forplayer]) - { - if ( - P_MobjWasRemoved(ticcmd_ztargetfocus[forplayer]) || - !ticcmd_ztargetfocus[forplayer]->health || - (ticcmd_ztargetfocus[forplayer]->flags2 & MF2_FRET) || - (ticcmd_ztargetfocus[forplayer]->type == MT_EGGMOBILE3 && !ticcmd_ztargetfocus[forplayer]->movecount) // Sea Egg is moving around underground and shouldn't be tracked - ) - P_SetTarget(&ticcmd_ztargetfocus[forplayer], NULL); - else - { - mobj_t *newtarget = NULL; - if (zchange[forplayer]) - { - if (!turnleft && !turnright && abs(cmd->angleturn) < angleturn[0]) - zchange[forplayer] = false; - } - else if (turnleft || cmd->angleturn > angleturn[0]) - { - zchange[forplayer] = true; - newtarget = P_LookForFocusTarget(player, ticcmd_ztargetfocus[forplayer], 1, cv_cam_lockonboss[forplayer].value); - } - else if (turnright || cmd->angleturn < -angleturn[0]) - { - zchange[forplayer] = true; - newtarget = P_LookForFocusTarget(player, ticcmd_ztargetfocus[forplayer], -1, cv_cam_lockonboss[forplayer].value); - } - - if (newtarget) - P_SetTarget(&ticcmd_ztargetfocus[forplayer], newtarget); - - // I assume this is netgame-safe because gunslinger spawns this for only the local player...... *sweats intensely* - newtarget = P_SpawnMobj(ticcmd_ztargetfocus[forplayer]->x, ticcmd_ztargetfocus[forplayer]->y, ticcmd_ztargetfocus[forplayer]->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker - P_SetTarget(&newtarget->target, ticcmd_ztargetfocus[forplayer]); - - if (P_AproxDistance( - player->mo->x - ticcmd_ztargetfocus[forplayer]->x, - player->mo->y - ticcmd_ztargetfocus[forplayer]->y - ) > 50*player->mo->scale) - { - INT32 anglediff = R_PointToAngle2(player->mo->x, player->mo->y, ticcmd_ztargetfocus[forplayer]->x, ticcmd_ztargetfocus[forplayer]->y) - *myangle; - const INT32 maxturn = ANG10/2; - anglediff /= 4; - - if (anglediff > maxturn) - anglediff = maxturn; - else if (anglediff < -maxturn) - anglediff = -maxturn; - - *myangle += anglediff; - } - } - } - - if (ticcmd_centerviewdown[forplayer] && controlstyle == CS_SIMPLE) - controlstyle = CS_LEGACY; - - if (PLAYERINPUTDOWN(ssplayer, gc_camreset)) - { - if (thiscam->chase && !resetdown[forplayer]) - P_ResetCamera(&players[ssplayer == 1 ? displayplayer : secondarydisplayplayer], thiscam); - - resetdown[forplayer] = true; - } - else - resetdown[forplayer] = false; - - - // jump button - axis = PlayerJoyAxis(ssplayer, AXISJUMP); - if (PLAYERINPUTDOWN(ssplayer, gc_jump) || (usejoystick && axis > 0)) - cmd->buttons |= BT_JUMP; ->>>>>>> srb2/next // spectator aiming shit, ahhhh... { @@ -2209,7 +1117,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // mouse look stuff (mouse look is not the same as mouse aim) if (mouseaiming && player->spectator) { -<<<<<<< HEAD kbl = false; // looking up/down @@ -2236,35 +1143,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) laim -= KB_LOOKSPEED * screen_invert; kbl = true; } -======= - keyboard_look[forplayer] = false; - - // looking up/down - *myaiming += (*mly<<19)*player_invert*screen_invert; - } - - if (analogjoystickmove && joyaiming[forplayer] && lookjoystickvector.yaxis != 0 && configlookaxis != 0) - *myaiming += (lookjoystickvector.yaxis<<16) * screen_invert; - - // spring back if not using keyboard neither mouselookin' - if (!keyboard_look[forplayer] && configlookaxis == 0 && !joyaiming[forplayer] && !mouseaiming) - *myaiming = 0; - - if (!(player->powers[pw_carry] == CR_NIGHTSMODE)) - { - if (PLAYERINPUTDOWN(ssplayer, gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)) - { - *myaiming += KB_LOOKSPEED * screen_invert; - keyboard_look[forplayer] = true; - } - else if (PLAYERINPUTDOWN(ssplayer, gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)) - { - *myaiming -= KB_LOOKSPEED * screen_invert; - keyboard_look[forplayer] = true; - } - else if (ticcmd_centerviewdown[forplayer]) - *myaiming = 0; ->>>>>>> srb2/next } if (InputDown(gc_centerview, ssplayer)) // No need to put a spectator limit on this one though :V @@ -2272,39 +1150,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // accept no mlook for network games if (!cv_allowmlook.value) -<<<<<<< HEAD laim = 0; cmd->aiming = G_ClipAimingPitch(&laim); } mousex = mousey = mlooky = 0; -======= - *myaiming = 0; - - cmd->aiming = G_ClipAimingPitch(myaiming); - } - - if (!mouseaiming && mousemove) - forward += *my; - - if ((!demoplayback && (player->pflags & PF_SLIDING))) // Analog for mouse - side += *mx*2; - else if (controlstyle == CS_LMAOGALOG) - { - if (*mx) - { - if (*mx > 0) - cmd->buttons |= BT_CAMRIGHT; - else - cmd->buttons |= BT_CAMLEFT; - } - } - else - cmd->angleturn = (INT16)(cmd->angleturn - (*mx*8)); - - *mx = *my = *mly = 0; ->>>>>>> srb2/next if (forward > MAXPLMOVE) forward = MAXPLMOVE; @@ -2316,7 +1167,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else if (side < -MAXPLMOVE) side = -MAXPLMOVE; -<<<<<<< HEAD if (forward || side) { cmd->forwardmove = (SINT8)(cmd->forwardmove + forward); @@ -2370,149 +1220,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) -Making some galaxy brain autopilot Lua if you're a masochist -Making a Mario Kart 8 Deluxe tier baby mode that steers you away from walls and whatnot. You know what, do what you want! */ -#ifdef HAVE_BLUA if (gamestate == GS_LEVEL) LUAh_PlayerCmd(player, cmd); -#endif //Reset away view if a command is given. if ((cmd->forwardmove || cmd->sidemove || cmd->buttons) && !r_splitscreen && displayplayers[0] != consoleplayer && ssplayer == 1) - displayplayers[0] = consoleplayer; -======= - // No additional acceleration when moving forward/backward and strafing simultaneously. - // do this AFTER we cap to MAXPLMOVE so people can't find ways to cheese around this. - if (!forcefullinput && forward && side) - { - angle_t angle = R_PointToAngle2(0, 0, side << FRACBITS, forward << FRACBITS); - INT32 maxforward = abs(P_ReturnThrustY(NULL, angle, MAXPLMOVE)); - INT32 maxside = abs(P_ReturnThrustX(NULL, angle, MAXPLMOVE)); - forward = max(min(forward, maxforward), -maxforward); - side = max(min(side, maxside), -maxside); - } - - //Silly hack to make 2d mode *somewhat* playable with no chasecam. - if ((twodlevel || (player->mo && player->mo->flags2 & MF2_TWOD)) && !thiscam->chase) - { - INT32 temp = forward; - forward = side; - side = temp; - } - - cmd->forwardmove = (SINT8)(cmd->forwardmove + forward); - cmd->sidemove = (SINT8)(cmd->sidemove + side); - - if (player->bot == 1) { // Tailsbot for P2 - if (!player->powers[pw_tailsfly] && (cmd->forwardmove || cmd->sidemove || cmd->buttons)) - { - player->bot = 2; // A player-controlled bot. Returns to AI when it respawns. - CV_SetValue(&cv_analog[1], true); - } - else - { - G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver - B_BuildTiccmd(player, cmd); - } - B_HandleFlightIndicator(player); - } - else if (player->bot == 2) - *myangle = localangle; // Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy - - if (controlstyle == CS_LMAOGALOG) { - if (player->awayviewtics) - cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16); - else - cmd->angleturn = (INT16)(thiscam->angle >> 16); - } - else - { - *myangle += (cmd->angleturn<<16); - cmd->angleturn = (INT16)(*myangle >> 16); - - // Adjust camera angle by player input - if (controlstyle == CS_SIMPLE && !forcestrafe && thiscam->chase && !turnheld[forplayer] && !ticcmd_centerviewdown[forplayer] && !player->climbing && player->powers[pw_carry] != CR_MINECART) - { - fixed_t camadjustfactor = cv_cam_turnfacinginput[forplayer].value; - - if (camadjustfactor) - { - fixed_t sine = FINESINE((R_PointToAngle2(0, 0, player->rmomx, player->rmomy) - localangle)>>ANGLETOFINESHIFT); - fixed_t factor; - - if ((sine > 0) == (cmd->sidemove > 0)) - sine = 0; // Prevent jerking right when braking from going left, or vice versa - - factor = min(40, FixedMul(player->speed, abs(sine))*2 / FRACUNIT); - - *myangle -= cmd->sidemove * factor * camadjustfactor; - } - - if (ticcmd_centerviewdown[forplayer] && (cv_cam_lockedinput[forplayer].value || (player->pflags & PF_STARTDASH))) - cmd->sidemove = 0; - } - - // Adjust camera angle to face player direction, depending on circumstances - // Nothing happens if cam left/right are held, so you can hold both to lock the camera in one direction - if (controlstyle == CS_SIMPLE && !forcestrafe && thiscam->chase && !turnheld[forplayer] && !ticcmd_centerviewdown[forplayer] && player->powers[pw_carry] != CR_MINECART) - { - fixed_t camadjustfactor; - boolean alt = false; // Reduce intensity on diagonals and prevent backwards movement from turning the camera - - if (player->pflags & PF_GLIDING) - camadjustfactor = cv_cam_turnfacingability[forplayer].value/4; - else if (player->pflags & PF_STARTDASH) - camadjustfactor = cv_cam_turnfacingspindash[forplayer].value/4; - else - { - alt = true; - camadjustfactor = cv_cam_turnfacing[forplayer].value/8; - } - - camadjustfactor = FixedMul(camadjustfactor, max(FRACUNIT - player->speed, min(player->speed/18, FRACUNIT))); - - camadjustfactor = FixedMul(camadjustfactor, tta_factor[forplayer]); - - if (tta_factor[forplayer] < FRACUNIT && (cmd->forwardmove || cmd->sidemove || tta_factor[forplayer] >= FRACUNIT/3)) - tta_factor[forplayer] += FRACUNIT>>5; - else if (tta_factor[forplayer] && tta_factor[forplayer] < FRACUNIT/3) - tta_factor[forplayer] -= FRACUNIT>>5; - - if (camadjustfactor) - { - angle_t controlangle; - INT32 anglediff; - - if ((cmd->forwardmove || cmd->sidemove) && !(player->pflags & PF_SPINNING)) - controlangle = (cmd->angleturn<<16) + R_PointToAngle2(0, 0, cmd->forwardmove << FRACBITS, -cmd->sidemove << FRACBITS); - else - controlangle = player->drawangle + drawangleoffset; - - anglediff = controlangle - *myangle; - - if (alt) - { - fixed_t sine = FINESINE((angle_t) (anglediff)>>ANGLETOFINESHIFT); - sine = abs(sine); - - if (abs(anglediff) > ANGLE_90) - sine = max(0, sine*3 - 2*FRACUNIT); // At about 135 degrees, this will stop turning - - anglediff = FixedMul(anglediff, sine); - } - - *myangle += FixedMul(anglediff, camadjustfactor); - } - } - } - - //Reset away view if a command is given. - if (ssplayer == 1 && (cmd->forwardmove || cmd->sidemove || cmd->buttons) - && displayplayer != consoleplayer) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. LUAh_ViewpointSwitch(player, &players[consoleplayer], true); - displayplayer = consoleplayer; + displayplayers[0] = consoleplayer; } } @@ -2533,157 +1251,6 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) dest[i].buttons = (UINT16)SHORT(src[i].buttons); } return dest; ->>>>>>> srb2/next -} - -// User has designated that they want -// analog ON, so tell the game to stop -// fudging with it. -static void UserAnalog_OnChange(void) -{ -<<<<<<< HEAD - /*if (cv_useranalog.value) - CV_SetValue(&cv_analog, 1); - else - CV_SetValue(&cv_analog, 0);*/ -======= - if (cv_useranalog[0].value) - CV_SetValue(&cv_analog[0], 1); - else - CV_SetValue(&cv_analog[0], 0); ->>>>>>> srb2/next -} - -static void UserAnalog2_OnChange(void) -{ -<<<<<<< HEAD - /*if (cv_useranalog2.value) - CV_SetValue(&cv_analog2, 1); - else - CV_SetValue(&cv_analog2, 0);*/ -} - -static void UserAnalog3_OnChange(void) -{ - /*if (cv_useranalog3.value) - CV_SetValue(&cv_analog3, 1); - else - CV_SetValue(&cv_analog3, 0);*/ -} - -static void UserAnalog4_OnChange(void) -{ - /*if (cv_useranalog4.value) - CV_SetValue(&cv_analog4, 1); - else - CV_SetValue(&cv_analog4, 0);*/ -======= - if (botingame) - return; - if (cv_useranalog[1].value) - CV_SetValue(&cv_analog[1], 1); - else - CV_SetValue(&cv_analog[1], 0); ->>>>>>> srb2/next -} - -static void Analog_OnChange(void) -{ - if (!cv_cam_dist.string) - return; - - // cameras are not initialized at this point - -<<<<<<< HEAD - /* - if (!cv_chasecam.value && cv_analog.value) { - CV_SetValue(&cv_analog, 0); -======= - if (!cv_chasecam.value && cv_analog[0].value) { - CV_SetValue(&cv_analog[0], 0); ->>>>>>> srb2/next - return; - } - */ - - SendWeaponPref(); -} - -static void Analog2_OnChange(void) -{ - if (!splitscreen || !cv_cam2_dist.string) - return; - - // cameras are not initialized at this point - -<<<<<<< HEAD - /* - if (!cv_chasecam2.value && cv_analog2.value) { - CV_SetValue(&cv_analog2, 0); -======= - if (!cv_chasecam2.value && cv_analog[1].value) { - CV_SetValue(&cv_analog[1], 0); ->>>>>>> srb2/next - return; - } - */ - - SendWeaponPref2(); -} - -<<<<<<< HEAD -static void Analog3_OnChange(void) -{ - if (splitscreen < 2 || !cv_cam3_dist.string) - return; - - // cameras are not initialized at this point - - /* - if (!cv_chasecam3.value && cv_analog3.value) { - CV_SetValue(&cv_analog3, 0); - return; - } - */ - - SendWeaponPref3(); -} - -static void Analog4_OnChange(void) -{ - if (splitscreen < 3 || !cv_cam4_dist.string) - return; - - // cameras are not initialized at this point - - /* - if (!cv_chasecam4.value && cv_analog4.value) { - CV_SetValue(&cv_analog4, 0); - return; - } - */ - - SendWeaponPref4(); -======= -static void DirectionChar_OnChange(void) -{ - SendWeaponPref(); -} - -static void DirectionChar2_OnChange(void) -{ - SendWeaponPref2(); -} - -static void AutoBrake_OnChange(void) -{ - SendWeaponPref(); -} - -static void AutoBrake2_OnChange(void) -{ - SendWeaponPref2(); ->>>>>>> srb2/next } // @@ -2778,20 +1345,11 @@ void G_DoLoadLevel(boolean resetplayer) server_lagless = cv_lagless.value; } -<<<<<<< HEAD -static INT32 pausedelay = 0; -static INT32 camtoggledelay, camtoggledelay2, camtoggledelay3, camtoggledelay4 = 0; -static INT32 spectatedelay, spectatedelay2, spectatedelay3, spectatedelay4 = 0; - -======= ->>>>>>> srb2/next // // Start the title card. // void G_StartTitleCard(void) { -<<<<<<< HEAD -======= // The title card has been disabled for this map. // Oh well. if (!G_IsTitleCardAvailable()) @@ -2873,7 +1431,7 @@ boolean G_IsTitleCardAvailable(void) INT32 pausedelay = 0; boolean pausebreakkey = false; -static INT32 camtoggledelay, camtoggledelay2 = 0; +static INT32 camtoggledelay[MAXSPLITSCREENPLAYERS]; // // G_Responder @@ -2881,7 +1439,6 @@ static INT32 camtoggledelay, camtoggledelay2 = 0; // boolean G_Responder(event_t *ev) { ->>>>>>> srb2/next // any other key pops up menu if in demos if (gameaction == ga_nothing && !demo.quitafterplaying && ((demo.playback && !modeattacking && !demo.title && !multiplayer) || gamestate == GS_TITLESCREEN)) @@ -2953,13 +1510,10 @@ boolean G_Responder(event_t *ev) } // Demo End else if (gamestate == GS_GAMEEND) + { return true; -<<<<<<< HEAD - - else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_WAITINGPLAYERS) -======= - else if (gamestate == GS_INTERMISSION || gamestate == GS_EVALUATION) ->>>>>>> srb2/next + } + else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_EVALUATION) if (HU_Responder(ev)) return true; // chat ate the event @@ -2967,81 +1521,11 @@ boolean G_Responder(event_t *ev) if (gamestate == GS_LEVEL && ev->type == ev_keydown && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1])) { -<<<<<<< HEAD if (!demo.playback && (r_splitscreen || !netgame)) g_localplayers[0] = consoleplayer; else { G_AdjustView(1, 1, true); -======= - // ViewpointSwitch Lua hook. - UINT8 canSwitchView = 0; - - if (splitscreen || !netgame) - displayplayer = consoleplayer; - else - { - // spy mode - do - { - displayplayer++; - if (displayplayer == MAXPLAYERS) - displayplayer = 0; - - if (!playeringame[displayplayer]) - continue; - - // Call ViewpointSwitch hooks here. - canSwitchView = LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], false); - if (canSwitchView == 1) // Set viewpoint to this player - break; - else if (canSwitchView == 2) // Skip this player - continue; - - if (players[displayplayer].spectator) - continue; - - if (G_GametypeHasTeams()) - { - if (players[consoleplayer].ctfteam - && players[displayplayer].ctfteam != players[consoleplayer].ctfteam) - continue; - } - else if (gametype == GT_HIDEANDSEEK) - { - if (players[consoleplayer].pflags & PF_TAGIT) - continue; - } - // Other Tag-based gametypes? - else if (G_TagGametype()) - { - if (!players[consoleplayer].spectator - && (players[consoleplayer].pflags & PF_TAGIT) != (players[displayplayer].pflags & PF_TAGIT)) - continue; - } - else if (G_GametypeHasSpectators() && G_RingSlingerGametype()) - { - if (!players[consoleplayer].spectator) - continue; - } - - break; - } while (displayplayer != consoleplayer); - - // change statusbar also if playing back demo - if (singledemo) - ST_changeDemoView(); - - // tell who's the view - CONS_Printf(M_GetText("Viewpoint: %s\n"), player_names[displayplayer]); - - return true; - } - } - - // update keys current state - G_MapEventsToControls(ev); ->>>>>>> srb2/next // change statusbar also if playing back demo if (demo.quitafterplaying) @@ -3053,7 +1537,6 @@ boolean G_Responder(event_t *ev) if (gamestate == GS_LEVEL && ev->type == ev_keydown && multiplayer && demo.playback && !demo.freecam) { -<<<<<<< HEAD if (ev->data1 == gamecontrolbis[gc_viewpoint][0] || ev->data1 == gamecontrolbis[gc_viewpoint][1]) { G_AdjustView(2, 1, true); @@ -3110,23 +1593,6 @@ boolean G_Responder(event_t *ev) switch (ev->type) { - case ev_keydown: - if (ev->data1 == gamecontrol[gc_pause][0] - || ev->data1 == gamecontrol[gc_pause][1] - || ev->data1 == KEY_PAUSE) - { - if (!pausedelay) - { - // don't let busy scripts prevent pausing - pausedelay = NEWTICRATE/7; - - // command will handle all the checks for us - COM_ImmedExecute("pause"); - return true; - } - else - pausedelay = NEWTICRATE/7; -======= case ev_keydown: if (ev->data1 == gamecontrol[gc_pause][0] || ev->data1 == gamecontrol[gc_pause][1] @@ -3158,78 +1624,18 @@ boolean G_Responder(event_t *ev) return true; } } ->>>>>>> srb2/next } - if (ev->data1 == gamecontrol[gc_camtoggle][0] - || ev->data1 == gamecontrol[gc_camtoggle][1]) + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) { - if (!camtoggledelay) + if (ev->data1 == gamecontrol[i][gc_camtoggle][0] + || ev->data1 == gamecontrol[i][gc_camtoggle][1]) { - camtoggledelay = NEWTICRATE / 7; - CV_SetValue(&cv_chasecam, cv_chasecam.value ? 0 : 1); - } - } - if (ev->data1 == gamecontrolbis[gc_camtoggle][0] - || ev->data1 == gamecontrolbis[gc_camtoggle][1]) - { - if (!camtoggledelay2) - { - camtoggledelay2 = NEWTICRATE / 7; - CV_SetValue(&cv_chasecam2, cv_chasecam2.value ? 0 : 1); - } - } - if (ev->data1 == gamecontrol3[gc_camtoggle][0] - || ev->data1 == gamecontrol3[gc_camtoggle][1]) - { - if (!camtoggledelay3) - { - camtoggledelay3 = NEWTICRATE / 7; - CV_SetValue(&cv_chasecam3, cv_chasecam3.value ? 0 : 1); - } - } - if (ev->data1 == gamecontrol4[gc_camtoggle][0] - || ev->data1 == gamecontrol4[gc_camtoggle][1]) - { - if (!camtoggledelay4) - { - camtoggledelay4 = NEWTICRATE / 7; - CV_SetValue(&cv_chasecam4, cv_chasecam4.value ? 0 : 1); - } - } - if (ev->data1 == gamecontrol[gc_spectate][0] - || ev->data1 == gamecontrol[gc_spectate][1]) - { - if (!spectatedelay) - { - spectatedelay = NEWTICRATE / 7; - COM_ImmedExecute("changeteam spectator"); - } - } - if (ev->data1 == gamecontrolbis[gc_spectate][0] - || ev->data1 == gamecontrolbis[gc_spectate][1]) - { - if (!spectatedelay2) - { - spectatedelay2 = NEWTICRATE / 7; - COM_ImmedExecute("changeteam2 spectator"); - } - } - if (ev->data1 == gamecontrol3[gc_spectate][0] - || ev->data1 == gamecontrol3[gc_spectate][1]) - { - if (!spectatedelay3) - { - spectatedelay3 = NEWTICRATE / 7; - COM_ImmedExecute("changeteam3 spectator"); - } - } - if (ev->data1 == gamecontrol4[gc_spectate][0] - || ev->data1 == gamecontrol4[gc_spectate][1]) - { - if (!spectatedelay4) - { - spectatedelay4 = NEWTICRATE / 7; - COM_ImmedExecute("changeteam4 spectator"); + if (!camtoggledelay[i]) + { + camtoggledelay[i] = NEWTICRATE / 7; + CV_SetValue(&cv_chasecam[i], cv_chasecam[i].value ? 0 : 1); + } } } @@ -3518,12 +1924,6 @@ void G_Ticker(boolean run) UINT32 i; INT32 buf; ticcmd_t *cmd; - UINT32 ra_timeskip = (modeattacking && !demo.playback && leveltime < starttime - TICRATE*4) ? 0 : (starttime - TICRATE*4 - 1); - // starttime - TICRATE*4 is where we want RA to start when we PLAY IT, so we will loop the main thinker on RA start to get it to this point, - // the reason this is done is to ensure that ghosts won't look out of synch with other map elements (objects, moving platforms...) - // when we REPLAY, don't skip, let the camera spin, do its thing etc~ - - // also the -1 is to ensure that the thinker runs in the loop below. P_MapStart(); // do player reborns if needed @@ -3534,20 +1934,12 @@ void G_Ticker(boolean run) { G_ClearRetryFlag(); -<<<<<<< HEAD - // Costs a life to retry ... unless the player in question is dead already. - /*if (G_GametypeUsesLives() && players[consoleplayer].playerstate == PST_LIVE) - players[consoleplayer].lives -= 1; - - G_DoReborn(consoleplayer);*/ - - D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), true, 1, false, false); -======= if (modeattacking) { pausedelay = INT32_MIN; M_ModeAttackRetry(0); } + /* else { // Costs a life to retry ... unless the player in question is dead already. @@ -3556,7 +1948,7 @@ void G_Ticker(boolean run) G_DoReborn(consoleplayer); } ->>>>>>> srb2/next + */ } for (i = 0; i < MAXPLAYERS; i++) @@ -3606,18 +1998,6 @@ void G_Ticker(boolean run) switch (gamestate) { case GS_LEVEL: -<<<<<<< HEAD - - for (; ra_timeskip < starttime - TICRATE*4; ra_timeskip++) // this looks weird but this is done to not break compability with older demos for now. - { - if (demo.title) - F_TitleDemoTicker(); - P_Ticker(run); // tic the game - ST_Ticker(); - AM_Ticker(); - HU_Ticker(); - } -======= if (titledemo) F_TitleDemoTicker(); P_Ticker(run); // tic the game @@ -3625,7 +2005,6 @@ void G_Ticker(boolean run) F_TextPromptTicker(); AM_Ticker(); HU_Ticker(); ->>>>>>> srb2/next break; case GS_INTERMISSION: @@ -3684,15 +2063,10 @@ void G_Ticker(boolean run) break; case GS_TITLESCREEN: -<<<<<<< HEAD -======= - if (titlemapinaction) P_Ticker(run); // then intentionally fall through - /* FALLTHRU */ - case GS_WAITINGPLAYERS: - F_MenuPresTicker(run); ->>>>>>> srb2/next + if (titlemapinaction) P_Ticker(run); F_TitleScreenTicker(run); break; + case GS_WAITINGPLAYERS: if (netgame) F_WaitingPlayersTicker(); @@ -3706,15 +2080,13 @@ void G_Ticker(boolean run) if (run) { -<<<<<<< HEAD if (G_GametypeHasSpectators() && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING // definitely good || gamestate == GS_WAITINGPLAYERS)) // definitely a problem if we don't do it at all in this gamestate, but might need more protection? + { K_CheckSpectateStatus(); + } - if (pausedelay) - pausedelay--; -======= if (pausedelay && pausedelay != INT32_MIN) { if (pausedelay > 0) @@ -3722,25 +2094,12 @@ void G_Ticker(boolean run) else pausedelay++; } ->>>>>>> srb2/next - if (camtoggledelay) - camtoggledelay--; - if (camtoggledelay2) - camtoggledelay2--; - if (camtoggledelay3) - camtoggledelay3--; - if (camtoggledelay4) - camtoggledelay4--; - - if (spectatedelay) - spectatedelay--; - if (spectatedelay2) - spectatedelay2--; - if (spectatedelay3) - spectatedelay3--; - if (spectatedelay4) - spectatedelay4--; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (camtoggledelay[i]) + camtoggledelay[i]--; + } } } @@ -3798,47 +2157,24 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) INT32 score, marescore; INT32 lives; INT32 continues; -<<<<<<< HEAD - // SRB2kart + UINT8 kartspeed; UINT8 kartweight; - // INT32 charflags; - INT32 pflags; -======= - fixed_t camerascale; - fixed_t shieldscale; - UINT8 charability; - UINT8 charability2; - fixed_t normalspeed; - fixed_t runspeed; - UINT8 thrustfactor; - UINT8 accelstart; - UINT8 acceleration; - INT32 charflags; - INT32 pflags; - UINT32 thokitem; - UINT32 spinitem; - UINT32 revitem; UINT32 followitem; - fixed_t actionspd; - fixed_t mindash; - fixed_t maxdash; ->>>>>>> srb2/next + + INT32 pflags; + INT32 ctfteam; + INT32 starposttime; INT16 starpostx; INT16 starposty; INT16 starpostz; INT32 starpostnum; INT32 starpostangle; -<<<<<<< HEAD -======= fixed_t starpostscale; - fixed_t jumpfactor; - fixed_t height; - fixed_t spinheight; ->>>>>>> srb2/next + INT32 exiting; INT16 numboxes; INT16 totalring; @@ -3847,18 +2183,15 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT16 skincolor; INT32 skin; UINT32 availabilities; + tic_t jointime; -<<<<<<< HEAD + tic_t quittime; + UINT8 splitscreenindex; boolean spectator; boolean bot; UINT8 botdifficulty; -======= - tic_t quittime; - boolean spectator; - boolean outofcoop; - INT16 bot; ->>>>>>> srb2/next + SINT8 pity; INT16 rings; INT16 spheres; @@ -3882,20 +2215,17 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) continues = players[player].continues; ctfteam = players[player].ctfteam; exiting = players[player].exiting; + jointime = players[player].jointime; -<<<<<<< HEAD + quittime = players[player].quittime; + splitscreenindex = players[player].splitscreenindex; spectator = players[player].spectator; - pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_WANTSTOJOIN)); -======= - quittime = players[player].quittime; - spectator = players[player].spectator; - outofcoop = players[player].outofcoop; - pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER)); + + pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_GAMETYPEOVER)); if (!betweenmaps) pflags |= (players[player].pflags & PF_FINISHED); ->>>>>>> srb2/next // As long as we're not in multiplayer, carry over cheatcodes from map to map if (!(netgame || multiplayer)) @@ -3907,23 +2237,13 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) skincolor = players[player].skincolor; skin = players[player].skin; -<<<<<<< HEAD + // SRB2kart kartspeed = players[player].kartspeed; kartweight = players[player].kartweight; - // -======= + availabilities = players[player].availabilities; - camerascale = players[player].camerascale; - shieldscale = players[player].shieldscale; - charability = players[player].charability; - charability2 = players[player].charability2; - normalspeed = players[player].normalspeed; - runspeed = players[player].runspeed; - thrustfactor = players[player].thrustfactor; - accelstart = players[player].accelstart; - acceleration = players[player].acceleration; ->>>>>>> srb2/next + charflags = players[player].charflags; starposttime = players[player].starposttime; @@ -3933,29 +2253,17 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) starpostnum = players[player].starpostnum; respawnflip = players[player].kartstuff[k_starpostflip]; //SRB2KART starpostangle = players[player].starpostangle; -<<<<<<< HEAD -======= starpostscale = players[player].starpostscale; - jumpfactor = players[player].jumpfactor; - height = players[player].height; - spinheight = players[player].spinheight; - thokitem = players[player].thokitem; - spinitem = players[player].spinitem; - revitem = players[player].revitem; + followitem = players[player].followitem; - actionspd = players[player].actionspd; - mindash = players[player].mindash; - maxdash = players[player].maxdash; ->>>>>>> srb2/next mare = players[player].mare; bot = players[player].bot; botdifficulty = players[player].botvars.difficulty; pity = players[player].pity; -<<<<<<< HEAD // SRB2kart - if (leveltime <= starttime) + if (betweenmaps || leveltime < starttime) { itemroulette = 0; roulettetype = 0; @@ -3993,17 +2301,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) rings = players[player].kartstuff[k_rings]; comebackpoints = players[player].kartstuff[k_comebackpoints]; wanted = players[player].kartstuff[k_wanted]; -======= - if (betweenmaps || !G_IsSpecialStage(gamemap)) - { - rings = (ultimatemode ? 0 : mapheaderinfo[gamemap-1]->startrings); - spheres = 0; - } - else - { - rings = players[player].rings; - spheres = players[player].spheres; ->>>>>>> srb2/next } p = &players[player]; @@ -4016,43 +2313,20 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->pflags = pflags; p->ctfteam = ctfteam; p->jointime = jointime; -<<<<<<< HEAD - p->splitscreenindex = splitscreenindex; -======= p->quittime = quittime; ->>>>>>> srb2/next + p->splitscreenindex = splitscreenindex; p->spectator = spectator; p->outofcoop = outofcoop; // save player config truth reborn p->skincolor = skincolor; p->skin = skin; -<<<<<<< HEAD - // SRB2kart p->kartspeed = kartspeed; p->kartweight = kartweight; // p->charflags = charflags; -======= p->availabilities = availabilities; - p->camerascale = camerascale; - p->shieldscale = shieldscale; - p->charability = charability; - p->charability2 = charability2; - p->normalspeed = normalspeed; - p->runspeed = runspeed; - p->thrustfactor = thrustfactor; - p->accelstart = accelstart; - p->acceleration = acceleration; - p->charflags = charflags; - p->thokitem = thokitem; - p->spinitem = spinitem; - p->revitem = revitem; p->followitem = followitem; - p->actionspd = actionspd; - p->mindash = mindash; - p->maxdash = maxdash; ->>>>>>> srb2/next p->starposttime = starposttime; p->starpostx = starpostx; @@ -4060,13 +2334,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->starpostz = starpostz; p->starpostnum = starpostnum; p->starpostangle = starpostangle; -<<<<<<< HEAD -======= p->starpostscale = starpostscale; - p->jumpfactor = jumpfactor; - p->height = height; - p->spinheight = spinheight; ->>>>>>> srb2/next + p->exiting = exiting; p->numboxes = numboxes; @@ -4103,13 +2372,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->playerstate = PST_LIVE; p->panim = PA_IDLE; // standing animation -<<<<<<< HEAD - if ((netgame || multiplayer) && !p->spectator) - p->powers[pw_flashing] = K_GetKartFlashing(p)-1; // Babysitting deterrent -======= - //if ((netgame || multiplayer) && !p->spectator) -- moved into P_SpawnPlayer to account for forced changes there - //p->powers[pw_flashing] = flashingtics-1; // Babysitting deterrent - // Check to make sure their color didn't change somehow... if (G_GametypeHasTeams()) { @@ -4131,7 +2393,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) if (betweenmaps) return; ->>>>>>> srb2/next if (p-players == consoleplayer) { @@ -4144,14 +2405,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) mapmusresume = 0; songcredit = true; } -<<<<<<< HEAD -======= // This is in S_Start, but this was not here previously. // if (RESETMUSIC) // S_StopMusic(); S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); ->>>>>>> srb2/next } P_RestoreMusic(p); @@ -4164,51 +2422,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) if (gametype == GT_COOP) P_FindEmerald(); // scan for emeralds to hunt for - -<<<<<<< HEAD - // Reset Nights score and max link to 0 on death - p->maxlink = 0; - -======= ->>>>>>> srb2/next - // If NiGHTS, find lowest mare to start with. - p->mare = 0; /*P_FindLowestMare(); - - CONS_Debug(DBG_NIGHTS, M_GetText("Current mare is %d\n"), p->mare); - - if (p->mare == 255) -<<<<<<< HEAD - p->mare = 0;*/ - - // Check to make sure their color didn't change somehow... - /*if (G_GametypeHasTeams()) - { - if (p->ctfteam == 1 && p->skincolor != skincolor_redteam) - { - if (p == &players[consoleplayer]) - CV_SetValue(&cv_playercolor, skincolor_redteam); - else if (p == &players[displayplayers[1]]) - CV_SetValue(&cv_playercolor2, skincolor_redteam); - else if (p == &players[displayplayers[2]]) - CV_SetValue(&cv_playercolor3, skincolor_redteam); - else if (p == &players[displayplayers[3]]) - CV_SetValue(&cv_playercolor4, skincolor_redteam); - } - else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam) - { - if (p == &players[consoleplayer]) - CV_SetValue(&cv_playercolor, skincolor_blueteam); - else if (p == &players[displayplayers[1]]) - CV_SetValue(&cv_playercolor2, skincolor_blueteam); - else if (p == &players[displayplayers[2]]) - CV_SetValue(&cv_playercolor3, skincolor_blueteam); - else if (p == &players[displayplayers[3]]) - CV_SetValue(&cv_playercolor4, skincolor_blueteam); - } - }*/ -======= - p->mare = 0; ->>>>>>> srb2/next } // @@ -4272,93 +2485,18 @@ void G_MovePlayerToSpawnOrStarpost(INT32 playernum) { if (players[playernum].starposttime) P_MovePlayerToStarpost(playernum); -<<<<<<< HEAD -#ifdef HAVE_BLUA - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) -#endif - return; - } - - // -- Record Attack -- - if (modeattacking || battlecapsules) - spawnpoint = playerstarts[0]; - - // -- CTF -- - // Order: CTF->DM->Coop - else if (gametype == GT_CTF && players[playernum].ctfteam) - { - if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start - && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start - spawnpoint = G_FindRaceStart(playernum); // fallback - } - - // -- DM/Tag/CTF-spectator/etc -- - // Order: DM->CTF->Coop - else if (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF - || ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && !(players[playernum].pflags & PF_TAGIT))) - { - if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start - && !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start - spawnpoint = G_FindRaceStart(playernum); // fallback - } - - // -- Other game modes -- - // Order: Coop->DM->CTF - else - { - if (!(spawnpoint = G_FindRaceStart(playernum)) // find a Race start - && !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start - spawnpoint = G_FindCTFStart(playernum); // fallback - } - - //No spawns found. ANYWHERE. - if (!spawnpoint) - { - if (nummapthings) - { - if (playernum == consoleplayer - || (splitscreen && playernum == g_localplayers[1]) - || (splitscreen > 1 && playernum == g_localplayers[2]) - || (splitscreen > 2 && playernum == g_localplayers[3])) - CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the first mapthing!\n")); - spawnpoint = &mapthings[0]; - } - else - { - if (playernum == consoleplayer - || (splitscreen && playernum == g_localplayers[1]) - || (splitscreen > 1 && playernum == g_localplayers[2]) - || (splitscreen > 2 && playernum == g_localplayers[3])) - CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the origin!\n")); - //P_MovePlayerToSpawn handles this fine if the spawnpoint is NULL. - } - } - P_MovePlayerToSpawn(playernum, spawnpoint); - -#ifdef HAVE_BLUA - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) -#endif - -======= else P_MovePlayerToSpawn(playernum, G_FindMapStart(playernum)); ->>>>>>> srb2/next } mapthing_t *G_FindCTFStart(INT32 playernum) { + const boolean doprints = P_IsLocalPlayer(&players[playernum]); INT32 i,j; if (!numredctfstarts && !numbluectfstarts) //why even bother, eh? { -<<<<<<< HEAD - if (playernum == consoleplayer - || (splitscreen && playernum == displayplayers[1]) - || (splitscreen > 1 && playernum == displayplayers[2]) - || (splitscreen > 2 && playernum == displayplayers[3])) -======= - if ((gametyperules & GTR_TEAMFLAGS) && (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))) ->>>>>>> srb2/next + if ((gametyperules & GTR_TEAMFLAGS) && doprints)) CONS_Alert(CONS_WARNING, M_GetText("No CTF starts in this map!\n")); return NULL; } @@ -4367,10 +2505,7 @@ mapthing_t *G_FindCTFStart(INT32 playernum) { if (!numredctfstarts) { - if (playernum == consoleplayer - || (splitscreen && playernum == displayplayers[1]) - || (splitscreen > 1 && playernum == displayplayers[2]) - || (splitscreen > 2 && playernum == displayplayers[3])) + if (doprints) CONS_Alert(CONS_WARNING, M_GetText("No Red Team starts in this map!\n")); return NULL; } @@ -4382,10 +2517,7 @@ mapthing_t *G_FindCTFStart(INT32 playernum) return redctfstarts[i]; } - if (playernum == consoleplayer - || (splitscreen && playernum == displayplayers[1]) - || (splitscreen > 1 && playernum == displayplayers[2]) - || (splitscreen > 2 && playernum == displayplayers[3])) + if (doprints) CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Red Team starts!\n")); return NULL; } @@ -4393,10 +2525,7 @@ mapthing_t *G_FindCTFStart(INT32 playernum) { if (!numbluectfstarts) { - if (playernum == consoleplayer - || (splitscreen && playernum == displayplayers[1]) - || (splitscreen > 1 && playernum == displayplayers[2]) - || (splitscreen > 2 && playernum == displayplayers[3])) + if (doprints) CONS_Alert(CONS_WARNING, M_GetText("No Blue Team starts in this map!\n")); return NULL; } @@ -4407,10 +2536,7 @@ mapthing_t *G_FindCTFStart(INT32 playernum) if (G_CheckSpot(playernum, bluectfstarts[i])) return bluectfstarts[i]; } - if (playernum == consoleplayer - || (splitscreen && playernum == displayplayers[1]) - || (splitscreen > 1 && playernum == displayplayers[2]) - || (splitscreen > 2 && playernum == displayplayers[3])) + if (doprints) CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Blue Team starts!\n")); return NULL; } @@ -4420,6 +2546,7 @@ mapthing_t *G_FindCTFStart(INT32 playernum) mapthing_t *G_FindMatchStart(INT32 playernum) { + const boolean doprints = P_IsLocalPlayer(&players[playernum]); INT32 i, j; if (numdmstarts) @@ -4430,24 +2557,20 @@ mapthing_t *G_FindMatchStart(INT32 playernum) if (G_CheckSpot(playernum, deathmatchstarts[i])) return deathmatchstarts[i]; } - if (playernum == consoleplayer - || (splitscreen && playernum == g_localplayers[1]) - || (splitscreen > 1 && playernum == g_localplayers[2]) - || (splitscreen > 2 && playernum == g_localplayers[3])) + if (doprints) CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Deathmatch starts!\n")); return NULL; } - if (playernum == consoleplayer - || (splitscreen && playernum == g_localplayers[1]) - || (splitscreen > 1 && playernum == g_localplayers[2]) - || (splitscreen > 2 && playernum == g_localplayers[3])) + if (doprints) CONS_Alert(CONS_WARNING, M_GetText("No Deathmatch starts in this map!\n")); return NULL; } mapthing_t *G_FindRaceStart(INT32 playernum) { + const boolean doprints = P_IsLocalPlayer(&players[playernum]); + if (numcoopstarts) { UINT8 i; @@ -4464,7 +2587,6 @@ mapthing_t *G_FindRaceStart(INT32 playernum) if (i == playernum) continue; -<<<<<<< HEAD if (players[i].score < players[playernum].score) { UINT8 j; @@ -4529,33 +2651,19 @@ mapthing_t *G_FindRaceStart(INT32 playernum) // Just spawn there. //return playerstarts[0]; - if (playernum == consoleplayer - || (splitscreen && playernum == g_localplayers[1]) - || (splitscreen > 1 && playernum == g_localplayers[2]) - || (splitscreen > 2 && playernum == g_localplayers[3])) + if (doprints) CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Race starts!\n")); return NULL; } - if (playernum == consoleplayer - || (splitscreen && playernum == g_localplayers[1]) - || (splitscreen > 1 && playernum == g_localplayers[2]) - || (splitscreen > 2 && playernum == g_localplayers[3])) + if (doprints) CONS_Alert(CONS_WARNING, M_GetText("No Race starts in this map!\n")); return NULL; } -// Go back through all the projectiles and remove all references to the old -// player mobj, replacing them with the new one. -void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo) -{ - thinker_t *th; - mobj_t *mo2; -======= mapthing_t *G_FindMapStart(INT32 playernum) { mapthing_t *spawnpoint; ->>>>>>> srb2/next if (!playeringame[playernum]) return NULL; @@ -4646,185 +2754,10 @@ void G_DoReborn(INT32 playernum) // Make sure objectplace is OFF when you first start the level! OP_ResetObjectplace(); -<<<<<<< HEAD -======= - if (player->bot && playernum != consoleplayer) - { // Bots respawn next to their master. - mobj_t *oldmo = NULL; - - // first dissasociate the corpse - if (player->mo) - { - oldmo = player->mo; - // Don't leave your carcass stuck 10-billion feet in the ground! - P_RemoveMobj(player->mo); - } - - B_RespawnBot(playernum); - if (oldmo) - G_ChangePlayerReferences(oldmo, players[playernum].mo); - - return; - } - - if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP)) - resetlevel = true; - else if ((G_GametypeUsesCoopLives() || G_GametypeUsesCoopStarposts()) && (netgame || multiplayer) && !G_IsSpecialStage(gamemap)) - { - boolean notgameover = true; - - if (G_GametypeUsesCoopLives() && (cv_cooplives.value != 0 && player->lives <= 0)) // consider game over first - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - if (players[i].exiting || players[i].lives > 0) - break; - } - - if (i == MAXPLAYERS) - { - notgameover = false; - if (!countdown2) - { - // They're dead, Jim. - //nextmapoverride = spstage_start; - nextmapoverride = gamemap; - countdown2 = TICRATE; - skipstats = 2; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - players[i].score = 0; - } - - //emeralds = 0; - tokenbits = 0; - tokenlist = 0; - token = 0; - } - } - } - - if (G_GametypeUsesCoopStarposts() && (notgameover && cv_coopstarposts.value == 2)) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - if (players[i].playerstate != PST_DEAD && !players[i].spectator && players[i].mo && players[i].mo->health) - break; - } - if (i == MAXPLAYERS) - resetlevel = true; - } - } - - if (resetlevel) - { - // reload the level from scratch - if (countdowntimeup) - { - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - players[i].starpostscale = 0; - players[i].starpostangle = 0; - players[i].starposttime = 0; - players[i].starpostx = 0; - players[i].starposty = 0; - players[i].starpostz = 0; - players[i].starpostnum = 0; - } - } - if (!countdowntimeup && (mapheaderinfo[gamemap-1]->levelflags & LF_NORELOAD)) - { - P_RespawnThings(); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - players[i].playerstate = PST_REBORN; - P_ClearStarPost(players[i].starpostnum); - } - - // Do a wipe - wipegamestate = -1; - wipestyleflags = WSF_CROSSFADE; - - if (camera.chase) - P_ResetCamera(&players[displayplayer], &camera); - if (camera2.chase && splitscreen) - P_ResetCamera(&players[secondarydisplayplayer], &camera2); - - // clear cmd building stuff - memset(gamekeydown, 0, sizeof (gamekeydown)); - for (i = 0; i < JOYAXISSET; i++) - { - joyxmove[i] = joyymove[i] = 0; - joy2xmove[i] = joy2ymove[i] = 0; - } - mousex = mousey = 0; - mouse2x = mouse2y = 0; - - // clear hud messages remains (usually from game startup) - CON_ClearHUD(); - - // Starpost support - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - G_SpawnPlayer(i); - } - - // restore time in netgame (see also p_setup.c) - if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2) - { - // is this a hack? maybe - tic_t maxstarposttime = 0; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].starposttime > maxstarposttime) - maxstarposttime = players[i].starposttime; - } - leveltime = maxstarposttime; - } - } - else - { - LUAh_MapChange(gamemap); - titlecardforreload = true; - G_DoLoadLevel(true); - titlecardforreload = false; - if (metalrecording) - G_BeginMetal(); - return; - } - } - else ->>>>>>> srb2/next { // respawn at the start mobj_t *oldmo = NULL; -<<<<<<< HEAD - // Now only respawn at the start if you haven't crossed it at all - if (player->laps) // SRB2kart - starpost = true; -======= - // Not resetting map, so return to level music - if (!countdown2 - && player->lives <= 0 - && cv_cooplives.value == 1) // not allowed for life steal because no way to come back from zero group lives without addons, which should call this anyways - P_RestoreMultiMusic(player); ->>>>>>> srb2/next - // first dissasociate the corpse if (player->mo) { @@ -4862,9 +2795,6 @@ void G_AddPlayer(INT32 playernum) if (!players[i].exiting) notexiting++; - if (!(cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (p->starpostnum < players[i].starpostnum))) - continue; - p->starpostscale = players[i].starpostscale; p->starposttime = players[i].starposttime; p->starpostx = players[i].starpostx; @@ -4877,13 +2807,10 @@ void G_AddPlayer(INT32 playernum) p->playerstate = PST_REBORN; -<<<<<<< HEAD demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN; // Set everything -======= - p->height = mobjinfo[MT_PLAYER].height; - if (G_GametypeUsesLives() || ((netgame || multiplayer) && gametype == GT_COOP)) - p->lives = cv_startinglives.value; + if (G_GametypeUsesLives()) + p->lives = 3; if ((countplayers && !notexiting) || G_IsSpecialStage(gamemap)) P_DoPlayerExit(p); @@ -4914,7 +2841,6 @@ boolean G_EnoughPlayersFinished(void) return exiting * 4 / total >= numneeded; else return false; ->>>>>>> srb2/next } void G_ExitLevel(void) @@ -4932,13 +2858,8 @@ void G_ExitLevel(void) CV_SetValue(&cv_teamscramble, cv_scrambleonchange.value); } -<<<<<<< HEAD - if (netgame || multiplayer) - CON_LogMessage(M_GetText("The round has ended.\n")); -======= if (!(gametyperules & GTR_CAMPAIGN)) - CONS_Printf(M_GetText("The round has ended.\n")); ->>>>>>> srb2/next + CON_LogMessage(M_GetText("The round has ended.\n")); // Remove CEcho text on round end. HU_ClearCEcho(); @@ -4959,60 +2880,23 @@ void G_ExitLevel(void) const char *Gametype_Names[NUMGAMETYPES] = { "Race", // GT_RACE - "Battle" // GT_MATCH - - /*"Co-op", // GT_COOP - "Competition", // GT_COMPETITION - "Team Match", // GT_TEAMMATCH - "Tag", // GT_TAG -<<<<<<< HEAD - "Hide and Seek", // GT_HIDEANDSEEK - "CTF" // GT_CTF*/ -======= - "Hide & Seek", // GT_HIDEANDSEEK - - "CTF" // GT_CTF ->>>>>>> srb2/next + "Battle" // GT_BATTLE }; // For dehacked const char *Gametype_ConstantNames[NUMGAMETYPES] = { - "GT_COOP", // GT_COOP - "GT_COMPETITION", // GT_COMPETITION "GT_RACE", // GT_RACE - - "GT_MATCH", // GT_MATCH - "GT_TEAMMATCH", // GT_TEAMMATCH - - "GT_TAG", // GT_TAG - "GT_HIDEANDSEEK", // GT_HIDEANDSEEK - - "GT_CTF" // GT_CTF + "GT_BATTLE" // GT_BATTLE }; // Gametype rules UINT32 gametypedefaultrules[NUMGAMETYPES] = { - // Co-op - GTR_CAMPAIGN|GTR_LIVES|GTR_FRIENDLY|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES|GTR_CUTSCENES, - // Competition - GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT, // Race GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT, - - // Match - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY, - // Team Match - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD, - - // Tag - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY, - // Hide and Seek - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY, - - // CTF - GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD, + // Battle + GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY }; // @@ -5262,22 +3146,18 @@ INT32 G_GetGametypeByName(const char *gametypestr) // boolean G_IsSpecialStage(INT32 mapnum) { -<<<<<<< HEAD -#if 0 - return (gametype == GT_COOP && modeattacking != ATTACKING_RECORD && mapnum >= sstage_start && mapnum <= sstage_end); -#else +#if 1 (void)mapnum; -======= +#else if (gametype != GT_COOP || modeattacking == ATTACKING_RECORD) return false; if (mapnum >= sstage_start && mapnum <= sstage_end) return true; if (mapnum >= smpstage_start && mapnum <= smpstage_end) return true; - ->>>>>>> srb2/next - return false; #endif + + return false; } // @@ -5288,25 +3168,17 @@ boolean G_IsSpecialStage(INT32 mapnum) // boolean G_GametypeUsesLives(void) { -<<<<<<< HEAD // SRB2kart NEEDS no lives #if 0 - // Coop, Competitive - if ((gametype == GT_COOP || gametype == GT_COMPETITION) - && !modeattacking // No lives in Time Attack - //&& !G_IsSpecialStage(gamemap) -======= // Coop, Competitive if ((gametyperules & GTR_LIVES) && !(modeattacking || metalrecording) // No lives in Time Attack && !G_IsSpecialStage(gamemap) ->>>>>>> srb2/next && !(maptol & TOL_NIGHTS)) // No lives in NiGHTS return true; - return false; -#else - return false; #endif + + return false; } // @@ -5350,25 +3222,14 @@ boolean G_GametypeHasTeams(void) // boolean G_GametypeHasSpectators(void) { -<<<<<<< HEAD // SRB2Kart: We don't have any exceptions to not being able to spectate yet. Maybe when SP & bots roll around. #if 0 - return (gametype != GT_COOP && gametype != GT_COMPETITION && gametype != GT_RACE); + return (gametyperules & GTR_SPECTATORS); #else return (netgame || (multiplayer && demo.playback)); //true #endif } -// -// G_BattleGametype -// -// Returns true in Battle gamemodes, previously was G_RingSlingerGametype. -// -boolean G_BattleGametype(void) -{ - return (gametype == GT_MATCH); -} - // // G_SometimesGetDifferentGametype // @@ -5432,9 +3293,6 @@ INT16 G_SometimesGetDifferentGametype(void) if (gametype == GT_MATCH) return GT_RACE; return GT_MATCH; -======= - return (gametyperules & GTR_SPECTATORS); ->>>>>>> srb2/next } // @@ -5445,7 +3303,6 @@ INT16 G_SometimesGetDifferentGametype(void) // UINT8 G_GetGametypeColor(INT16 gt) { -<<<<<<< HEAD if (modeattacking // == ATTACKING_RECORD || gamestate == GS_TIMEATTACK) return orangemap[0]; @@ -5454,9 +3311,6 @@ UINT8 G_GetGametypeColor(INT16 gt) if (gt == GT_RACE) return skymap[0]; return 255; // FALLBACK -======= - return ((gametyperules & GTR_RINGSLINGER) || (cv_ringslinger.value)); ->>>>>>> srb2/next } // @@ -5466,11 +3320,17 @@ UINT8 G_GetGametypeColor(INT16 gt) // boolean G_RaceGametype(void) { -<<<<<<< HEAD return (gametype == GT_RACE); -======= - return (!(gametyperules & GTR_RINGSLINGER)); ->>>>>>> srb2/next +} + +// +// G_BattleGametype +// +// Returns true in Battle gamemodes, previously was G_RingslingerGametype. +// +boolean G_BattleGametype(void) +{ + return (gametype == GT_BATTLE); } // @@ -5502,24 +3362,10 @@ boolean G_CompetitionGametype(void) */ UINT32 G_TOLFlag(INT32 pgametype) { -<<<<<<< HEAD - if (!multiplayer) return TOL_SP; - if (pgametype == GT_COOP) return TOL_RACE; // SRB2kart - if (pgametype == GT_COMPETITION) return TOL_COMPETITION; - if (pgametype == GT_RACE) return TOL_RACE; - if (pgametype == GT_MATCH) return TOL_MATCH; - if (pgametype == GT_TEAMMATCH) return TOL_MATCH; - if (pgametype == GT_TAG) return TOL_TAG; - if (pgametype == GT_HIDEANDSEEK) return TOL_TAG; - if (pgametype == GT_CTF) return TOL_CTF; - - CONS_Alert(CONS_ERROR, M_GetText("Unknown gametype! %d\n"), pgametype); - return INT16_MAX; -======= if (!multiplayer) return TOL_SP; + return gametypetol[pgametype]; ->>>>>>> srb2/next } static INT32 TOLMaps(INT16 tolflags) @@ -5549,12 +3395,8 @@ static INT32 TOLMaps(INT16 tolflags) * has those flags. * \author Graue */ -<<<<<<< HEAD static INT16 *okmaps = NULL; -INT16 G_RandMap(INT16 tolflags, INT16 pprevmap, boolean ignorebuffer, UINT8 maphell, boolean callagainsoon, INT16 *extbuffer) -======= -static INT16 RandMap(UINT32 tolflags, INT16 pprevmap) ->>>>>>> srb2/next +INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, boolean ignorebuffer, UINT8 maphell, boolean callagainsoon, INT16 *extbuffer) { INT32 numokmaps = 0; INT16 ix, bufx; @@ -5695,13 +3537,7 @@ void G_AddMapToBuffer(INT16 map) // static void G_UpdateVisited(void) { -<<<<<<< HEAD - INT32 i, j = 0; - boolean gottoken = false; - SINT8 powertype = PWRLV_DISABLED; - - tokenlist = 0; // Reset the list -======= + boolean spec = G_IsSpecialStage(gamemap); // Update visitation flags? if ((!modifiedgame || savemoddata) // Not modified @@ -5744,14 +3580,15 @@ static void G_UpdateVisited(void) // static void G_DoCompleted(void) { - INT32 i; + INT32 i, j = 0; + boolean gottoken = false; + SINT8 powertype = PWRLV_DISABLED; boolean spec = G_IsSpecialStage(gamemap); tokenlist = 0; // Reset the list if (modeattacking && pausedelay) pausedelay = 0; ->>>>>>> srb2/next gameaction = ga_nothing; @@ -5803,11 +3640,7 @@ static void G_DoCompleted(void) // a map of the proper gametype -- skip levels that don't support // the current gametype. (Helps avoid playing boss levels in Race, // for instance). -<<<<<<< HEAD - if (!token && !G_IsSpecialStage(gamemap) && !modeattacking -======= - if (!token && !spec ->>>>>>> srb2/next + if (!token && !spec && !modeattacking && (nextmap >= 0 && nextmap < NUMMAPS)) { register INT16 cm = nextmap; @@ -5853,11 +3686,7 @@ static void G_DoCompleted(void) I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1); // wrap around in race -<<<<<<< HEAD - if (nextmap >= 1100-1 && nextmap <= 1102-1 && G_RaceGametype()) -======= if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN)) ->>>>>>> srb2/next nextmap = (INT16)(spstage_start-1); if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token))) @@ -5897,7 +3726,6 @@ static void G_DoCompleted(void) if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) P_AllocMapHeader(nextmap); -<<<<<<< HEAD // Set up power level gametype scrambles if (netgame && cv_kartusepwrlv.value) { @@ -5911,12 +3739,9 @@ static void G_DoCompleted(void) demointermission: - if (skipstats && !modeattacking) // Don't skip stats if we're in record attack -======= if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed)) { G_UpdateVisited(); ->>>>>>> srb2/next G_AfterIntermission(); } else @@ -5949,7 +3774,6 @@ void G_AfterIntermission(void) else D_StartTitle(); -<<<<<<< HEAD return; } else if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) @@ -5961,10 +3785,7 @@ void G_AfterIntermission(void) return; } - if (mapheaderinfo[gamemap-1]->cutscenenum) // Start a custom cutscene. -======= if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene. ->>>>>>> srb2/next F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); else { @@ -6267,57 +4088,21 @@ void G_LoadGameData(void) timesBeaten = READUINT32(save_p); timesBeatenWithEmeralds = READUINT32(save_p); - //timesBeatenUltimate = READUINT32(save_p); // Main records for (i = 0; i < NUMMAPS; ++i) { -<<<<<<< HEAD rectime = (tic_t)READUINT32(save_p); reclap = (tic_t)READUINT32(save_p); - //recscore = READUINT32(save_p); - //recrings = READUINT16(save_p); -======= - recscore = READUINT32(save_p); - rectime = (tic_t)READUINT32(save_p); - recrings = READUINT16(save_p); - save_p++; // compat ->>>>>>> srb2/next - - /*if (recrings > 10000 || recscore > MAXSCORE) - goto datacorrupt;*/ if (rectime || reclap) { G_AllocMainRecordData((INT16)i); mainrecords[i]->time = rectime; mainrecords[i]->lap = reclap; - //mainrecords[i]->score = recscore; - //mainrecords[i]->rings = recrings; } } - // Nights records - /*for (i = 0; i < NUMMAPS; ++i) - { - if ((recmares = READUINT8(save_p)) == 0) - continue; - - G_AllocNightsRecordData((INT16)i); - - for (curmare = 0; curmare < (recmares+1); ++curmare) - { - nightsrecords[i]->score[curmare] = READUINT32(save_p); - nightsrecords[i]->grade[curmare] = READUINT8(save_p); - nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p); - - if (nightsrecords[i]->grade[curmare] > GRADE_S) - goto datacorrupt; - } - - nightsrecords[i]->nummares = recmares; - }*/ - // done Z_Free(savebuffer); save_p = NULL; @@ -6468,64 +4253,6 @@ void G_SaveGameData(boolean force) #define VERSIONSIZE 16 -<<<<<<< HEAD -#ifdef SAVEGAMES_OTHERVERSIONS -static INT16 startonmapnum = 0; - -// -// User wants to load a savegame from a different version? -// -static void M_ForceLoadGameResponse(INT32 ch) -{ - if (ch != 'y' && ch != KEY_ENTER) - { - //refused - Z_Free(savebuffer); - save_p = savebuffer = NULL; - startonmapnum = 0; - M_SetupNextMenu(&SP_LoadDef); - return; - } - - // pick up where we left off. - save_p += VERSIONSIZE; - if (!P_LoadGame(startonmapnum)) - { - M_ClearMenus(true); // so ESC backs out to title - M_StartMessage(M_GetText("Savegame file corrupted\n\nPress ESC\n"), NULL, MM_NOTHING); - Command_ExitGame_f(); - Z_Free(savebuffer); - save_p = savebuffer = NULL; - startonmapnum = 0; - - // no cheating! - memset(&savedata, 0, sizeof(savedata)); - return; - } - - // done - Z_Free(savebuffer); - save_p = savebuffer = NULL; - startonmapnum = 0; - - //set cursaveslot to -1 so nothing gets saved. - cursaveslot = -1; - - displayplayers[0] = consoleplayer; - multiplayer = false; - splitscreen = 0; - SplitScreen_OnChange(); // not needed? - - if (setsizeneeded) - R_ExecuteSetViewSize(); - - M_ClearMenus(true); - CON_ToggleOff(); -} -#endif - -======= ->>>>>>> srb2/next // // G_InitFromSavegame // Can be called by the startup code or the menu task. @@ -6776,28 +4503,14 @@ cleanup: // void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, UINT8 ssplayers, boolean FLS) { -<<<<<<< HEAD INT32 i; - UINT8 color = 0; -======= - UINT16 color = skins[pickedchar].prefcolor; ->>>>>>> srb2/next + UINT16 color = SKINCOLOR_NONE; paused = false; if (demo.playback) COM_BufAddText("stopdemo\n"); -<<<<<<< HEAD - while (ghosts) - { - demoghost *next = ghosts->next; - Z_Free(ghosts); - ghosts = next; - } - ghosts = NULL; -======= G_FreeGhosts(); // TODO: do we actually need to do this? ->>>>>>> srb2/next for (i = 0; i < NUMMAPS+1; i++) randmapbuffer[i] = -1; @@ -6807,12 +4520,7 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar if (savedata.lives > 0) { -<<<<<<< HEAD color = savedata.skincolor; -======= - if ((botingame = ((botskin = savedata.botskin) != 0))) - botcolor = skins[botskin-1].prefcolor; ->>>>>>> srb2/next } else if (splitscreen != ssplayers) { @@ -6820,31 +4528,25 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar SplitScreen_OnChange(); } -<<<<<<< HEAD - if (!color && !modeattacking) - color = skins[pickedchar].prefcolor; -======= - color = skins[pickedchar].prefcolor; ->>>>>>> srb2/next SetPlayerSkinByNum(consoleplayer, pickedchar); CV_StealthSet(&cv_skin, skins[pickedchar].name); - if (color) + if (color != SKINCOLOR_NONE) + { CV_StealthSetValue(&cv_playercolor, color); + } if (mapname) + { D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pencoremode, true, 1, false, FLS); + } } // // This is the map command interpretation something like Command_Map_f // // called at: map cmd execution, doloadgame, doplaydemo -<<<<<<< HEAD -void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, boolean skipprecutscene) -======= -void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS) ->>>>>>> srb2/next +void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS) { INT32 i; @@ -6856,16 +4558,11 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean S_ResumeAudio(); } -<<<<<<< HEAD prevencoremode = ((gamestate == GS_TITLESCREEN) ? false : encoremode); encoremode = pencoremode; legitimateexit = false; // SRB2Kart comebackshowninfo = false; -======= - if (netgame || multiplayer) // Nice try, haxor. - pultmode = false; ->>>>>>> srb2/next if (!demo.playback && !netgame) // Netgame sets random seed elsewhere, demo playback sets seed just before us! P_SetRandSeed(M_RandomizedSeed()); // Use a more "Random" random seed @@ -6874,13 +4571,8 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean //if (resetplayer) { // Clear a bunch of variables -<<<<<<< HEAD - tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; - racecountdown = exitcountdown = mapreset = 0; -======= numgameovers = tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; - countdown = countdown2 = exitfadestarted = 0; ->>>>>>> srb2/next + racecountdown = exitcountdown = mapreset = exitfadestarted = 0; for (i = 0; i < MAXPLAYERS; i++) { @@ -6901,15 +4593,13 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean players[i].continues = (pultmode) ? 0 : 1; } -<<<<<<< HEAD players[i].xtralife = 0; #else players[i].lives = 1; // SRB2Kart #endif -======= + if (!((netgame || multiplayer) && (FLS))) players[i].score = 0; ->>>>>>> srb2/next // The latter two should clear by themselves, but just in case players[i].pflags &= ~(PF_TAGIT|PF_GAMETYPEOVER|PF_FULLSTASIS); @@ -6917,16 +4607,12 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean // Clear cheatcodes too, just in case. players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS); -<<<<<<< HEAD players[i].marescore = 0; if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart { players[i].score = 0; } -======= - players[i].xtralife = 0; ->>>>>>> srb2/next } // Reset unlockable triggers @@ -6967,9 +4653,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); else { -#ifdef HAVE_BLUA LUAh_MapChange(gamemap); -#endif G_DoLoadLevel(resetplayer); } @@ -6992,23 +4676,17 @@ char *G_BuildMapTitle(INT32 mapnum) { char *title = NULL; -<<<<<<< HEAD if (mapnum == 0) return Z_StrDup("Random"); -======= + if (!mapheaderinfo[mapnum-1]) P_AllocMapHeader(mapnum-1); ->>>>>>> srb2/next if (strcmp(mapheaderinfo[mapnum-1]->lvlttl, "")) { size_t len = 1; const char *zonetext = NULL; -<<<<<<< HEAD - const char *actnum = NULL; -======= const UINT8 actnum = mapheaderinfo[mapnum-1]->actnum; ->>>>>>> srb2/next len += strlen(mapheaderinfo[mapnum-1]->lvlttl); if (strlen(mapheaderinfo[mapnum-1]->zonttl) > 0) @@ -7037,87 +4715,9 @@ char *G_BuildMapTitle(INT32 mapnum) return title; } -<<<<<<< HEAD -// -// DEMO RECORDING -// - -#define DEMOVERSION 0x0003 -#define DEMOHEADER "\xF0" "KartReplay" "\x0F" - -#define DF_GHOST 0x01 // This demo contains ghost data too! -#define DF_TIMEATTACK 0x02 // This demo is from Time Attack and contains its final completion time & best lap! -#define DF_BREAKTHECAPSULES 0x04 // This demo is from Break the Capsules and contains its final completion time! -#define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? - -#ifdef HAVE_BLUA -#define DF_LUAVARS 0x20 // this demo contains extra lua vars; this is mostly used for backwards compability -#endif - -#define DF_ATTACKSHIFT 1 -#define DF_ENCORE 0x40 -#define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode! - -#define DEMO_SPECTATOR 0x40 - -// For demos -#define ZT_FWD 0x01 -#define ZT_SIDE 0x02 -#define ZT_ANGLE 0x04 -#define ZT_BUTTONS 0x08 -#define ZT_AIMING 0x10 -#define ZT_DRIFT 0x20 -#define ZT_LATENCY 0x40 -#define DEMOMARKER 0x80 // demoend - -UINT8 demo_extradata[MAXPLAYERS]; -UINT8 demo_writerng; // 0=no, 1=yes, 2=yes but on a timeout -static ticcmd_t oldcmd[MAXPLAYERS]; - -#define DW_END 0xFF // End of extradata block -#define DW_RNG 0xFE // Check RNG seed! - -#define DW_EXTRASTUFF 0xFE // Numbers below this are reserved for writing player slot data - -// Below consts are only used for demo extrainfo sections -#define DW_STANDING 0x00 - -// For Metal Sonic and time attack ghosts -#define GZT_XYZ 0x01 -#define GZT_MOMXY 0x02 -#define GZT_MOMZ 0x04 -#define GZT_ANGLE 0x08 -// Not used for Metal Sonic -#define GZT_SPRITE 0x10 // Animation frame -#define GZT_EXTRA 0x20 - -// GZT_EXTRA flags -#define EZT_THOK 0x01 // Spawned a thok object -#define EZT_SPIN 0x02 // Because one type of thok object apparently wasn't enough -#define EZT_REV 0x03 // And two types wasn't enough either yet -#define EZT_THOKMASK 0x03 -#define EZT_COLOR 0x04 // Changed color (Super transformation, Mario fireflowers/invulnerability, etc.) -#define EZT_FLIP 0x08 // Reversed gravity -#define EZT_SCALE 0x10 // Changed size -#define EZT_HIT 0x20 // Damaged a mobj -#define EZT_SPRITE 0x40 // Changed sprite set completely out of PLAY (NiGHTS, SOCs, whatever) -#define EZT_KART 0x80 // SRB2Kart: Changed current held item/quantity and bumpers for battle - -static mobj_t oldmetal, oldghost[MAXPLAYERS]; - -void G_SaveMetal(UINT8 **buffer) -{ - I_Assert(buffer != NULL && *buffer != NULL); - - WRITEUINT32(*buffer, metal_p - metalbuffer); -} - -void G_LoadMetal(UINT8 **buffer) -======= static void measurekeywords(mapsearchfreq_t *fr, struct searchdim **dimp, UINT8 *cuntp, const char *s, const char *q, boolean wanttable) ->>>>>>> srb2/next { char *qp; char *sp; @@ -7159,309 +4759,6 @@ static void writesimplefreq(mapsearchfreq_t *fr, INT32 *frc, INT32 G_FindMap(const char *mapname, char **foundmapnamep, mapsearchfreq_t **freqp, INT32 *freqcp) { -<<<<<<< HEAD - size_t i; - for (i = 0; i < n; i++) - { - dest[i].forwardmove = src[i].forwardmove; - dest[i].sidemove = src[i].sidemove; - dest[i].angleturn = SHORT(src[i].angleturn); - dest[i].aiming = (INT16)SHORT(src[i].aiming); - dest[i].buttons = (UINT16)SHORT(src[i].buttons); - dest[i].driftturn = (INT16)SHORT(src[i].driftturn); - dest[i].latency = (INT16)SHORT(src[i].latency); - } - return dest; -} - -// Finds a skin with the closest stats if the expected skin doesn't exist. -static INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight) -{ - INT32 i, closest_skin = 0; - UINT8 closest_stats = UINT8_MAX, stat_diff; - - for (i = 0; i < numskins; i++) - { - stat_diff = abs(skins[i].kartspeed - kartspeed) + abs(skins[i].kartweight - kartweight); - if (stat_diff < closest_stats) - { - closest_stats = stat_diff; - closest_skin = i; - } - } - - return closest_skin; -} - -static void FindClosestSkinForStats(UINT32 p, UINT8 kartspeed, UINT8 kartweight) -{ - INT32 closest_skin = GetSkinNumClosestToStats(kartspeed, kartweight); - - //CONS_Printf("Using %s instead...\n", skins[closest_skin].name); - SetPlayerSkinByNum(p, closest_skin); -} - -void G_ReadDemoExtraData(void) -{ - INT32 p, extradata, i; - char name[17]; - - if (leveltime > starttime) - { - rewind_t *rewind = CL_SaveRewindPoint(demo_p - demobuffer); - if (rewind) - { - memcpy(rewind->oldcmd, oldcmd, sizeof (oldcmd)); - memcpy(rewind->oldghost, oldghost, sizeof (oldghost)); - } - } - - memset(name, '\0', 17); - - p = READUINT8(demo_p); - - while (p < DW_EXTRASTUFF) - { - extradata = READUINT8(demo_p); - - if (extradata & DXD_RESPAWN) - { - if (players[p].mo) - { - // Is this how this should work..? - K_DoIngameRespawn(&players[p]); - } - } - if (extradata & DXD_SKIN) - { - UINT8 kartspeed, kartweight; - - // Skin - M_Memcpy(name, demo_p, 16); - demo_p += 16; - SetPlayerSkin(p, name); - - kartspeed = READUINT8(demo_p); - kartweight = READUINT8(demo_p); - - if (stricmp(skins[players[p].skin].name, name) != 0) - FindClosestSkinForStats(p, kartspeed, kartweight); - - players[p].kartspeed = kartspeed; - players[p].kartweight = kartweight; - } - if (extradata & DXD_COLOR) - { - // Color - M_Memcpy(name, demo_p, 16); - demo_p += 16; - for (i = 0; i < MAXSKINCOLORS; i++) - if (!stricmp(KartColor_Names[i], name)) // SRB2kart - { - players[p].skincolor = i; - if (players[p].mo) - players[p].mo->color = i; - break; - } - } - if (extradata & DXD_NAME) - { - // Name - M_Memcpy(player_names[p],demo_p,16); - demo_p += 16; - } - if (extradata & DXD_PLAYSTATE) - { - extradata = READUINT8(demo_p); - - switch (extradata) { - case DXD_PST_PLAYING: - players[p].pflags |= PF_WANTSTOJOIN; // fuck you - break; - - case DXD_PST_SPECTATING: - players[p].pflags &= ~PF_WANTSTOJOIN; // double-fuck you - if (!playeringame[p]) - { - CL_ClearPlayer(p); - playeringame[p] = true; - G_AddPlayer(p); - players[p].spectator = true; - - // There's likely an off-by-one error in timing recording or playback of joins. This hacks around it so I don't have to find out where that is. \o/ - if (oldcmd[p].forwardmove) - P_RandomByte(); - } - else - { - players[p].spectator = true; - if (players[p].mo) - P_DamageMobj(players[p].mo, NULL, NULL, 10000); - else - players[p].playerstate = PST_REBORN; - } - break; - - case DXD_PST_LEFT: - CL_RemovePlayer(p, 0); - break; - } - - G_ResetViews(); - - // maybe these are necessary? - if (G_BattleGametype()) - K_CheckBumpers(); // SRB2Kart - else if (G_RaceGametype()) - P_CheckRacers(); // also SRB2Kart - } - - - p = READUINT8(demo_p); - } - - while (p != DW_END) - { - UINT32 rng; - - switch (p) - { - case DW_RNG: - rng = READUINT32(demo_p); - if (P_GetRandSeed() != rng) - { - P_SetRandSeed(rng); - - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; - } - } - - p = READUINT8(demo_p); - } - - if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) - { - // end of demo data stream - G_CheckDemoStatus(); - return; - } -} - -void G_WriteDemoExtraData(void) -{ - INT32 i; - char name[16]; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (demo_extradata[i]) - { - WRITEUINT8(demo_p, i); - WRITEUINT8(demo_p, demo_extradata[i]); - - //if (demo_extradata[i] & DXD_RESPAWN) has no extra data - if (demo_extradata[i] & DXD_SKIN) - { - // Skin - memset(name, 0, 16); - strncpy(name, skins[players[i].skin].name, 16); - M_Memcpy(demo_p,name,16); - demo_p += 16; - - WRITEUINT8(demo_p, skins[players[i].skin].kartspeed); - WRITEUINT8(demo_p, skins[players[i].skin].kartweight); - } - if (demo_extradata[i] & DXD_COLOR) - { - // Color - memset(name, 0, 16); - strncpy(name, KartColor_Names[players[i].skincolor], 16); - M_Memcpy(demo_p,name,16); - demo_p += 16; - } - if (demo_extradata[i] & DXD_NAME) - { - // Name - memset(name, 0, 16); - strncpy(name, player_names[i], 16); - M_Memcpy(demo_p,name,16); - demo_p += 16; - } - if (demo_extradata[i] & DXD_PLAYSTATE) - { - demo_writerng = 1; - if (!playeringame[i]) - WRITEUINT8(demo_p, DXD_PST_LEFT); - else if ( - players[i].spectator && - !(players[i].pflags & PF_WANTSTOJOIN) // <= fuck you specifically - ) - WRITEUINT8(demo_p, DXD_PST_SPECTATING); - else - WRITEUINT8(demo_p, DXD_PST_PLAYING); - } - } - - demo_extradata[i] = 0; - } - - // May not be necessary, but might as well play it safe... - if ((leveltime & 255) == 128) - demo_writerng = 1; - - { - static UINT8 timeout = 0; - - if (timeout) timeout--; - - if (demo_writerng == 1 || (demo_writerng == 2 && timeout == 0)) - { - demo_writerng = 0; - timeout = 16; - WRITEUINT8(demo_p, DW_RNG); - WRITEUINT32(demo_p, P_GetRandSeed()); - } - } - - WRITEUINT8(demo_p, DW_END); -} - -void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) -{ - UINT8 ziptic; - - if (!demo_p || !demo.deferstart) - return; - ziptic = READUINT8(demo_p); - - if (ziptic & ZT_FWD) - oldcmd[playernum].forwardmove = READSINT8(demo_p); - if (ziptic & ZT_SIDE) - oldcmd[playernum].sidemove = READSINT8(demo_p); - if (ziptic & ZT_ANGLE) - oldcmd[playernum].angleturn = READINT16(demo_p); - if (ziptic & ZT_BUTTONS) - oldcmd[playernum].buttons = READUINT16(demo_p); - if (ziptic & ZT_AIMING) - oldcmd[playernum].aiming = READINT16(demo_p); - if (ziptic & ZT_DRIFT) - oldcmd[playernum].driftturn = READINT16(demo_p); - if (ziptic & ZT_LATENCY) - oldcmd[playernum].latency = READUINT8(demo_p); - - G_CopyTiccmd(cmd, &oldcmd[playernum], 1); - - // SRB2kart: Copy-pasted from ticcmd building, removes that crappy demo cam - if (((players[displayplayers[0]].mo && players[displayplayers[0]].speed > 0) // Moving - || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn - || (players[displayplayers[0]].kartstuff[k_respawn]) // Respawning - || (players[displayplayers[0]].spectator || objectplacing)) // Not a physical player - && !(players[displayplayers[0]].kartstuff[k_spinouttimer] - && (players[displayplayers[0]].kartstuff[k_sneakertimer] || players[displayplayers[0]].kartstuff[k_levelbooster]))) // Spinning and boosting cancels out spinout - localangle[0] += (cmd->angleturn<<16); -======= INT32 newmapnum = 0; INT32 mapnum; INT32 apromapnum = 0; @@ -7480,7 +4777,6 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) INT32 i; mapnamelen = strlen(mapname); ->>>>>>> srb2/next /* Count available maps; how ugly. */ for (i = 0, freqc = 0; i < NUMMAPS; ++i) @@ -7489,73 +4785,10 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) freqc++; } -<<<<<<< HEAD -void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) -{ - char ziptic = 0; - UINT8 *ziptic_p; -======= freq = ZZ_Calloc(freqc * sizeof (mapsearchfreq_t)); ->>>>>>> srb2/next wanttable = !!( freqp ); -<<<<<<< HEAD - if (cmd->forwardmove != oldcmd[playernum].forwardmove) - { - WRITEUINT8(demo_p,cmd->forwardmove); - oldcmd[playernum].forwardmove = cmd->forwardmove; - ziptic |= ZT_FWD; - } - - if (cmd->sidemove != oldcmd[playernum].sidemove) - { - WRITEUINT8(demo_p,cmd->sidemove); - oldcmd[playernum].sidemove = cmd->sidemove; - ziptic |= ZT_SIDE; - } - - if (cmd->angleturn != oldcmd[playernum].angleturn) - { - WRITEINT16(demo_p,cmd->angleturn); - oldcmd[playernum].angleturn = cmd->angleturn; - ziptic |= ZT_ANGLE; - } - - if (cmd->buttons != oldcmd[playernum].buttons) - { - WRITEUINT16(demo_p,cmd->buttons); - oldcmd[playernum].buttons = cmd->buttons; - ziptic |= ZT_BUTTONS; - } - - if (cmd->aiming != oldcmd[playernum].aiming) - { - WRITEINT16(demo_p,cmd->aiming); - oldcmd[playernum].aiming = cmd->aiming; - ziptic |= ZT_AIMING; - } - - if (cmd->driftturn != oldcmd[playernum].driftturn) - { - WRITEINT16(demo_p,cmd->driftturn); - oldcmd[playernum].driftturn = cmd->driftturn; - ziptic |= ZT_DRIFT; - } - - if (cmd->latency != oldcmd[playernum].latency) - { - WRITEUINT8(demo_p,cmd->latency); - oldcmd[playernum].latency = cmd->latency; - ziptic |= ZT_LATENCY; - } - - *ziptic_p = ziptic; - - // attention here for the ticcmd size! - // latest demos with mouse aiming byte in ticcmd - if (!(demoflags & DF_GHOST) && ziptic_p > demoend - 9) -======= freqc = 0; for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum) if (mapheaderinfo[i]) @@ -7624,7 +4857,6 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) } if (newmapnum == 0)/* calculate most queries met! */ ->>>>>>> srb2/next { frequ = 0; for (i = 0; i < freqc; ++i) @@ -7641,101 +4873,6 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) } } -<<<<<<< HEAD -void G_GhostAddThok(INT32 playernum) -{ - if (!demo.recording || !(demoflags & DF_GHOST)) - return; - ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_THOK; -} - -void G_GhostAddSpin(INT32 playernum) -{ - if (!demo.recording || !(demoflags & DF_GHOST)) - return; - ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_SPIN; -} - -void G_GhostAddRev(INT32 playernum) -{ - if (!demo.recording || !(demoflags & DF_GHOST)) - return; - ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_REV; -} - -void G_GhostAddFlip(INT32 playernum) -{ - if (!demo.recording || !(demoflags & DF_GHOST)) - return; - ghostext[playernum].flags |= EZT_FLIP; -} - -void G_GhostAddColor(INT32 playernum, ghostcolor_t color) -{ - if (!demo.recording || !(demoflags & DF_GHOST)) - return; - if (ghostext[playernum].lastcolor == (UINT8)color) - { - ghostext[playernum].flags &= ~EZT_COLOR; - return; - } - ghostext[playernum].flags |= EZT_COLOR; - ghostext[playernum].color = (UINT8)color; -} - -void G_GhostAddScale(INT32 playernum, fixed_t scale) -{ - if (!demo.recording || !(demoflags & DF_GHOST)) - return; - if (ghostext[playernum].lastscale == scale) - { - ghostext[playernum].flags &= ~EZT_SCALE; - return; - } - ghostext[playernum].flags |= EZT_SCALE; - ghostext[playernum].scale = scale; -} - -void G_GhostAddHit(INT32 playernum, mobj_t *victim) -{ - if (!demo.recording || !(demoflags & DF_GHOST)) - return; - ghostext[playernum].flags |= EZT_HIT; - ghostext[playernum].hits++; - ghostext[playernum].hitlist = Z_Realloc(ghostext[playernum].hitlist, ghostext[playernum].hits * sizeof(mobj_t *), PU_LEVEL, NULL); - ghostext[playernum].hitlist[ghostext[playernum].hits-1] = victim; -} - -void G_WriteAllGhostTics(void) -{ - INT32 i, counter = leveltime; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!players[i].mo) - continue; - - counter++; - - if (counter % cv_netdemosyncquality.value != 0) // Only write 1 in this many ghost datas per tic to cut down on multiplayer replay size. - continue; - - WRITEUINT8(demo_p, i); - G_WriteGhostTic(players[i].mo, i); - } - WRITEUINT8(demo_p, 0xFF); -} - -void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) -{ - char ziptic = 0; - UINT8 *ziptic_p; - UINT32 i; - UINT8 sprite; - UINT8 frame; -======= if (freqp) (*freqp) = freq; else @@ -7769,29 +4906,9 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) INT32 newmapnum; size_t mapnamelen; ->>>>>>> srb2/next char *p; -<<<<<<< HEAD - ziptic_p = demo_p++; // the ziptic, written at the end of this function - - #define MAXMOM (0x7FFF<<8) - - // GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic. - if (abs(ghost->x-oldghost[playernum].x) > MAXMOM - || abs(ghost->y-oldghost[playernum].y) > MAXMOM - || abs(ghost->z-oldghost[playernum].z) > MAXMOM - || ((UINT8)(leveltime & 255) > 0 && (UINT8)(leveltime & 255) <= (UINT8)cv_netdemosyncquality.value)) // Hack to enable slightly nicer resyncing - { - oldghost[playernum].x = ghost->x; - oldghost[playernum].y = ghost->y; - oldghost[playernum].z = ghost->z; - ziptic |= GZT_XYZ; - WRITEFIXED(demo_p,oldghost[playernum].x); - WRITEFIXED(demo_p,oldghost[playernum].y); - WRITEFIXED(demo_p,oldghost[playernum].z); -======= mapnamelen = strlen(mapname); if (mapnamelen == 2)/* maybe two digit code */ @@ -7803,2811 +4920,10 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) { if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) )) usemapcode = true; ->>>>>>> srb2/next } if (!usemapcode) { -<<<<<<< HEAD - // For moving normally: - // Store one full byte of movement, plus one byte of fractional movement. - INT16 momx = (INT16)((ghost->x-oldghost[playernum].x + (1<<4))>>8); - INT16 momy = (INT16)((ghost->y-oldghost[playernum].y + (1<<4))>>8); - if (momx != oldghost[playernum].momx - || momy != oldghost[playernum].momy) - { - oldghost[playernum].momx = momx; - oldghost[playernum].momy = momy; - ziptic |= GZT_MOMXY; - WRITEINT16(demo_p,momx); - WRITEINT16(demo_p,momy); - } - momx = (INT16)((ghost->z-oldghost[playernum].z + (1<<4))>>8); - if (momx != oldghost[playernum].momz) - { - oldghost[playernum].momz = momx; - ziptic |= GZT_MOMZ; - WRITEINT16(demo_p,momx); - } - - // This SHOULD set oldghost.x/y/z to match ghost->x/y/z - // but it keeps the fractional loss of one byte, - // so it will hopefully be made up for in future tics. - oldghost[playernum].x += oldghost[playernum].momx<<8; - oldghost[playernum].y += oldghost[playernum].momy<<8; - oldghost[playernum].z += oldghost[playernum].momz<<8; - } - - #undef MAXMOM - - // Only store the 8 most relevant bits of angle - // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites - // and it does not affect this mode of movement at all anyway. - if (ghost->angle>>24 != oldghost[playernum].angle) - { - oldghost[playernum].angle = ghost->angle>>24; - ziptic |= GZT_ANGLE; - WRITEUINT8(demo_p,oldghost[playernum].angle); - } - - // Store the sprite frame. - frame = ghost->frame & FF_FRAMEMASK; - if (frame != oldghost[playernum].frame) - { - oldghost[playernum].frame = frame; - ziptic |= GZT_SPRITE; - WRITEUINT8(demo_p,oldghost[playernum].frame); - } - - // Check for sprite set changes - sprite = ghost->sprite; - if (sprite != oldghost[playernum].sprite) - { - oldghost[playernum].sprite = sprite; - ghostext[playernum].flags |= EZT_SPRITE; - } - - if (ghost->player) - { - if ( - ghostext[playernum].kartitem != ghost->player->kartstuff[k_itemtype] || - ghostext[playernum].kartamount != ghost->player->kartstuff[k_itemamount] || - ghostext[playernum].kartbumpers != ghost->player->kartstuff[k_bumper] - ) - { - ghostext[playernum].flags |= EZT_KART; - ghostext[playernum].kartitem = ghost->player->kartstuff[k_itemtype]; - ghostext[playernum].kartamount = ghost->player->kartstuff[k_itemamount]; - ghostext[playernum].kartbumpers = ghost->player->kartstuff[k_bumper]; - - } - } - - if (ghostext[playernum].color == ghostext[playernum].lastcolor) - ghostext[playernum].flags &= ~EZT_COLOR; - if (ghostext[playernum].scale == ghostext[playernum].lastscale) - ghostext[playernum].flags &= ~EZT_SCALE; - - if (ghostext[playernum].flags) - { - ziptic |= GZT_EXTRA; - WRITEUINT8(demo_p,ghostext[playernum].flags); - - if (ghostext[playernum].flags & EZT_COLOR) - { - WRITEUINT8(demo_p,ghostext[playernum].color); - ghostext[playernum].lastcolor = ghostext[playernum].color; - } - if (ghostext[playernum].flags & EZT_SCALE) - { - WRITEFIXED(demo_p,ghostext[playernum].scale); - ghostext[playernum].lastscale = ghostext[playernum].scale; - } - if (ghostext[playernum].flags & EZT_HIT) - { - WRITEUINT16(demo_p,ghostext[playernum].hits); - for (i = 0; i < ghostext[playernum].hits; i++) - { - mobj_t *mo = ghostext[playernum].hitlist[i]; - WRITEUINT32(demo_p,UINT32_MAX); // reserved for some method of determining exactly which mobj this is. (mobjnum doesn't work here.) - WRITEUINT32(demo_p,mo->type); - WRITEUINT16(demo_p,(UINT16)mo->health); - WRITEFIXED(demo_p,mo->x); - WRITEFIXED(demo_p,mo->y); - WRITEFIXED(demo_p,mo->z); - WRITEANGLE(demo_p,mo->angle); - } - Z_Free(ghostext[playernum].hitlist); - ghostext[playernum].hits = 0; - ghostext[playernum].hitlist = NULL; - } - if (ghostext[playernum].flags & EZT_SPRITE) - WRITEUINT8(demo_p,sprite); - if (ghostext[playernum].flags & EZT_KART) - { - WRITEINT32(demo_p, ghostext[playernum].kartitem); - WRITEINT32(demo_p, ghostext[playernum].kartamount); - WRITEINT32(demo_p, ghostext[playernum].kartbumpers); - } - ghostext[playernum].flags = 0; - } - - *ziptic_p = ziptic; - - // attention here for the ticcmd size! - // latest demos with mouse aiming byte in ticcmd - if (demo_p >= demoend - (13 + 9)) - { - G_CheckDemoStatus(); // no more space - return; - } -} - -void G_ConsAllGhostTics(void) -{ - UINT8 p = READUINT8(demo_p); - - while (p != 0xFF) - { - G_ConsGhostTic(p); - p = READUINT8(demo_p); - } - - if (*demo_p == DEMOMARKER) - { - // end of demo data stream - G_CheckDemoStatus(); - return; - } -} - -// Uses ghost data to do consistency checks on your position. -// This fixes desynchronising demos when fighting eggman. -void G_ConsGhostTic(INT32 playernum) -{ - UINT8 ziptic; - fixed_t px,py,pz,gx,gy,gz; - mobj_t *testmo; - fixed_t syncleeway; - boolean nightsfail = false; - - if (!demo_p || !demo.deferstart) - return; - if (!(demoflags & DF_GHOST)) - return; // No ghost data to use. - - testmo = players[playernum].mo; - - // Grab ghost data. - ziptic = READUINT8(demo_p); - if (ziptic & GZT_XYZ) - { - oldghost[playernum].x = READFIXED(demo_p); - oldghost[playernum].y = READFIXED(demo_p); - oldghost[playernum].z = READFIXED(demo_p); - syncleeway = 0; - } - else - { - if (ziptic & GZT_MOMXY) - { - oldghost[playernum].momx = READINT16(demo_p)<<8; - oldghost[playernum].momy = READINT16(demo_p)<<8; - } - if (ziptic & GZT_MOMZ) - oldghost[playernum].momz = READINT16(demo_p)<<8; - oldghost[playernum].x += oldghost[playernum].momx; - oldghost[playernum].y += oldghost[playernum].momy; - oldghost[playernum].z += oldghost[playernum].momz; - syncleeway = FRACUNIT; - } - if (ziptic & GZT_ANGLE) - demo_p++; - if (ziptic & GZT_SPRITE) - demo_p++; - - if (ziptic & GZT_EXTRA) - { // But wait, there's more! - ziptic = READUINT8(demo_p); - if (ziptic & EZT_COLOR) - demo_p++; - if (ziptic & EZT_SCALE) - demo_p += sizeof(fixed_t); - if (ziptic & EZT_HIT) - { // Resync mob damage. - UINT16 i, count = READUINT16(demo_p); - thinker_t *th; - mobj_t *mobj; - - UINT32 type; - UINT16 health; - fixed_t x; - fixed_t y; - fixed_t z; - - for (i = 0; i < count; i++) - { - demo_p += 4; // reserved. - type = READUINT32(demo_p); - health = READUINT16(demo_p); - x = READFIXED(demo_p); - y = READFIXED(demo_p); - z = READFIXED(demo_p); - demo_p += sizeof(angle_t); // angle, unnecessary for cons. - - mobj = NULL; - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - mobj = (mobj_t *)th; - if (mobj->type == (mobjtype_t)type && mobj->x == x && mobj->y == y && mobj->z == z) - break; - mobj = NULL; // wasn't this one, keep searching. - } - if (mobj && mobj->health != health) // Wasn't damaged?! This is desync! Fix it! - { - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; - P_DamageMobj(mobj, players[0].mo, players[0].mo, 1); - } - } - } - if (ziptic & EZT_SPRITE) - demo_p++; - if (ziptic & EZT_KART) - { - ghostext[playernum].kartitem = READINT32(demo_p); - ghostext[playernum].kartamount = READINT32(demo_p); - ghostext[playernum].kartbumpers = READINT32(demo_p); - } - } - - if (testmo) - { - // Re-synchronise - px = testmo->x; - py = testmo->y; - pz = testmo->z; - gx = oldghost[playernum].x; - gy = oldghost[playernum].y; - gz = oldghost[playernum].z; - - if (nightsfail || abs(px-gx) > syncleeway || abs(py-gy) > syncleeway || abs(pz-gz) > syncleeway) - { - ghostext[playernum].desyncframes++; - - if (ghostext[playernum].desyncframes >= 2) - { - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; - - P_UnsetThingPosition(testmo); - testmo->x = oldghost[playernum].x; - testmo->y = oldghost[playernum].y; - P_SetThingPosition(testmo); - testmo->z = oldghost[playernum].z; - - if (abs(testmo->z - testmo->floorz) < 4*FRACUNIT) - testmo->z = testmo->floorz; // Sync players to the ground when they're likely supposed to be there... - - ghostext[playernum].desyncframes = 2; - } - } - else - ghostext[playernum].desyncframes = 0; - - if (players[playernum].kartstuff[k_itemtype] != ghostext[playernum].kartitem - || players[playernum].kartstuff[k_itemamount] != ghostext[playernum].kartamount - || players[playernum].kartstuff[k_bumper] != ghostext[playernum].kartbumpers) - { - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; - - players[playernum].kartstuff[k_itemtype] = ghostext[playernum].kartitem; - players[playernum].kartstuff[k_itemamount] = ghostext[playernum].kartamount; - players[playernum].kartstuff[k_bumper] = ghostext[playernum].kartbumpers; - } - } - - if (*demo_p == DEMOMARKER) - { - // end of demo data stream - G_CheckDemoStatus(); - return; - } -} - -void G_GhostTicker(void) -{ - demoghost *g,*p; - for(g = ghosts, p = NULL; g; g = g->next) - { - // Skip normal demo data. - UINT8 ziptic = READUINT8(g->p); - - while (ziptic != DW_END) // Get rid of extradata stuff - { - if (ziptic == 0) // Only support player 0 info for now - { - ziptic = READUINT8(g->p); - if (ziptic & DXD_SKIN) - g->p += 18; // We _could_ read this info, but it shouldn't change anything in record attack... - if (ziptic & DXD_COLOR) - g->p += 16; // Same tbh - if (ziptic & DXD_NAME) - g->p += 16; // yea - if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING) - I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this - } - else if (ziptic == DW_RNG) - g->p += 4; // RNG seed - else - I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this - - ziptic = READUINT8(g->p); - } - - ziptic = READUINT8(g->p); // Back to actual ziptic stuff - - if (ziptic & ZT_FWD) - g->p++; - if (ziptic & ZT_SIDE) - g->p++; - if (ziptic & ZT_ANGLE) - g->p += 2; - if (ziptic & ZT_BUTTONS) - g->p += 2; - if (ziptic & ZT_AIMING) - g->p += 2; - if (ziptic & ZT_DRIFT) - g->p += 2; - if (ziptic & ZT_LATENCY) - g->p += 1; - - // Grab ghost data. - ziptic = READUINT8(g->p); - - if (ziptic == 0xFF) - goto skippedghosttic; // Didn't write ghost info this frame - else if (ziptic != 0) - I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this - ziptic = READUINT8(g->p); - - if (ziptic & GZT_XYZ) - { - g->oldmo.x = READFIXED(g->p); - g->oldmo.y = READFIXED(g->p); - g->oldmo.z = READFIXED(g->p); - } - else - { - if (ziptic & GZT_MOMXY) - { - g->oldmo.momx = READINT16(g->p)<<8; - g->oldmo.momy = READINT16(g->p)<<8; - } - if (ziptic & GZT_MOMZ) - g->oldmo.momz = READINT16(g->p)<<8; - g->oldmo.x += g->oldmo.momx; - g->oldmo.y += g->oldmo.momy; - g->oldmo.z += g->oldmo.momz; - } - if (ziptic & GZT_ANGLE) - g->oldmo.angle = READUINT8(g->p)<<24; - if (ziptic & GZT_SPRITE) - g->oldmo.frame = READUINT8(g->p); - - // Update ghost - P_UnsetThingPosition(g->mo); - g->mo->x = g->oldmo.x; - g->mo->y = g->oldmo.y; - g->mo->z = g->oldmo.z; - P_SetThingPosition(g->mo); - g->mo->angle = g->oldmo.angle; - g->mo->frame = g->oldmo.frame | tr_trans30<p); - if (ziptic & EZT_COLOR) - { - g->color = READUINT8(g->p); - switch(g->color) - { - default: - case GHC_NORMAL: // Go back to skin color - g->mo->color = g->oldmo.color; - break; - // Handled below - case GHC_SUPER: - case GHC_INVINCIBLE: - break; - case GHC_FIREFLOWER: // Fireflower - g->mo->color = SKINCOLOR_WHITE; - break; - } - } - if (ziptic & EZT_FLIP) - g->mo->eflags ^= MFE_VERTICALFLIP; - if (ziptic & EZT_SCALE) - { - g->mo->destscale = READFIXED(g->p); - if (g->mo->destscale != g->mo->scale) - P_SetScale(g->mo, g->mo->destscale); - } - if (ziptic & EZT_THOKMASK) - { // Let's only spawn ONE of these per frame, thanks. - mobj_t *mobj; - INT32 type = -1; - if (g->mo->skin) - { - switch (ziptic & EZT_THOKMASK) - { - case EZT_THOK: - type = (UINT32)mobjinfo[MT_PLAYER].painchance; - break; - case EZT_SPIN: - type = (UINT32)mobjinfo[MT_PLAYER].damage; - break; - case EZT_REV: - type = (UINT32)mobjinfo[MT_PLAYER].raisestate; - break; - } - } - if (type == MT_GHOST) - { - mobj = P_SpawnGhostMobj(g->mo); // does a large portion of the work for us - mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<mo->x, g->mo->y, g->mo->z - FixedDiv(FixedMul(g->mo->info->height, g->mo->scale) - g->mo->height,3*FRACUNIT), MT_THOK); - mobj->sprite = states[mobjinfo[type].spawnstate].sprite; - mobj->frame = (states[mobjinfo[type].spawnstate].frame & FF_FRAMEMASK) | tr_trans60<tics = -1; // nope. - mobj->color = g->mo->color; - if (g->mo->eflags & MFE_VERTICALFLIP) - { - mobj->flags2 |= MF2_OBJECTFLIP; - mobj->eflags |= MFE_VERTICALFLIP; - } - P_SetScale(mobj, g->mo->scale); - mobj->destscale = g->mo->scale; - } - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_UnsetThingPosition(mobj); - mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... - P_SetThingPosition(mobj); - mobj->fuse = 8; - P_SetTarget(&mobj->target, g->mo); - } - if (ziptic & EZT_HIT) - { // Spawn hit poofs for killing things! - UINT16 i, count = READUINT16(g->p), health; - //UINT32 type; - fixed_t x,y,z; - angle_t angle; - mobj_t *poof; - for (i = 0; i < count; i++) - { - g->p += 4; // reserved - g->p += 4; // backwards compat., type used to be here - health = READUINT16(g->p); - x = READFIXED(g->p); - y = READFIXED(g->p); - z = READFIXED(g->p); - angle = READANGLE(g->p); - if (health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad. - continue; - poof = P_SpawnMobj(x, y, z, MT_GHOST); - poof->angle = angle; - poof->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... - poof->health = 0; - P_SetMobjStateNF(poof, S_XPLD1); - } - } - if (ziptic & EZT_SPRITE) - g->mo->sprite = READUINT8(g->p); - if (ziptic & EZT_KART) - g->p += 12; // kartitem, kartamount, kartbumpers - } - - if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here. - I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this - -skippedghosttic: - // Tick ghost colors (Super and Mario Invincibility flashing) - switch(g->color) - { - case GHC_SUPER: // Super Sonic (P_DoSuperStuff) - g->mo->color = SKINCOLOR_SUPER1; - g->mo->color += abs( ( (signed)( (unsigned)leveltime >> 1 ) % 9) - 4); - break; - case GHC_INVINCIBLE: // Mario invincibility (P_CheckInvincibilityTimer) - g->mo->color = (UINT8)(leveltime % MAXSKINCOLORS); - break; - default: - break; - } - - // Demo ends after ghost data. - if (*g->p == DEMOMARKER) - { - g->mo->momx = g->mo->momy = g->mo->momz = 0; - if (p) - p->next = g->next; - else - ghosts = g->next; - Z_Free(g); - continue; - } - p = g; - } -} - -// Demo rewinding functions -typedef struct rewindinfo_s { - tic_t leveltime; - - struct { - boolean ingame; - player_t player; - mobj_t mobj; - } playerinfo[MAXPLAYERS]; - - struct rewindinfo_s *prev; -} rewindinfo_t; - -static tic_t currentrewindnum; -static rewindinfo_t *rewindhead = NULL; // Reverse chronological order - -void G_InitDemoRewind(void) -{ - CL_ClearRewinds(); - - while (rewindhead) - { - rewindinfo_t *p = rewindhead->prev; - Z_Free(rewindhead); - rewindhead = p; - } - - currentrewindnum = 0; -} - -void G_StoreRewindInfo(void) -{ - static UINT8 timetolog = 8; - rewindinfo_t *info; - size_t i; - - if (timetolog-- > 0) - return; - timetolog = 8; - - info = Z_Calloc(sizeof(rewindinfo_t), PU_STATIC, NULL); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - { - info->playerinfo[i].ingame = false; - continue; - } - - info->playerinfo[i].ingame = true; - memcpy(&info->playerinfo[i].player, &players[i], sizeof(player_t)); - if (players[i].mo) - memcpy(&info->playerinfo[i].mobj, players[i].mo, sizeof(mobj_t)); - } - - info->leveltime = leveltime; - info->prev = rewindhead; - rewindhead = info; -} - -void G_PreviewRewind(tic_t previewtime) -{ - SINT8 i; - size_t j; - fixed_t tweenvalue = 0; - rewindinfo_t *info = rewindhead, *next_info = rewindhead; - - if (!info) - return; - - while (info->leveltime > previewtime && info->prev) - { - next_info = info; - info = info->prev; - } - if (info != next_info) - tweenvalue = FixedDiv(previewtime - info->leveltime, next_info->leveltime - info->leveltime); - - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - { - if (info->playerinfo[i].player.mo) - { - //@TODO spawn temp object to act as a player display - } - - continue; - } - - if (!info->playerinfo[i].ingame || !info->playerinfo[i].player.mo) - { - if (players[i].mo) - players[i].mo->flags2 |= MF2_DONTDRAW; - - continue; - } - - if (!players[i].mo) - continue; //@TODO spawn temp object to act as a player display - - players[i].mo->flags2 &= ~MF2_DONTDRAW; - - P_UnsetThingPosition(players[i].mo); -#define TWEEN(pr) info->playerinfo[i].mobj.pr + FixedMul((INT32) (next_info->playerinfo[i].mobj.pr - info->playerinfo[i].mobj.pr), tweenvalue) - players[i].mo->x = TWEEN(x); - players[i].mo->y = TWEEN(y); - players[i].mo->z = TWEEN(z); - players[i].mo->angle = TWEEN(angle); -#undef TWEEN - P_SetThingPosition(players[i].mo); - - players[i].frameangle = info->playerinfo[i].player.frameangle + FixedMul((INT32) (next_info->playerinfo[i].player.frameangle - info->playerinfo[i].player.frameangle), tweenvalue); - - players[i].mo->sprite = info->playerinfo[i].mobj.sprite; - players[i].mo->frame = info->playerinfo[i].mobj.frame; - - players[i].realtime = info->playerinfo[i].player.realtime; - for (j = 0; j < NUMKARTSTUFF; j++) - players[i].kartstuff[j] = info->playerinfo[i].player.kartstuff[j]; - } - - for (i = splitscreen; i >= 0; i--) - P_ResetCamera(&players[displayplayers[i]], &camera[i]); -} - -void G_ConfirmRewind(tic_t rewindtime) -{ - SINT8 i; - tic_t j; - boolean oldmenuactive = menuactive, oldsounddisabled = sound_disabled; - - INT32 olddp1 = displayplayers[0], olddp2 = displayplayers[1], olddp3 = displayplayers[2], olddp4 = displayplayers[3]; - UINT8 oldss = splitscreen; - - menuactive = false; // Prevent loops - - CV_StealthSetValue(&cv_renderview, 0); - - if (rewindtime <= starttime) - { - demo.rewinding = false; - G_DoPlayDemo(NULL); // Restart the current demo - } - else - { - rewind_t *rewind; - sound_disabled = true; // Prevent sound spam - demo.rewinding = true; - - rewind = CL_RewindToTime(rewindtime); - - if (rewind) - { - demo_p = demobuffer + rewind->demopos; - memcpy(oldcmd, rewind->oldcmd, sizeof (oldcmd)); - memcpy(oldghost, rewind->oldghost, sizeof (oldghost)); - paused = false; - } - else - { - demo.rewinding = true; - G_DoPlayDemo(NULL); // Restart the current demo - } - } - - for (j = 0; j < rewindtime && leveltime < rewindtime; j++) - { - G_Ticker((j % NEWTICRATERATIO) == 0); - } - - demo.rewinding = false; - menuactive = oldmenuactive; // Bring the menu back up - sound_disabled = oldsounddisabled; // Re-enable SFX - - wipegamestate = gamestate; // No fading back in! - - COM_BufInsertText("renderview on\n"); - - if (demo.freecam) - return; // don't touch from there - - splitscreen = oldss; - displayplayers[0] = olddp1; - displayplayers[1] = olddp2; - displayplayers[2] = olddp3; - displayplayers[3] = olddp4; - R_ExecuteSetViewSize(); - G_ResetViews(); - - for (i = splitscreen; i >= 0; i--) - P_ResetCamera(&players[displayplayers[i]], &camera[i]); -} - -void G_ReadMetalTic(mobj_t *metal) -{ - UINT8 ziptic; - UINT16 speed; - UINT8 statetype; - - if (!metal_p) - return; - ziptic = READUINT8(metal_p); - - // Read changes from the tic - if (ziptic & GZT_XYZ) - { - P_TeleportMove(metal, READFIXED(metal_p), READFIXED(metal_p), READFIXED(metal_p)); - oldmetal.x = metal->x; - oldmetal.y = metal->y; - oldmetal.z = metal->z; - } - else - { - if (ziptic & GZT_MOMXY) - { - oldmetal.momx = READINT16(metal_p)<<8; - oldmetal.momy = READINT16(metal_p)<<8; - } - if (ziptic & GZT_MOMZ) - oldmetal.momz = READINT16(metal_p)<<8; - oldmetal.x += oldmetal.momx; - oldmetal.y += oldmetal.momy; - oldmetal.z += oldmetal.momz; - } - if (ziptic & GZT_ANGLE) - oldmetal.angle = READUINT8(metal_p)<<24; - if (ziptic & GZT_SPRITE) - metal_p++; // Currently unused. (Metal Sonic figures out what he's doing his own damn self.) - - // Set movement, position, and angle - // oldmetal contains where you're supposed to be. - metal->momx = oldmetal.momx; - metal->momy = oldmetal.momy; - metal->momz = oldmetal.momz; - P_UnsetThingPosition(metal); - metal->x = oldmetal.x; - metal->y = oldmetal.y; - metal->z = oldmetal.z; - P_SetThingPosition(metal); - metal->angle = oldmetal.angle; - - if (ziptic & GZT_EXTRA) - { // But wait, there's more! - ziptic = READUINT8(metal_p); - if (ziptic & EZT_FLIP) - metal->eflags ^= MFE_VERTICALFLIP; - if (ziptic & EZT_SCALE) - { - metal->destscale = READFIXED(metal_p); - if (metal->destscale != metal->scale) - P_SetScale(metal, metal->destscale); - } - } - - // Calculates player's speed based on distance-of-a-line formula - speed = FixedDiv(P_AproxDistance(oldmetal.momx, oldmetal.momy), metal->scale)>>FRACBITS; - - // Use speed to decide an appropriate state - if (speed > 20) // default skin runspeed - statetype = 2; - else if (speed > 1) // stopspeed - statetype = 1; - else - statetype = 0; - - // Set state - if (statetype != metal->threshold) - { - switch (statetype) - { - case 2: // run - P_SetMobjState(metal,metal->info->meleestate); - break; - case 1: // walk - P_SetMobjState(metal,metal->info->seestate); - break; - default: // stand - P_SetMobjState(metal,metal->info->spawnstate); - break; - } - metal->threshold = statetype; - } - - // TODO: Modify state durations based on movement speed, similar to players? - - if (*metal_p == DEMOMARKER) - { - // end of demo data stream - G_StopMetalDemo(); - return; - } -} - -void G_WriteMetalTic(mobj_t *metal) -{ - UINT8 ziptic = 0; - UINT8 *ziptic_p; - - if (!demo_p) // demo_p will be NULL until the race start linedef executor is triggered! - return; - - ziptic_p = demo_p++; // the ziptic, written at the end of this function - - #define MAXMOM (0xFFFF<<8) - - // GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic. - if (abs(metal->x-oldmetal.x) > MAXMOM - || abs(metal->y-oldmetal.y) > MAXMOM - || abs(metal->z-oldmetal.z) > MAXMOM) - { - oldmetal.x = metal->x; - oldmetal.y = metal->y; - oldmetal.z = metal->z; - WRITEFIXED(demo_p,oldmetal.x); - WRITEFIXED(demo_p,oldmetal.y); - WRITEFIXED(demo_p,oldmetal.z); - ziptic |= GZT_XYZ; - } - else - { - // For moving normally: - // Store one full byte of movement, plus one byte of fractional movement. - INT16 momx = (INT16)((metal->x-oldmetal.x)>>8); - INT16 momy = (INT16)((metal->y-oldmetal.y)>>8); - if (momx != oldmetal.momx - || momy != oldmetal.momy) - { - oldmetal.momx = momx; - oldmetal.momy = momy; - WRITEINT16(demo_p,momx); - WRITEINT16(demo_p,momy); - ziptic |= GZT_MOMXY; - } - momx = (INT16)((metal->z-oldmetal.z)>>8); - if (momx != oldmetal.momz) - { - oldmetal.momz = momx; - WRITEINT16(demo_p,momx); - ziptic |= GZT_MOMZ; - } - - // This SHOULD set oldmetal.x/y/z to match metal->x/y/z - // but it keeps the fractional loss of one byte, - // so it will hopefully be made up for in future tics. - oldmetal.x += oldmetal.momx<<8; - oldmetal.y += oldmetal.momy<<8; - oldmetal.z += oldmetal.momz<<8; - } - - #undef MAXMOM - - // Only store the 8 most relevant bits of angle - // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites - // and it does not affect movement at all anyway. - if (metal->angle>>24 != oldmetal.angle) - { - oldmetal.angle = metal->angle>>24; - WRITEUINT8(demo_p,oldmetal.angle); - ziptic |= GZT_ANGLE; - } - - // Metal Sonic does not need our state changes. - // ... currently. - - { - UINT8 *exttic_p = NULL; - UINT8 exttic = 0; - if ((metal->eflags & MFE_VERTICALFLIP) != (oldmetal.eflags & MFE_VERTICALFLIP)) - { - if (!exttic_p) - exttic_p = demo_p++; - exttic |= EZT_FLIP; - oldmetal.eflags ^= MFE_VERTICALFLIP; - } - if (metal->scale != oldmetal.scale) - { - if (!exttic_p) - exttic_p = demo_p++; - exttic |= EZT_SCALE; - WRITEFIXED(demo_p,metal->scale); - oldmetal.scale = metal->scale; - } - if (exttic_p) - { - *exttic_p = exttic; - ziptic |= GZT_EXTRA; - } - } - - *ziptic_p = ziptic; - - // attention here for the ticcmd size! - // latest demos with mouse aiming byte in ticcmd - if (demo_p >= demoend - 32) - { - G_StopMetalRecording(); // no more space - return; - } -} - -// -// G_RecordDemo -// -void G_RecordDemo(const char *name) -{ - INT32 maxsize; - - CONS_Printf("Recording demo %s.lmp\n", name); - - strcpy(demoname, name); - strcat(demoname, ".lmp"); - //@TODO make a maxdemosize cvar - maxsize = 1024*1024*2; - if (M_CheckParm("-maxdemo") && M_IsNextParm()) - maxsize = atoi(M_GetNextParm()) * 1024; -// if (demobuffer) -// free(demobuffer); - demo_p = NULL; - demobuffer = malloc(maxsize); - demoend = demobuffer + maxsize; - - demo.recording = true; -} - -void G_RecordMetal(void) -{ - INT32 maxsize; - maxsize = 1024*1024; - if (M_CheckParm("-maxdemo") && M_IsNextParm()) - maxsize = atoi(M_GetNextParm()) * 1024; - demo_p = NULL; - demobuffer = malloc(maxsize); - demoend = demobuffer + maxsize; - metalrecording = true; -} - -void G_BeginRecording(void) -{ - UINT8 i, p; - char name[16]; - player_t *player = &players[consoleplayer]; - - char *filename; - UINT8 totalfiles; - UINT8 *m; - - if (demo_p) - return; - memset(name,0,sizeof(name)); - - demo_p = demobuffer; - demoflags = DF_GHOST|(multiplayer ? DF_MULTIPLAYER : (modeattacking<important) - { - nameonly(( filename = va("%s", wadfiles[i]->filename) )); - WRITESTRINGN(demo_p, filename, 64); - WRITEMEM(demo_p, wadfiles[i]->md5sum, 16); - - totalfiles++; - } - - WRITEUINT8(m, totalfiles); - - switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - WRITEUINT32(demo_p,UINT32_MAX); // lap - break; - case ATTACKING_CAPSULES: // 2 - demotime_p = demo_p; - WRITEUINT32(demo_p,UINT32_MAX); // time - break; - default: // 3 - break; - } - - WRITEUINT32(demo_p,P_GetInitSeed()); - - // Reserved for extrainfo location from start of file - demoinfo_p = demo_p; - WRITEUINT32(demo_p, 0); - - // Save netvars - CV_SaveNetVars(&demo_p, true); - - // Now store some info for each in-game player - for (p = 0; p < MAXPLAYERS; p++) { - if (playeringame[p]) { - player = &players[p]; - - WRITEUINT8(demo_p, p | (player->spectator ? DEMO_SPECTATOR : 0)); - - // Name - memset(name, 0, 16); - strncpy(name, player_names[p], 16); - M_Memcpy(demo_p,name,16); - demo_p += 16; - - // Skin - memset(name, 0, 16); - strncpy(name, skins[player->skin].name, 16); - M_Memcpy(demo_p,name,16); - demo_p += 16; - - // Color - memset(name, 0, 16); - strncpy(name, KartColor_Names[player->skincolor], 16); - M_Memcpy(demo_p,name,16); - demo_p += 16; - - // Score, since Kart uses this to determine where you start on the map - WRITEUINT32(demo_p, player->score); - - // Power Levels - WRITEUINT16(demo_p, clientpowerlevels[p][G_BattleGametype() ? PWRLV_BATTLE : PWRLV_RACE]); - - // Kart speed and weight - WRITEUINT8(demo_p, skins[player->skin].kartspeed); - WRITEUINT8(demo_p, skins[player->skin].kartweight); - - } - } - - WRITEUINT8(demo_p, 0xFF); // Denote the end of the player listing - -#ifdef HAVE_BLUA - // player lua vars, always saved even if empty - LUA_ArchiveDemo(); -#endif - - memset(&oldcmd,0,sizeof(oldcmd)); - memset(&oldghost,0,sizeof(oldghost)); - memset(&ghostext,0,sizeof(ghostext)); - - for (i = 0; i < MAXPLAYERS; i++) - { - ghostext[i].lastcolor = ghostext[i].color = GHC_NORMAL; - ghostext[i].lastscale = ghostext[i].scale = FRACUNIT; - - if (players[i].mo) - { - oldghost[i].x = players[i].mo->x; - oldghost[i].y = players[i].mo->y; - oldghost[i].z = players[i].mo->z; - oldghost[i].angle = players[i].mo->angle; - - // preticker started us gravity flipped - if (players[i].mo->eflags & MFE_VERTICALFLIP) - ghostext[i].flags |= EZT_FLIP; - } - } -} - -void G_BeginMetal(void) -{ - mobj_t *mo = players[consoleplayer].mo; - - if (demo_p) - return; - - demo_p = demobuffer; - - // Write header. - M_Memcpy(demo_p, DEMOHEADER, 12); demo_p += 12; - WRITEUINT8(demo_p,VERSION); - WRITEUINT8(demo_p,SUBVERSION); - WRITEUINT16(demo_p,DEMOVERSION); - - // demo checksum - demo_p += 16; - - M_Memcpy(demo_p, "METL", 4); demo_p += 4; - - // Set up our memory. - memset(&oldmetal,0,sizeof(oldmetal)); - oldmetal.x = mo->x; - oldmetal.y = mo->y; - oldmetal.z = mo->z; - oldmetal.angle = mo->angle; -} - -void G_WriteStanding(UINT8 ranking, char *name, INT32 skinnum, UINT8 color, UINT32 val) -{ - char temp[16]; - - if (demoinfo_p && *(UINT32 *)demoinfo_p == 0) - { - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - *(UINT32 *)demoinfo_p = demo_p - demobuffer; - } - - WRITEUINT8(demo_p, DW_STANDING); - WRITEUINT8(demo_p, ranking); - - // Name - memset(temp, 0, 16); - strncpy(temp, name, 16); - M_Memcpy(demo_p,temp,16); - demo_p += 16; - - // Skin - memset(temp, 0, 16); - strncpy(temp, skins[skinnum].name, 16); - M_Memcpy(demo_p,temp,16); - demo_p += 16; - - // Color - memset(temp, 0, 16); - strncpy(temp, KartColor_Names[color], 16); - M_Memcpy(demo_p,temp,16); - demo_p += 16; - - // Score/time/whatever - WRITEUINT32(demo_p, val); -} - -void G_SetDemoTime(UINT32 ptime, UINT32 plap) -{ - if (!demo.recording || !demotime_p) - return; - if (demoflags & DF_TIMEATTACK) - { - WRITEUINT32(demotime_p, ptime); - WRITEUINT32(demotime_p, plap); - demotime_p = NULL; - } - else if (demoflags & DF_BREAKTHECAPSULES) - { - WRITEUINT32(demotime_p, ptime); - (void)plap; - demotime_p = NULL; - } -} - -static void G_LoadDemoExtraFiles(UINT8 **pp) -{ - UINT8 totalfiles; - char filename[MAX_WADPATH]; - UINT8 md5sum[16]; - filestatus_t ncs; - boolean toomany = false; - boolean alreadyloaded; - UINT8 i, j; - - totalfiles = READUINT8((*pp)); - for (i = 0; i < totalfiles; ++i) - { - if (toomany) - SKIPSTRING((*pp)); - else - { - strlcpy(filename, (char *)(*pp), sizeof filename); - SKIPSTRING((*pp)); - } - READMEM((*pp), md5sum, 16); - - if (!toomany) - { - alreadyloaded = false; - - for (j = 0; j < numwadfiles; ++j) - { - if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0) - { - alreadyloaded = true; - break; - } - } - - if (alreadyloaded) - continue; - - if (numwadfiles >= MAX_WADFILES) - toomany = true; - else - ncs = findfile(filename, md5sum, false); - - if (toomany) - { - CONS_Alert(CONS_WARNING, M_GetText("Too many files loaded to add anymore for demo playback\n")); - if (!CON_Ready()) - M_StartMessage(M_GetText("There are too many files loaded to add this demo's addons.\n\nDemo playback may desync.\n\nPress ESC\n"), NULL, MM_NOTHING); - } - else if (ncs != FS_FOUND) - { - if (ncs == FS_NOTFOUND) - CONS_Alert(CONS_NOTICE, M_GetText("You do not have a copy of %s\n"), filename); - else if (ncs == FS_MD5SUMBAD) - CONS_Alert(CONS_NOTICE, M_GetText("Checksum mismatch on %s\n"), filename); - else - CONS_Alert(CONS_NOTICE, M_GetText("Unknown error finding file %s\n"), filename); - - if (!CON_Ready()) - M_StartMessage(M_GetText("There were errors trying to add this demo's addons. Check the console for more information.\n\nDemo playback may desync.\n\nPress ESC\n"), NULL, MM_NOTHING); - } - else - { - P_AddWadFile(filename); - } - } - } -} - -static void G_SkipDemoExtraFiles(UINT8 **pp) -{ - UINT8 totalfiles; - UINT8 i; - - totalfiles = READUINT8((*pp)); - for (i = 0; i < totalfiles; ++i) - { - SKIPSTRING((*pp));// file name - (*pp) += 16;// md5 - } -} - -// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's. -// Enabling quick prevents filesystem checks to see if needed files are available to load. -static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick) -{ - UINT8 totalfiles, filesloaded, nmusfilecount; - char filename[MAX_WADPATH]; - UINT8 md5sum[16]; - boolean toomany = false; - boolean alreadyloaded; - UINT8 i, j; - UINT8 error = 0; - - totalfiles = READUINT8((*pp)); - filesloaded = 0; - for (i = 0; i < totalfiles; ++i) - { - if (toomany) - SKIPSTRING((*pp)); - else - { - strlcpy(filename, (char *)(*pp), sizeof filename); - SKIPSTRING((*pp)); - } - READMEM((*pp), md5sum, 16); - - if (!toomany) - { - alreadyloaded = false; - nmusfilecount = 0; - - for (j = 0; j < numwadfiles; ++j) - { - if (wadfiles[j]->important && j > mainwads) - nmusfilecount++; - else - continue; - - if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0) - { - alreadyloaded = true; - - if (i != nmusfilecount-1 && error < DFILE_ERROR_OUTOFORDER) - error |= DFILE_ERROR_OUTOFORDER; - - break; - } - } - - if (alreadyloaded) - { - filesloaded++; - continue; - } - - if (numwadfiles >= MAX_WADFILES) - error = DFILE_ERROR_CANNOTLOAD; - else if (!quick && findfile(filename, md5sum, false) != FS_FOUND) - error = DFILE_ERROR_CANNOTLOAD; - else if (error < DFILE_ERROR_INCOMPLETEOUTOFORDER) - error |= DFILE_ERROR_NOTLOADED; - } else - error = DFILE_ERROR_CANNOTLOAD; - } - - // Get final file count - nmusfilecount = 0; - - for (j = 0; j < numwadfiles; ++j) - if (wadfiles[j]->important && j > mainwads) - nmusfilecount++; - - if (!error && filesloaded < nmusfilecount) - error = DFILE_ERROR_EXTRAFILES; - - return error; -} - -// Returns bitfield: -// 1 == new demo has lower time -// 2 == new demo has higher score -// 4 == new demo has higher rings -UINT8 G_CmpDemoTime(char *oldname, char *newname) -{ - UINT8 *buffer,*p; - UINT8 flags; - UINT32 oldtime, newtime, oldlap, newlap; - UINT16 oldversion; - size_t bufsize ATTRUNUSED; - UINT8 c; - UINT16 s ATTRUNUSED; - UINT8 aflags = 0; - boolean uselaps = false; - - // load the new file - FIL_DefaultExtension(newname, ".lmp"); - bufsize = FIL_ReadFile(newname, &buffer); - I_Assert(bufsize != 0); - p = buffer; - - // read demo header - I_Assert(!memcmp(p, DEMOHEADER, 12)); - p += 12; // DEMOHEADER - c = READUINT8(p); // VERSION - I_Assert(c == VERSION); - c = READUINT8(p); // SUBVERSION - I_Assert(c == SUBVERSION); - s = READUINT16(p); - I_Assert(s == DEMOVERSION); - p += 64; // full demo title - p += 16; // demo checksum - I_Assert(!memcmp(p, "PLAY", 4)); - p += 4; // PLAY - p += 2; // gamemap - p += 16; // map md5 - flags = READUINT8(p); // demoflags - p++; // gametype - G_SkipDemoExtraFiles(&p); - - aflags = flags & (DF_TIMEATTACK|DF_BREAKTHECAPSULES); - I_Assert(aflags); - - if (flags & DF_TIMEATTACK) - uselaps = true; // get around uninitalized error - - newtime = READUINT32(p); - if (uselaps) - newlap = READUINT32(p); - else - newlap = UINT32_MAX; - - Z_Free(buffer); - - // load old file - FIL_DefaultExtension(oldname, ".lmp"); - if (!FIL_ReadFile(oldname, &buffer)) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), oldname); - return UINT8_MAX; - } - p = buffer; - - // read demo header - if (memcmp(p, DEMOHEADER, 12)) - { - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; - } p += 12; // DEMOHEADER - p++; // VERSION - p++; // SUBVERSION - oldversion = READUINT16(p); - switch(oldversion) // demoversion - { - case DEMOVERSION: // latest always supported - p += 64; // full demo title - break; - // too old, cannot support. - default: - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; - } - p += 16; // demo checksum - if (memcmp(p, "PLAY", 4)) - { - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; - } p += 4; // "PLAY" - p += 2; // gamemap - p += 16; // mapmd5 - flags = READUINT8(p); - p++; // gametype - G_SkipDemoExtraFiles(&p); - if (!(flags & aflags)) - { - CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname); - Z_Free(buffer); - return UINT8_MAX; - } - - oldtime = READUINT32(p); - if (uselaps) - oldlap = READUINT32(p); - else - oldlap = 0; - - Z_Free(buffer); - - c = 0; - - if (uselaps) - { - if (newtime < oldtime - || (newtime == oldtime && (newlap < oldlap))) - c |= 1; // Better time - if (newlap < oldlap - || (newlap == oldlap && newtime < oldtime)) - c |= 1<<1; // Better lap time - } - else - { - if (newtime < oldtime) - c |= 1; // Better time - } - - return c; -} - -void G_LoadDemoInfo(menudemo_t *pdemo) -{ - UINT8 *infobuffer, *info_p, *extrainfo_p; - UINT8 version, subversion, pdemoflags; - UINT16 pdemoversion, count; - - if (!FIL_ReadFile(pdemo->filepath, &infobuffer)) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), pdemo->filepath); - pdemo->type = MD_INVALID; - sprintf(pdemo->title, "INVALID REPLAY"); - - return; - } - - info_p = infobuffer; - - if (memcmp(info_p, DEMOHEADER, 12)) - { - CONS_Alert(CONS_ERROR, M_GetText("%s is not a SRB2Kart replay file.\n"), pdemo->filepath); - pdemo->type = MD_INVALID; - sprintf(pdemo->title, "INVALID REPLAY"); - Z_Free(infobuffer); - return; - } - - pdemo->type = MD_LOADED; - - info_p += 12; // DEMOHEADER - - version = READUINT8(info_p); - subversion = READUINT8(info_p); - pdemoversion = READUINT16(info_p); - - switch(pdemoversion) - { - case DEMOVERSION: // latest always supported - // demo title - M_Memcpy(pdemo->title, info_p, 64); - info_p += 64; - - break; - // too old, cannot support. - default: - CONS_Alert(CONS_ERROR, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemo->filepath); - pdemo->type = MD_INVALID; - sprintf(pdemo->title, "INVALID REPLAY"); - Z_Free(infobuffer); - return; - } - - if (version != VERSION || subversion != SUBVERSION) - pdemo->type = MD_OUTDATED; - - info_p += 16; // demo checksum - if (memcmp(info_p, "PLAY", 4)) - { - CONS_Alert(CONS_ERROR, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemo->filepath); - pdemo->type = MD_INVALID; - sprintf(pdemo->title, "INVALID REPLAY"); - Z_Free(infobuffer); - return; - } - info_p += 4; // "PLAY" - pdemo->map = READINT16(info_p); - info_p += 16; // mapmd5 - - pdemoflags = READUINT8(info_p); - - // temp? - if (!(pdemoflags & DF_MULTIPLAYER)) - { - CONS_Alert(CONS_ERROR, M_GetText("%s is not a multiplayer replay and can't be listed on this menu fully yet.\n"), pdemo->filepath); - Z_Free(infobuffer); - return; - } - - pdemo->gametype = READUINT8(info_p); - - pdemo->addonstatus = G_CheckDemoExtraFiles(&info_p, true); - info_p += 4; // RNG seed - - extrainfo_p = infobuffer + READUINT32(info_p); - - // Pared down version of CV_LoadNetVars to find the kart speed - pdemo->kartspeed = KARTSPEED_NORMAL; // Default to normal speed - count = READUINT16(info_p); - while (count--) - { - UINT16 netid; - char *svalue; - - netid = READUINT16(info_p); - svalue = (char *)info_p; - SKIPSTRING(info_p); - info_p++; // stealth - - if (netid == cv_kartspeed.netid) - { - UINT8 j; - for (j = 0; kartspeed_cons_t[j].strvalue; j++) - if (!stricmp(kartspeed_cons_t[j].strvalue, svalue)) - pdemo->kartspeed = kartspeed_cons_t[j].value; - } - else if (netid == cv_basenumlaps.netid && pdemo->gametype == GT_RACE) - pdemo->numlaps = atoi(svalue); - } - - if (pdemoflags & DF_ENCORE) - pdemo->kartspeed |= DF_ENCORE; - - /*// Temporary info until this is actually present in replays. - (void)extrainfo_p; - sprintf(pdemo->winnername, "transrights420"); - pdemo->winnerskin = 1; - pdemo->winnercolor = SKINCOLOR_MOONSET; - pdemo->winnertime = 6666;*/ - - // Read standings! - count = 0; - - while (READUINT8(extrainfo_p) == DW_STANDING) // Assume standings are always first in the extrainfo - { - INT32 i; - char temp[16]; - - pdemo->standings[count].ranking = READUINT8(extrainfo_p); - - // Name - M_Memcpy(pdemo->standings[count].name, extrainfo_p, 16); - extrainfo_p += 16; - - // Skin - M_Memcpy(temp,extrainfo_p,16); - extrainfo_p += 16; - pdemo->standings[count].skin = UINT8_MAX; - for (i = 0; i < numskins; i++) - if (stricmp(skins[i].name, temp) == 0) - { - pdemo->standings[count].skin = i; - break; - } - - // Color - M_Memcpy(temp,extrainfo_p,16); - extrainfo_p += 16; - for (i = 0; i < MAXSKINCOLORS; i++) - if (!stricmp(KartColor_Names[i],temp)) // SRB2kart - { - pdemo->standings[count].color = i; - break; - } - - // Score/time/whatever - pdemo->standings[count].timeorscore = READUINT32(extrainfo_p); - - count++; - - if (count >= MAXPLAYERS) - break; //@TODO still cycle through the rest of these if extra demo data is ever used - } - - // I think that's everything we need? - Z_Free(infobuffer); -} - -// -// G_PlayDemo -// -void G_DeferedPlayDemo(const char *name) -{ - COM_BufAddText("playdemo \""); - COM_BufAddText(name); - COM_BufAddText("\" -addfiles\n"); -} - -// -// Start a demo from a .LMP file or from a wad resource -// -#define SKIPERRORS -void G_DoPlayDemo(char *defdemoname) -{ - UINT8 i, p; - lumpnum_t l; - char skin[17],color[17],*n,*pdemoname; - UINT8 version,subversion; - UINT32 randseed; - char msg[1024]; -#if defined(SKIPERRORS) && !defined(DEVELOP) - boolean skiperrors = false; -#endif - boolean spectator; - UINT8 slots[MAXPLAYERS], kartspeed[MAXPLAYERS], kartweight[MAXPLAYERS], numslots = 0; - - G_InitDemoRewind(); - - skin[16] = '\0'; - color[16] = '\0'; - - // No demo name means we're restarting the current demo - if (defdemoname == NULL) - { - demo_p = demobuffer; - pdemoname = ZZ_Alloc(1); // Easier than adding checks for this everywhere it's freed - } - else - { - n = defdemoname+strlen(defdemoname); - while (*n != '/' && *n != '\\' && n != defdemoname) - n--; - if (n != defdemoname) - n++; - pdemoname = ZZ_Alloc(strlen(n)+1); - strcpy(pdemoname,n); - - M_SetPlaybackMenuPointer(); - - // Internal if no extension, external if one exists - if (FIL_CheckExtension(defdemoname)) - { - //FIL_DefaultExtension(defdemoname, ".lmp"); - if (!FIL_ReadFile(defdemoname, &demobuffer)) - { - snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - demo_p = demobuffer; - } - // load demo resource from WAD - else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) - { - snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - else // it's an internal demo - { - demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC); -#if defined(SKIPERRORS) && !defined(DEVELOP) - skiperrors = true; // SRB2Kart: Don't print warnings for staff ghosts, since they'll inevitably happen when we make bugfixes/changes... -#endif - } - } - - // read demo header - gameaction = ga_nothing; - demo.playback = true; - if (memcmp(demo_p, DEMOHEADER, 12)) - { - snprintf(msg, 1024, M_GetText("%s is not a SRB2Kart replay file.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - demo_p += 12; // DEMOHEADER - - version = READUINT8(demo_p); - subversion = READUINT8(demo_p); - demo.version = READUINT16(demo_p); - switch(demo.version) - { - case DEMOVERSION: // latest always supported - // demo title - M_Memcpy(demo.titlename, demo_p, 64); - demo_p += 64; - - break; - // too old, cannot support. - default: - snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - demo_p += 16; // demo checksum - if (memcmp(demo_p, "PLAY", 4)) - { - snprintf(msg, 1024, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - demo_p += 4; // "PLAY" - gamemap = READINT16(demo_p); - demo_p += 16; // mapmd5 - - demoflags = READUINT8(demo_p); - gametype = READUINT8(demo_p); - - if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running. - G_SkipDemoExtraFiles(&demo_p); - else if (demo.loadfiles) - G_LoadDemoExtraFiles(&demo_p); - else if (demo.ignorefiles) - G_SkipDemoExtraFiles(&demo_p); - else - { - UINT8 error = G_CheckDemoExtraFiles(&demo_p, false); - - if (error) - { - switch (error) - { - case DFILE_ERROR_NOTLOADED: - snprintf(msg, 1024, - M_GetText("Required files for this demo are not loaded.\n\nUse\n\"playdemo %s -addfiles\"\nto load them and play the demo.\n"), - pdemoname); - break; - - case DFILE_ERROR_OUTOFORDER: - snprintf(msg, 1024, - M_GetText("Required files for this demo are loaded out of order.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), - pdemoname); - break; - - case DFILE_ERROR_INCOMPLETEOUTOFORDER: - snprintf(msg, 1024, - M_GetText("Required files for this demo are not loaded, and some are out of order.\n\nUse\n\"playdemo %s -addfiles\"\nto load needed files and play the demo.\n"), - pdemoname); - break; - - case DFILE_ERROR_CANNOTLOAD: - snprintf(msg, 1024, - M_GetText("Required files for this demo cannot be loaded.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), - pdemoname); - break; - - case DFILE_ERROR_EXTRAFILES: - snprintf(msg, 1024, - M_GetText("You have additional files loaded beyond the demo's file list.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), - pdemoname); - break; - } - - CONS_Alert(CONS_ERROR, "%s", msg); - if (!CON_Ready()) // In the console they'll just see the notice there! No point pulling them out. - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - } - - modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT; - multiplayer = !!(demoflags & DF_MULTIPLAYER); - CON_ToggleOff(); - - hu_demotime = UINT32_MAX; - hu_demolap = UINT32_MAX; - - switch (modeattacking) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - hu_demotime = READUINT32(demo_p); - hu_demolap = READUINT32(demo_p); - break; - case ATTACKING_CAPSULES: // 2 - hu_demotime = READUINT32(demo_p); - break; - default: // 3 - modeattacking = ATTACKING_NONE; - break; - } - - // Random seed - randseed = READUINT32(demo_p); - demo_p += 4; // Extrainfo location - - // ...*map* not loaded? - if (!gamemap || (gamemap > NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->menuflags & LF2_EXISTSHACK)) - { - snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - - // net var data - CV_LoadNetVars(&demo_p); - - // Sigh ... it's an empty demo. - if (*demo_p == DEMOMARKER) - { - snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - - Z_Free(pdemoname); - - memset(&oldcmd,0,sizeof(oldcmd)); - memset(&oldghost,0,sizeof(oldghost)); - memset(&ghostext,0,sizeof(ghostext)); - -#if defined(SKIPERRORS) && !defined(DEVELOP) - if ((VERSION != version || SUBVERSION != subversion) && !skiperrors) -#else - if (VERSION != version || SUBVERSION != subversion) -#endif - CONS_Alert(CONS_WARNING, M_GetText("Demo version does not match game version. Desyncs may occur.\n")); - - // console warning messages -#if defined(SKIPERRORS) && !defined(DEVELOP) - demosynced = (!skiperrors); -#else - demosynced = true; -#endif - - // didn't start recording right away. - demo.deferstart = false; - -/*#ifdef HAVE_BLUA - LUAh_MapChange(gamemap); -#endif*/ - displayplayers[0] = consoleplayer = 0; - memset(playeringame,0,sizeof(playeringame)); - - // Load players that were in-game when the map started - p = READUINT8(demo_p); - - for (i = 1; i < MAXSPLITSCREENPLAYERS; i++) - displayplayers[i] = INT32_MAX; - - while (p != 0xFF) - { - spectator = false; - if (p & DEMO_SPECTATOR) - { - spectator = true; - p &= ~DEMO_SPECTATOR; - - if (modeattacking) - { - snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with spectators, and is thus invalid.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - } - slots[numslots] = p; numslots++; - - if (modeattacking && numslots > 1) - { - snprintf(msg, 1024, M_GetText("%s is a Record Attack replay with multiple players, and is thus invalid.\n"), pdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - M_StartMessage(msg, NULL, MM_NOTHING); - Z_Free(pdemoname); - Z_Free(demobuffer); - demo.playback = false; - demo.title = false; - return; - } - - if (!playeringame[displayplayers[0]] || players[displayplayers[0]].spectator) - displayplayers[0] = consoleplayer = serverplayer = p; - - playeringame[p] = true; - players[p].spectator = spectator; - - // Name - M_Memcpy(player_names[p],demo_p,16); - demo_p += 16; - - // Skin - M_Memcpy(skin,demo_p,16); - demo_p += 16; - SetPlayerSkin(p, skin); - - // Color - M_Memcpy(color,demo_p,16); - demo_p += 16; - for (i = 0; i < MAXSKINCOLORS; i++) - if (!stricmp(KartColor_Names[i],color)) // SRB2kart - { - players[p].skincolor = i; - break; - } - - // Score, since Kart uses this to determine where you start on the map - players[p].score = READUINT32(demo_p); - - // Power Levels - clientpowerlevels[p][G_BattleGametype() ? PWRLV_BATTLE : PWRLV_RACE] = READUINT16(demo_p); - - // Kart stats, temporarily - kartspeed[p] = READUINT8(demo_p); - kartweight[p] = READUINT8(demo_p); - - if (stricmp(skins[players[p].skin].name, skin) != 0) - FindClosestSkinForStats(p, kartspeed[p], kartweight[p]); - - // Look for the next player - p = READUINT8(demo_p); - } - -// end of player read (the 0xFF marker) -// so this is where we are to read our lua variables (if possible!) -#ifdef HAVE_BLUA - if (demoflags & DF_LUAVARS) // again, used for compability, lua shit will be saved to replays regardless of if it's even been loaded - { - if (!gL) // No Lua state! ...I guess we'll just start one... - LUA_ClearState(); - - LUA_UnArchiveDemo(); - } -#endif - - splitscreen = 0; - - if (demo.title) - { - splitscreen = M_RandomKey(6)-1; - splitscreen = min(min(3, numslots-1), splitscreen); // Bias toward 1p and 4p views - - for (p = 0; p <= splitscreen; p++) - G_ResetView(p+1, slots[M_RandomKey(numslots)], false); - } - - R_ExecuteSetViewSize(); - - P_SetRandSeed(randseed); - G_InitNew(demoflags & DF_ENCORE, G_BuildMapName(gamemap), true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer. - - for (i = 0; i < MAXPLAYERS; i++) - { - if (players[i].mo) - { - players[i].mo->color = players[i].skincolor; - oldghost[i].x = players[i].mo->x; - oldghost[i].y = players[i].mo->y; - oldghost[i].z = players[i].mo->z; - } - - // Set saved attribute values - // No cheat checking here, because even if they ARE wrong... - // it would only break the replay if we clipped them. - players[i].kartspeed = kartspeed[i]; - players[i].kartweight = kartweight[i]; - } - - demo.deferstart = true; -} -#undef SKIPERRORS - -void G_AddGhost(char *defdemoname) -{ - INT32 i; - lumpnum_t l; - char name[17],skin[17],color[17],*n,*pdemoname,md5[16]; - demoghost *gh; - UINT8 flags; - UINT8 *buffer,*p; - mapthing_t *mthing; - UINT16 count, ghostversion; - skin_t *ghskin = &skins[0]; - UINT8 kartspeed = UINT8_MAX, kartweight = UINT8_MAX; - - name[16] = '\0'; - skin[16] = '\0'; - color[16] = '\0'; - - n = defdemoname+strlen(defdemoname); - while (*n != '/' && *n != '\\' && n != defdemoname) - n--; - if (n != defdemoname) - n++; - pdemoname = ZZ_Alloc(strlen(n)+1); - strcpy(pdemoname,n); - - // Internal if no extension, external if one exists - if (FIL_CheckExtension(defdemoname)) - { - //FIL_DefaultExtension(defdemoname, ".lmp"); - if (!FIL_ReadFileTag(defdemoname, &buffer, PU_LEVEL)) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), defdemoname); - Z_Free(pdemoname); - return; - } - p = buffer; - } - // load demo resource from WAD - else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to read lump '%s'.\n"), defdemoname); - Z_Free(pdemoname); - return; - } - else // it's an internal demo - buffer = p = W_CacheLumpNum(l, PU_LEVEL); - - // read demo header - if (memcmp(p, DEMOHEADER, 12)) - { - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Not a SRB2Kart replay.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - p += 12; // DEMOHEADER - p++; // VERSION - p++; // SUBVERSION - - ghostversion = READUINT16(p); - switch(ghostversion) - { - case DEMOVERSION: // latest always supported - p += 64; // title - break; - // too old, cannot support. - default: - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo version incompatible.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - M_Memcpy(md5, p, 16); p += 16; // demo checksum - for (gh = ghosts; gh; gh = gh->next) - if (!memcmp(md5, gh->checksum, 16)) // another ghost in the game already has this checksum? - { // Don't add another one, then! - CONS_Debug(DBG_SETUP, "Rejecting duplicate ghost %s (MD5 was matched)\n", pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - if (memcmp(p, "PLAY", 4)) - { - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo format unacceptable.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - p += 4; // "PLAY" - p += 2; // gamemap - p += 16; // mapmd5 (possibly check for consistency?) - - flags = READUINT8(p); - - if (!(flags & DF_GHOST)) - { - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: No ghost data in this demo.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - p++; // gametype - G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts. - - switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - p += 8; // demo time, lap - break; - case ATTACKING_CAPSULES: // 2 - p += 4; // demo time - break; - default: // 3 - break; - } - - p += 4; // random seed - p += 4; // Extra data location reference - - // net var data - count = READUINT16(p); - while (count--) - { - p += 2; - SKIPSTRING(p); - p++; - } - - if (*p == DEMOMARKER) - { - CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Replay is empty.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - if (READUINT8(p) != 0) - { - CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - // Player name (TODO: Display this somehow if it doesn't match cv_playername!) - M_Memcpy(name, p, 16); - p += 16; - - // Skin - M_Memcpy(skin, p, 16); - p += 16; - - // Color - M_Memcpy(color, p, 16); - p += 16; - - p += 4; // score - p += 2; // powerlevel - - kartspeed = READUINT8(p); - kartweight = READUINT8(p); - - if (READUINT8(p) != 0xFF) - { - CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); - Z_Free(pdemoname); - Z_Free(buffer); - return; - } - - for (i = 0; i < numskins; i++) - if (!stricmp(skins[i].name,skin)) - { - ghskin = &skins[i]; - break; - } - - if (i == numskins) - { - if (kartspeed != UINT8_MAX && kartweight != UINT8_MAX) - ghskin = &skins[GetSkinNumClosestToStats(kartspeed, kartweight)]; - - CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Invalid character. Falling back to %s.\n"), pdemoname, ghskin->name); - } - - gh = Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL); - gh->next = ghosts; - gh->buffer = buffer; - M_Memcpy(gh->checksum, md5, 16); - gh->p = p; - - ghosts = gh; - - gh->version = ghostversion; - mthing = playerstarts[0]; - I_Assert(mthing); - { // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling. - fixed_t z,f,c; - gh->mo = P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST); - gh->mo->angle = FixedAngle(mthing->angle*FRACUNIT); - f = gh->mo->floorz; - c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height; - if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP)) - { - z = c; - if (mthing->options >> ZSHIFT) - z -= ((mthing->options >> ZSHIFT) << FRACBITS); - if (z < f) - z = f; - } - else - { - z = f; - if (mthing->options >> ZSHIFT) - z += ((mthing->options >> ZSHIFT) << FRACBITS); - if (z > c) - z = c; - } - gh->mo->z = z; - } - gh->mo->state = states+S_KART_STILL1; // SRB2kart - was S_PLAY_STND - gh->mo->sprite = gh->mo->state->sprite; - gh->mo->frame = (gh->mo->state->frame & FF_FRAMEMASK) | tr_trans20<mo->tics = -1; - - gh->oldmo.x = gh->mo->x; - gh->oldmo.y = gh->mo->y; - gh->oldmo.z = gh->mo->z; - - // Set skin - gh->mo->skin = gh->oldmo.skin = ghskin; - - // Set color - gh->mo->color = ((skin_t*)gh->mo->skin)->prefcolor; - for (i = 0; i < MAXSKINCOLORS; i++) - if (!stricmp(KartColor_Names[i],color)) // SRB2kart - { - gh->mo->color = (UINT8)i; - break; - } - gh->oldmo.color = gh->mo->color; - - CONS_Printf(M_GetText("Added ghost %s from %s\n"), name, pdemoname); - Z_Free(pdemoname); -} - -// A simplified version of G_AddGhost... -void G_UpdateStaffGhostName(lumpnum_t l) -{ - UINT8 *buffer,*p; - UINT16 ghostversion; - UINT8 flags; - - buffer = p = W_CacheLumpNum(l, PU_CACHE); - - // read demo header - if (memcmp(p, DEMOHEADER, 12)) - { - goto fail; - } - - p += 12; // DEMOHEADER - p++; // VERSION - p++; // SUBVERSION - - ghostversion = READUINT16(p); - switch(ghostversion) - { - case DEMOVERSION: // latest always supported - p += 64; // full demo title - break; - - // too old, cannot support. - default: - goto fail; - } - - p += 16; // demo checksum - - if (memcmp(p, "PLAY", 4)) - { - goto fail; - } - - p += 4; // "PLAY" - p += 2; // gamemap - p += 16; // mapmd5 (possibly check for consistency?) - - flags = READUINT8(p); - if (!(flags & DF_GHOST)) - { - goto fail; // we don't NEED to do it here, but whatever - } - - p++; // Gametype - - G_SkipDemoExtraFiles(&p); - - switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) - { - case ATTACKING_NONE: // 0 - break; - case ATTACKING_RECORD: // 1 - p += 8; // demo time, lap - break; - case ATTACKING_CAPSULES: // 2 - p += 4; // demo time - break; - default: // 3 - break; - } - - p += 4; // random seed - p += 4; // Extrainfo location marker - - // Ehhhh don't need ghostversion here (?) so I'll reuse the var here - ghostversion = READUINT16(p); - while (ghostversion--) - { - p += 2; - SKIPSTRING(p); - p++; // stealth - } - - // Assert first player is in and then read name - if (READUINT8(p) != 0) - goto fail; - M_Memcpy(dummystaffname, p,16); - dummystaffname[16] = '\0'; - - // Ok, no longer any reason to care, bye -fail: - Z_Free(buffer); - return; -} - -// -// G_TimeDemo -// NOTE: name is a full filename for external demos -// -static INT32 restorecv_vidwait; - -void G_TimeDemo(const char *name) -{ - nodrawers = M_CheckParm("-nodraw"); - noblit = M_CheckParm("-noblit"); - restorecv_vidwait = cv_vidwait.value; - if (cv_vidwait.value) - CV_Set(&cv_vidwait, "0"); - demo.timing = true; - singletics = true; - framecount = 0; - demostarttime = I_GetTime(); - G_DeferedPlayDemo(name); -} - -void G_DoPlayMetal(void) -{ - lumpnum_t l; - mobj_t *mo = NULL; - thinker_t *th; - - // it's an internal demo - if ((l = W_CheckNumForName(va("%sMS",G_BuildMapName(gamemap)))) == LUMPERROR) - { - CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n")); - return; - } - else - metalbuffer = metal_p = W_CacheLumpNum(l, PU_STATIC); - - // find metal sonic - for (th = thinkercap.next; th != &thinkercap; th = th->next) - { - if (th->function.acp1 != (actionf_p1)P_MobjThinker) - continue; - - mo = (mobj_t *)th; - if (mo->type == MT_METALSONIC_RACE) - break; - } - if (!mo) - { - CONS_Alert(CONS_ERROR, M_GetText("Failed to find bot entity.\n")); - Z_Free(metalbuffer); - return; - } - - // read demo header - metal_p += 12; // DEMOHEADER - metal_p++; // VERSION - metal_p++; // SUBVERSION - metalversion = READUINT16(metal_p); - switch(metalversion) - { - case DEMOVERSION: // latest always supported - break; - // too old, cannot support. - default: - CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, format version incompatible.\n")); - Z_Free(metalbuffer); - return; - } - metal_p += 16; // demo checksum - if (memcmp(metal_p, "METL", 4)) - { - CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, wasn't recorded in Metal format.\n")); - Z_Free(metalbuffer); - return; - } metal_p += 4; // "METL" - - // read initial tic - memset(&oldmetal,0,sizeof(oldmetal)); - oldmetal.x = mo->x; - oldmetal.y = mo->y; - oldmetal.z = mo->z; - oldmetal.angle = mo->angle; - metalplayback = mo; -} - -void G_DoneLevelLoad(void) -{ - CONS_Printf(M_GetText("Loaded level in %f sec\n"), (double)(I_GetTime() - demostarttime) / TICRATE); - framecount = 0; - demostarttime = I_GetTime(); -} - -/* -=================== -= -= G_CheckDemoStatus -= -= Called after a death or level completion to allow demos to be cleaned up -= Returns true if a new demo loop action will take place -=================== -*/ - -// Stops metal sonic's demo. Separate from other functions because metal + replays can coexist -void G_StopMetalDemo(void) -{ - - // Metal Sonic finishing doesn't end the game, dammit. - Z_Free(metalbuffer); - metalbuffer = NULL; - metalplayback = NULL; - metal_p = NULL; -} - -// Stops metal sonic recording. -ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(void) -{ - boolean saved = false; - if (demo_p) - { - UINT8 *p = demobuffer+16; // checksum position -#ifdef NOMD5 - UINT8 i; - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - for (i = 0; i < 16; i++, p++) - *p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. -#else - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - md5_buffer((char *)p+16, demo_p - (p+16), (void *)p); // make a checksum of everything after the checksum in the file. -#endif - saved = FIL_WriteFile(va("%sMS.LMP", G_BuildMapName(gamemap)), demobuffer, demo_p - demobuffer); // finally output the file. - } - free(demobuffer); - metalrecording = false; - if (saved) - I_Error("Saved to %sMS.LMP", G_BuildMapName(gamemap)); - I_Error("Failed to save demo!"); -} - -// reset engine variable set for the demos -// called from stopdemo command, map command, and g_checkdemoStatus. -void G_StopDemo(void) -{ - Z_Free(demobuffer); - demobuffer = NULL; - demo.playback = false; - if (demo.title) - modeattacking = false; - demo.title = false; - demo.timing = false; - singletics = false; - - demo.freecam = false; - // reset democam shit too: - democam.cam = NULL; - democam.soundmobj = NULL; - democam.localangle = 0; - democam.localaiming = 0; - democam.turnheld = false; - democam.keyboardlook = false; - - CV_SetValue(&cv_playbackspeed, 1); - demo.rewinding = false; - CL_ClearRewinds(); - - if (gamestate == GS_LEVEL && rendermode != render_none) - { - V_SetPaletteLump("PLAYPAL"); // Reset the palette - R_ReInitColormaps(0, LUMPERROR); - } - if (gamestate == GS_INTERMISSION) - Y_EndIntermission(); // cleanup - if (gamestate == GS_VOTING) - Y_EndVote(); - - G_SetGamestate(GS_NULL); - wipegamestate = GS_NULL; - SV_StopServer(); - SV_ResetServer(); -} - -boolean G_CheckDemoStatus(void) -{ - while (ghosts) - { - demoghost *next = ghosts->next; - Z_Free(ghosts); - ghosts = next; - } - ghosts = NULL; - - // DO NOT end metal sonic demos here - - if (demo.timing) - { - INT32 demotime; - double f1, f2; - demotime = I_GetTime() - demostarttime; - if (!demotime) - return true; - G_StopDemo(); - demo.timing = false; - f1 = (double)demotime; - f2 = (double)framecount*TICRATE; - CONS_Printf(M_GetText("timed %u gametics in %d realtics\n%f seconds, %f avg fps\n"), leveltime,demotime,f1/TICRATE,f2/f1); - if (restorecv_vidwait != cv_vidwait.value) - CV_SetValue(&cv_vidwait, restorecv_vidwait); - D_AdvanceDemo(); - return true; - } - - if (demo.playback) - { - if (demo.quitafterplaying) - I_Quit(); - - if (multiplayer && !demo.title) - G_ExitLevel(); - else - { - G_StopDemo(); - - if (modeattacking) - M_EndModeAttackRun(); - else - D_AdvanceDemo(); - } - - return true; - } - - if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) - { - G_SaveDemo(); - return true; - } - demo.recording = false; - - return false; -} - -void G_SaveDemo(void) -{ - UINT8 *p = demobuffer+16; // after version - UINT32 length; -#ifdef NOMD5 - UINT8 i; -#endif - - // Ensure extrainfo pointer is always available, even if no info is present. - if (demoinfo_p && *(UINT32 *)demoinfo_p == 0) - { - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - *(UINT32 *)demoinfo_p = demo_p - demobuffer; - } - WRITEUINT8(demo_p, DW_END); // Mark end of demo extra data. - - M_Memcpy(p, demo.titlename, 64); // Write demo title here - p += 64; - - if (multiplayer) - { - // Change the demo's name to be a slug of the title - char demo_slug[128]; - char *writepoint; - size_t i, strindex = 0; - boolean dash = true; - - for (i = 0; demo.titlename[i] && i < 127; i++) - { - if ((demo.titlename[i] >= 'a' && demo.titlename[i] <= 'z') || - (demo.titlename[i] >= '0' && demo.titlename[i] <= '9')) - { - demo_slug[strindex] = demo.titlename[i]; - strindex++; - dash = false; - } - else if (demo.titlename[i] >= 'A' && demo.titlename[i] <= 'Z') - { - demo_slug[strindex] = demo.titlename[i] + 'a' - 'A'; - strindex++; - dash = false; - } - else if (!dash) - { - demo_slug[strindex] = '-'; - strindex++; - dash = true; - } - } - - demo_slug[strindex] = 0; - if (dash) demo_slug[strindex-1] = 0; - - writepoint = strstr(demoname, "-") + 1; - demo_slug[128 - (writepoint - demoname) - 4] = 0; - sprintf(writepoint, "%s.lmp", demo_slug); - } - - length = *(UINT32 *)demoinfo_p; - WRITEUINT32(demoinfo_p, length); -#ifdef NOMD5 - for (i = 0; i < 16; i++, p++) - *p = M_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. -#else - // Make a checksum of everything after the checksum in the file up to the end of the standard data. Extrainfo is freely modifiable. - md5_buffer((char *)p+16, (demobuffer + length) - (p+16), p); -#endif - - - if (FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer)) // finally output the file. - demo.savemode = DSM_SAVED; - free(demobuffer); - demo.recording = false; - - if (!modeattacking) - { - if (demo.savemode == DSM_SAVED) - CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); - else - CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname); - } -} - -boolean G_DemoTitleResponder(event_t *ev) -{ - size_t len; - INT32 ch; - - if (ev->type != ev_keydown) - return false; - - ch = (INT32)ev->data1; - - // Only ESC and non-keyboard keys abort connection - if (ch == KEY_ESCAPE) - { - demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING; - return true; - } - - if (ch == KEY_ENTER || ch >= KEY_MOUSE1) - { - demo.savemode = DSM_WILLSAVE; - return true; - } - - if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART]) - || ch == ' ') // Allow spaces, of course - { - len = strlen(demo.titlename); - if (len < 64) - { - demo.titlename[len+1] = 0; - demo.titlename[len] = CON_ShiftChar(ch); - } - } - else if (ch == KEY_BACKSPACE) - { - if (shiftdown) - memset(demo.titlename, 0, sizeof(demo.titlename)); - else - { - len = strlen(demo.titlename); - - if (len > 0) - demo.titlename[len-1] = 0; - } - } - - return true; -======= /* Now detect map number in base 10, which no one asked for. */ newmapnum = strtol(mapname, &p, 10); if (*p == '\0')/* we got it */ @@ -10636,7 +4952,6 @@ boolean G_DemoTitleResponder(event_t *ev) } return newmapnum; ->>>>>>> srb2/next } // diff --git a/src/g_game.h b/src/g_game.h index 407b4dc6b..a22e6e38c 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -32,77 +32,6 @@ extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; extern player_t players[MAXPLAYERS]; extern boolean playeringame[MAXPLAYERS]; -<<<<<<< HEAD -extern UINT8 *demo_p; - -// ====================================== -// DEMO playback/recording related stuff. -// ====================================== - -// demoplaying back and demo recording -extern consvar_t cv_recordmultiplayerdemos, cv_netdemosyncquality; - -// Publicly-accessible demo vars -struct demovars_s { - char titlename[65]; - boolean recording, playback, timing; - UINT16 version; // Current file format of the demo being played - boolean title; // Title Screen demo can be cancelled by any key - boolean rewinding; // Rewind in progress - - boolean loadfiles, ignorefiles; // Demo file loading options - boolean fromtitle; // SRB2Kart: Don't stop the music - boolean inreplayhut; // Go back to replayhut after demos - boolean quitafterplaying; // quit after playing a demo from cmdline - boolean deferstart; // don't start playing demo right away - - tic_t savebutton; // Used to determine when the local player can choose to save the replay while the race is still going - enum { - DSM_NOTSAVING, - DSM_WILLAUTOSAVE, - DSM_TITLEENTRY, - DSM_WILLSAVE, - DSM_SAVED - } savemode; - - boolean freecam; - -}; - -extern struct demovars_s demo; - -typedef enum { - MD_NOTLOADED, - MD_LOADED, - MD_SUBDIR, - MD_OUTDATED, - MD_INVALID -} menudemotype_e; - -typedef struct menudemo_s { - char filepath[256]; - menudemotype_e type; - - char title[65]; // Null-terminated for string prints - UINT16 map; - UINT8 addonstatus; // What do we need to do addon-wise to play this demo? - UINT8 gametype; - SINT8 kartspeed; // Add OR DF_ENCORE for encore mode, idk - UINT8 numlaps; - - struct { - UINT8 ranking; - char name[17]; - UINT8 skin, color; - UINT32 timeorscore; - } standings[MAXPLAYERS]; -} menudemo_t; - - -extern mobj_t *metalplayback; - -======= ->>>>>>> srb2/next // gametic at level start extern tic_t levelstarttic; @@ -111,28 +40,31 @@ extern INT16 prevmap, nextmap; extern INT32 gameovertics; extern UINT8 ammoremovaltics; extern tic_t timeinmap; // Ticker for time spent in level (used for levelcard display) -extern INT16 rw_maximums[NUM_WEAPONS]; extern INT32 pausedelay; extern boolean pausebreakkey; extern boolean promptactive; + +extern consvar_t cv_tutorialprompt; + +extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection; +extern consvar_t cv_songcredits; + extern consvar_t cv_pauseifunfocused; -// used in game menu -<<<<<<< HEAD -extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection/*, cv_compactscoreboard*/; -extern consvar_t cv_songcredits; -extern consvar_t cv_pauseifunfocused; -//extern consvar_t cv_crosshair, cv_crosshair2, cv_crosshair3, cv_crosshair4; -extern consvar_t cv_invertmouse/*, cv_alwaysfreelook, cv_chasefreelook, cv_mousemove*/; -extern consvar_t cv_invertmouse2/*, cv_alwaysfreelook2, cv_chasefreelook2, cv_mousemove2*/; -extern consvar_t cv_useranalog, cv_useranalog2, cv_useranalog3, cv_useranalog4; -extern consvar_t cv_analog, cv_analog2, cv_analog3, cv_analog4; -extern consvar_t cv_turnaxis,cv_moveaxis,cv_brakeaxis,cv_aimaxis,cv_lookaxis,cv_fireaxis,cv_driftaxis,cv_deadzone; -extern consvar_t cv_turnaxis2,cv_moveaxis2,cv_brakeaxis2,cv_aimaxis2,cv_lookaxis2,cv_fireaxis2,cv_driftaxis2,cv_deadzone2; -extern consvar_t cv_turnaxis3,cv_moveaxis3,cv_brakeaxis3,cv_aimaxis3,cv_lookaxis3,cv_fireaxis3,cv_driftaxis3,cv_deadzone3; -extern consvar_t cv_turnaxis4,cv_moveaxis4,cv_brakeaxis4,cv_aimaxis4,cv_lookaxis4,cv_fireaxis4,cv_driftaxis4,cv_deadzone4; +extern consvar_t cv_invertmouse; + +extern consvar_t cv_turnaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_moveaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_brakeaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_aimaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_lookaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_fireaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_driftaxis[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_digitaldeadzone[MAXSPLITSCREENPLAYERS]; + extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff; extern consvar_t cv_invincmusicfade; @@ -142,57 +74,6 @@ extern consvar_t cv_resetspecialmusic; extern consvar_t cv_resume; -typedef enum -{ - AXISNONE = 0, - AXISTURN, - AXISMOVE, - AXISBRAKE, - AXISAIM, - AXISLOOK, - AXISDEAD, //Axises that don't want deadzones - AXISFIRE, - AXISDRIFT, - AXISLOOKBACK, -} axis_input_e; -======= -extern consvar_t cv_tutorialprompt; -extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection, cv_compactscoreboard; -extern consvar_t cv_crosshair, cv_crosshair2; -extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_chasefreelook, cv_mousemove; -extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_chasefreelook2, cv_mousemove2; - -extern consvar_t cv_useranalog[2], cv_analog[2]; -extern consvar_t cv_directionchar[2]; - -typedef enum { - CS_LEGACY, - CS_LMAOGALOG, - CS_STANDARD, - CS_SIMPLE = CS_LMAOGALOG|CS_STANDARD, -} controlstyle_e; -#define G_ControlStyle(ssplayer) (cv_directionchar[(ssplayer)-1].value == 3 ? CS_LMAOGALOG : ((cv_analog[(ssplayer)-1].value ? CS_LMAOGALOG : 0) | (cv_directionchar[(ssplayer)-1].value ? CS_STANDARD : 0))) -#define P_ControlStyle(player) ((((player)->pflags & PF_ANALOGMODE) ? CS_LMAOGALOG : 0) | (((player)->pflags & PF_DIRECTIONCHAR) ? CS_STANDARD : 0)) - -extern consvar_t cv_autobrake, cv_autobrake2; -extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_jumpaxis,cv_spinaxis,cv_fireaxis,cv_firenaxis,cv_deadzone,cv_digitaldeadzone; -extern consvar_t cv_sideaxis2,cv_turnaxis2,cv_moveaxis2,cv_lookaxis2,cv_jumpaxis2,cv_spinaxis2,cv_fireaxis2,cv_firenaxis2,cv_deadzone2,cv_digitaldeadzone2; -extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest; ->>>>>>> srb2/next - -// hi here's some new controls -extern consvar_t cv_cam_shiftfacing[2], cv_cam_turnfacing[2], - cv_cam_turnfacingability[2], cv_cam_turnfacingspindash[2], cv_cam_turnfacinginput[2], - cv_cam_centertoggle[2], cv_cam_lockedinput[2], cv_cam_lockonboss[2]; - -typedef enum -{ - LOCK_BOSS = 1<<0, - LOCK_ENEMY = 1<<1, - LOCK_INTERESTS = 1<<2, -} lockassist_e; - - // mouseaiming (looking up/down with the mouse or keyboard) #define KB_LOOKSPEED (1<<25) #define MAXPLMOVE (50) @@ -200,12 +81,7 @@ typedef enum // build an internal map name MAPxx from map number const char *G_BuildMapName(INT32 map); -<<<<<<< HEAD -======= -extern boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player -extern mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object? ->>>>>>> srb2/next void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer); // copy ticcmd_t to and fro the normal way @@ -228,15 +104,9 @@ extern INT32 localaiming[MAXSPLITSCREENPLAYERS]; // should be an angle_t but sig // void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo); void G_DoReborn(INT32 playernum); -<<<<<<< HEAD -void G_PlayerReborn(INT32 player); -void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, - boolean skipprecutscene); -======= void G_PlayerReborn(INT32 player, boolean betweenmaps); -void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, +void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS); ->>>>>>> srb2/next char *G_BuildMapTitle(INT32 mapnum); struct searchdim @@ -266,30 +136,19 @@ INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep); // XMOD spawning mapthing_t *G_FindCTFStart(INT32 playernum); mapthing_t *G_FindMatchStart(INT32 playernum); -<<<<<<< HEAD mapthing_t *G_FindRaceStart(INT32 playernum); -void G_SpawnPlayer(INT32 playernum, boolean starpost); -======= -mapthing_t *G_FindCoopStart(INT32 playernum); -mapthing_t *G_FindMapStart(INT32 playernum); void G_MovePlayerToSpawnOrStarpost(INT32 playernum); void G_SpawnPlayer(INT32 playernum); ->>>>>>> srb2/next // Can be called by the startup code or M_Responder. // A normal game starts at map 1, but a warp test can start elsewhere void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, UINT8 ssplayers, boolean FLS); void G_DoLoadLevel(boolean resetplayer); -<<<<<<< HEAD -void G_LoadDemoInfo(menudemo_t *pdemo); -void G_DeferedPlayDemo(const char *demo); -======= void G_StartTitleCard(void); void G_PreLevelTitleCard(void); boolean G_IsTitleCardAvailable(void); ->>>>>>> srb2/next // Can be called by the startup code or M_Responder, calls P_SetupLevel. void G_LoadGame(UINT32 slot, INT16 mapoverride); @@ -300,91 +159,6 @@ void G_SaveGame(UINT32 slot); void G_SaveGameOver(UINT32 slot, boolean modifylives); -<<<<<<< HEAD -// Only called by shutdown code. -void G_WriteStanding(UINT8 ranking, char *name, INT32 skinnum, UINT8 color, UINT32 val); -void G_SetDemoTime(UINT32 ptime, UINT32 plap); -UINT8 G_CmpDemoTime(char *oldname, char *newname); - -typedef enum -{ - GHC_NORMAL = 0, - GHC_SUPER, - GHC_FIREFLOWER, - GHC_INVINCIBLE -} ghostcolor_t; - -extern UINT8 demo_extradata[MAXPLAYERS]; -extern UINT8 demo_writerng; -#define DXD_RESPAWN 0x01 // "respawn" command in console -#define DXD_SKIN 0x02 // skin changed -#define DXD_NAME 0x04 // name changed -#define DXD_COLOR 0x08 // color changed -#define DXD_PLAYSTATE 0x10 // state changed between playing, spectating, or not in-game - -#define DXD_PST_PLAYING 0x01 -#define DXD_PST_SPECTATING 0x02 -#define DXD_PST_LEFT 0x03 - -// Record/playback tics -void G_ReadDemoExtraData(void); -void G_WriteDemoExtraData(void); -void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum); -void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum); -void G_GhostAddThok(INT32 playernum); -void G_GhostAddSpin(INT32 playernum); -void G_GhostAddRev(INT32 playernum); -void G_GhostAddColor(INT32 playernum, ghostcolor_t color); -void G_GhostAddFlip(INT32 playernum); -void G_GhostAddScale(INT32 playernum, fixed_t scale); -void G_GhostAddHit(INT32 playernum, mobj_t *victim); -void G_WriteAllGhostTics(void); -void G_WriteGhostTic(mobj_t *ghost, INT32 playernum); -void G_ConsAllGhostTics(void); -void G_ConsGhostTic(INT32 playernum); -void G_GhostTicker(void); - -void G_InitDemoRewind(void); -void G_StoreRewindInfo(void); -void G_PreviewRewind(tic_t previewtime); -void G_ConfirmRewind(tic_t rewindtime); - -void G_ReadMetalTic(mobj_t *metal); -void G_WriteMetalTic(mobj_t *metal); -void G_SaveMetal(UINT8 **buffer); -void G_LoadMetal(UINT8 **buffer); - -// Your naming conventions are stupid and useless. -// There is no conflict here. -typedef struct demoghost { - UINT8 checksum[16]; - UINT8 *buffer, *p, color; - UINT16 version; - mobj_t oldmo, *mo; - struct demoghost *next; -} demoghost; -extern demoghost *ghosts; - -// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's. -#define DFILE_ERROR_NOTLOADED 0x01 // Files are not loaded, but can be without a restart. -#define DFILE_ERROR_OUTOFORDER 0x02 // Files are loaded, but out of order. -#define DFILE_ERROR_INCOMPLETEOUTOFORDER 0x03 // Some files are loaded out of order, but others are not. -#define DFILE_ERROR_CANNOTLOAD 0x04 // Files are missing and cannot be loaded. -#define DFILE_ERROR_EXTRAFILES 0x05 // Extra files outside of the replay's file list are loaded. - -void G_DoPlayDemo(char *defdemoname); -void G_TimeDemo(const char *name); -void G_AddGhost(char *defdemoname); -void G_UpdateStaffGhostName(lumpnum_t l); -void G_DoPlayMetal(void); -void G_DoneLevelLoad(void); -void G_StopMetalDemo(void); -ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(void); -void G_StopDemo(void); -boolean G_CheckDemoStatus(void); -void G_SaveDemo(void); -boolean G_DemoTitleResponder(event_t *ev); -======= extern UINT32 gametypedefaultrules[NUMGAMETYPES]; extern UINT32 gametypetol[NUMGAMETYPES]; extern INT16 gametyperankings[NUMGAMETYPES]; @@ -396,7 +170,6 @@ void G_UpdateGametypeSelections(void); void G_AddTOL(UINT32 newtol, const char *tolname); void G_AddGametypeTOL(INT16 gtype, UINT32 newtol); void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolor, UINT8 rightcolor); ->>>>>>> srb2/next INT32 G_GetGametypeByName(const char *gametypestr); boolean G_IsSpecialStage(INT32 mapnum); @@ -459,19 +232,9 @@ void G_SetGamestate(gamestate_t newstate); // Gamedata record shit void G_AllocMainRecordData(INT16 i); -//void G_AllocNightsRecordData(INT16 i); void G_ClearRecords(void); -//UINT32 G_GetBestScore(INT16 map); tic_t G_GetBestTime(INT16 map); -//tic_t G_GetBestLap(INT16 map); -//UINT16 G_GetBestRings(INT16 map); -//UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare); -//tic_t G_GetBestNightsTime(INT16 map, UINT8 mare); -//UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare); - -//void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare); -//void G_SetNightsRecords(void); FUNCMATH INT32 G_TicsToHours(tic_t tics); FUNCMATH INT32 G_TicsToMinutes(tic_t tics, boolean full);