2020-05-28 11:29:42 +00:00
|
|
|
#include "save.h"
|
|
|
|
|
2014-07-26 12:46:31 +00:00
|
|
|
#include <cstdio>
|
2020-09-26 19:41:58 +00:00
|
|
|
#include <new>
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2021-01-09 14:18:52 +00:00
|
|
|
#include "entities/character.h"
|
2020-05-28 11:29:42 +00:00
|
|
|
#include "gamemodes/DDRace.h"
|
2021-01-09 14:37:02 +00:00
|
|
|
#include "player.h"
|
2020-09-26 19:41:58 +00:00
|
|
|
#include "teams.h"
|
2014-07-28 13:43:47 +00:00
|
|
|
#include <engine/shared/config.h>
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2022-02-14 23:32:04 +00:00
|
|
|
CSaveTee::CSaveTee() = default;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2021-02-16 17:15:50 +00:00
|
|
|
void CSaveTee::Save(CCharacter *pChr)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-06-03 20:08:21 +00:00
|
|
|
m_ClientID = pChr->m_pPlayer->GetCID();
|
2021-01-11 22:22:29 +00:00
|
|
|
str_copy(m_aName, pChr->Server()->ClientName(m_ClientID), sizeof(m_aName));
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
m_Alive = pChr->m_Alive;
|
2017-05-16 15:02:28 +00:00
|
|
|
m_Paused = abs(pChr->m_pPlayer->IsPaused());
|
2017-03-21 10:24:44 +00:00
|
|
|
m_NeededFaketuning = pChr->m_NeededFaketuning;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2021-07-20 20:16:40 +00:00
|
|
|
m_TeeStarted = pChr->Teams()->TeeStarted(m_ClientID);
|
2020-06-03 20:08:21 +00:00
|
|
|
m_TeeFinished = pChr->Teams()->TeeFinished(m_ClientID);
|
2019-04-23 16:43:24 +00:00
|
|
|
m_IsSolo = pChr->m_Solo;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int i = 0; i < NUM_WEAPONS; i++)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2022-03-21 22:50:41 +00:00
|
|
|
m_aWeapons[i].m_AmmoRegenStart = pChr->m_Core.m_aWeapons[i].m_AmmoRegenStart;
|
|
|
|
m_aWeapons[i].m_Ammo = pChr->m_Core.m_aWeapons[i].m_Ammo;
|
|
|
|
m_aWeapons[i].m_Ammocost = pChr->m_Core.m_aWeapons[i].m_Ammocost;
|
|
|
|
m_aWeapons[i].m_Got = pChr->m_Core.m_aWeapons[i].m_Got;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
m_LastWeapon = pChr->m_LastWeapon;
|
|
|
|
m_QueuedWeapon = pChr->m_QueuedWeapon;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
m_SuperJump = pChr->m_SuperJump;
|
|
|
|
m_Jetpack = pChr->m_Jetpack;
|
|
|
|
m_NinjaJetpack = pChr->m_NinjaJetpack;
|
|
|
|
m_FreezeTime = pChr->m_FreezeTime;
|
|
|
|
m_FreezeTick = pChr->Server()->Tick() - pChr->m_FreezeTick;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
m_DeepFreeze = pChr->m_DeepFreeze;
|
2022-01-07 15:53:40 +00:00
|
|
|
m_LiveFreeze = pChr->m_LiveFreeze;
|
2017-03-21 10:24:44 +00:00
|
|
|
m_EndlessHook = pChr->m_EndlessHook;
|
|
|
|
m_DDRaceState = pChr->m_DDRaceState;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
m_Hit = pChr->m_Hit;
|
|
|
|
m_TuneZone = pChr->m_TuneZone;
|
|
|
|
m_TuneZoneOld = pChr->m_TuneZoneOld;
|
2021-03-16 22:33:48 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
if(pChr->m_StartTime)
|
2021-03-20 16:08:54 +00:00
|
|
|
m_Time = pChr->Server()->Tick() - pChr->m_StartTime + g_Config.m_SvSaveSwapGamesPenalty * pChr->Server()->TickSpeed();
|
2020-06-02 14:27:31 +00:00
|
|
|
else
|
|
|
|
m_Time = 0;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
m_Pos = pChr->m_Pos;
|
|
|
|
m_PrevPos = pChr->m_PrevPos;
|
|
|
|
m_TeleCheckpoint = pChr->m_TeleCheckpoint;
|
|
|
|
m_LastPenalty = pChr->m_LastPenalty;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
if(pChr->m_CpTick)
|
|
|
|
m_CpTime = pChr->Server()->Tick() - pChr->m_CpTick;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
m_CpActive = pChr->m_CpActive;
|
|
|
|
m_CpLastBroadcast = pChr->m_CpLastBroadcast;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2014-10-28 09:07:21 +00:00
|
|
|
for(int i = 0; i < 25; i++)
|
2020-05-22 11:58:37 +00:00
|
|
|
m_aCpCurrent[i] = pChr->m_CpCurrent[i];
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2018-12-17 13:55:58 +00:00
|
|
|
m_NotEligibleForFinish = pChr->m_pPlayer->m_NotEligibleForFinish;
|
|
|
|
|
2019-08-19 08:09:53 +00:00
|
|
|
m_HasTelegunGun = pChr->m_Core.m_HasTelegunGun;
|
|
|
|
m_HasTelegunGrenade = pChr->m_Core.m_HasTelegunGrenade;
|
|
|
|
m_HasTelegunLaser = pChr->m_Core.m_HasTelegunLaser;
|
2019-04-06 16:13:11 +00:00
|
|
|
|
2014-07-26 12:46:31 +00:00
|
|
|
// Core
|
2017-03-21 10:24:44 +00:00
|
|
|
m_CorePos = pChr->m_Core.m_Pos;
|
|
|
|
m_Vel = pChr->m_Core.m_Vel;
|
|
|
|
m_Hook = pChr->m_Core.m_Hook;
|
|
|
|
m_Collision = pChr->m_Core.m_Collision;
|
|
|
|
m_ActiveWeapon = pChr->m_Core.m_ActiveWeapon;
|
|
|
|
m_Jumped = pChr->m_Core.m_Jumped;
|
|
|
|
m_JumpedTotal = pChr->m_Core.m_JumpedTotal;
|
|
|
|
m_Jumps = pChr->m_Core.m_Jumps;
|
|
|
|
m_HookPos = pChr->m_Core.m_HookPos;
|
|
|
|
m_HookDir = pChr->m_Core.m_HookDir;
|
|
|
|
m_HookTeleBase = pChr->m_Core.m_HookTeleBase;
|
|
|
|
|
|
|
|
m_HookTick = pChr->m_Core.m_HookTick;
|
|
|
|
|
|
|
|
m_HookState = pChr->m_Core.m_HookState;
|
2020-06-29 20:28:26 +00:00
|
|
|
m_HookedPlayer = pChr->m_Core.m_HookedPlayer;
|
|
|
|
m_NewHook = pChr->m_Core.m_NewHook != 0;
|
|
|
|
|
|
|
|
m_InputDirection = pChr->m_SavedInput.m_Direction;
|
|
|
|
m_InputJump = pChr->m_SavedInput.m_Jump;
|
|
|
|
m_InputFire = pChr->m_SavedInput.m_Fire;
|
|
|
|
m_InputHook = pChr->m_SavedInput.m_Hook;
|
2019-04-06 16:37:25 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
m_ReloadTimer = pChr->m_ReloadTimer;
|
2020-06-30 21:49:40 +00:00
|
|
|
|
2020-05-20 20:01:47 +00:00
|
|
|
FormatUuid(pChr->GameServer()->GameUuid(), m_aGameUuid, sizeof(m_aGameUuid));
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2021-08-21 08:02:45 +00:00
|
|
|
void CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2017-04-08 23:16:48 +00:00
|
|
|
pChr->m_pPlayer->Pause(m_Paused, true);
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_Alive = m_Alive;
|
|
|
|
pChr->m_NeededFaketuning = m_NeededFaketuning;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2021-08-21 08:02:45 +00:00
|
|
|
if(!IsSwap)
|
|
|
|
{
|
|
|
|
pChr->Teams()->SetForceCharacterTeam(pChr->m_pPlayer->GetCID(), Team);
|
|
|
|
pChr->Teams()->SetStarted(pChr->m_pPlayer->GetCID(), m_TeeStarted);
|
|
|
|
pChr->Teams()->SetFinished(pChr->m_pPlayer->GetCID(), m_TeeFinished);
|
|
|
|
}
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int i = 0; i < NUM_WEAPONS; i++)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2022-03-21 22:50:41 +00:00
|
|
|
pChr->m_Core.m_aWeapons[i].m_AmmoRegenStart = m_aWeapons[i].m_AmmoRegenStart;
|
2020-05-01 21:39:04 +00:00
|
|
|
// m_Ammo not used anymore for tracking freeze following https://github.com/ddnet/ddnet/pull/2086
|
2022-03-21 22:50:41 +00:00
|
|
|
pChr->m_Core.m_aWeapons[i].m_Ammo = -1;
|
|
|
|
pChr->m_Core.m_aWeapons[i].m_Ammocost = m_aWeapons[i].m_Ammocost;
|
|
|
|
pChr->m_Core.m_aWeapons[i].m_Got = m_aWeapons[i].m_Got;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_LastWeapon = m_LastWeapon;
|
|
|
|
pChr->m_QueuedWeapon = m_QueuedWeapon;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_SuperJump = m_SuperJump;
|
|
|
|
pChr->m_Jetpack = m_Jetpack;
|
|
|
|
pChr->m_NinjaJetpack = m_NinjaJetpack;
|
|
|
|
pChr->m_FreezeTime = m_FreezeTime;
|
|
|
|
pChr->m_FreezeTick = pChr->Server()->Tick() - m_FreezeTick;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_DeepFreeze = m_DeepFreeze;
|
2022-01-07 15:53:40 +00:00
|
|
|
pChr->m_LiveFreeze = m_LiveFreeze;
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_EndlessHook = m_EndlessHook;
|
|
|
|
pChr->m_DDRaceState = m_DDRaceState;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_Hit = m_Hit;
|
|
|
|
pChr->m_TuneZone = m_TuneZone;
|
|
|
|
pChr->m_TuneZoneOld = m_TuneZoneOld;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
|
|
|
if(m_Time)
|
2021-03-20 16:08:54 +00:00
|
|
|
pChr->m_StartTime = pChr->Server()->Tick() - m_Time;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_Pos = m_Pos;
|
|
|
|
pChr->m_PrevPos = m_PrevPos;
|
|
|
|
pChr->m_TeleCheckpoint = m_TeleCheckpoint;
|
|
|
|
pChr->m_LastPenalty = m_LastPenalty;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
|
|
|
if(m_CpTime)
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_CpTick = pChr->Server()->Tick() - m_CpTime;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
pChr->m_CpActive = m_CpActive;
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_CpLastBroadcast = m_CpLastBroadcast;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2014-10-28 09:07:21 +00:00
|
|
|
for(int i = 0; i < 25; i++)
|
2020-05-22 11:58:37 +00:00
|
|
|
pChr->m_CpCurrent[i] = m_aCpCurrent[i];
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2018-12-17 13:55:58 +00:00
|
|
|
pChr->m_pPlayer->m_NotEligibleForFinish = pChr->m_pPlayer->m_NotEligibleForFinish || m_NotEligibleForFinish;
|
|
|
|
|
2019-08-19 08:09:53 +00:00
|
|
|
pChr->m_Core.m_HasTelegunGun = m_HasTelegunGun;
|
|
|
|
pChr->m_Core.m_HasTelegunLaser = m_HasTelegunLaser;
|
|
|
|
pChr->m_Core.m_HasTelegunGrenade = m_HasTelegunGrenade;
|
2019-04-06 16:13:11 +00:00
|
|
|
|
2014-07-26 12:46:31 +00:00
|
|
|
// Core
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_Core.m_Pos = m_CorePos;
|
|
|
|
pChr->m_Core.m_Vel = m_Vel;
|
|
|
|
pChr->m_Core.m_Hook = m_Hook;
|
|
|
|
pChr->m_Core.m_Collision = m_Collision;
|
|
|
|
pChr->m_Core.m_ActiveWeapon = m_ActiveWeapon;
|
2017-05-24 18:23:54 +00:00
|
|
|
pChr->m_Core.m_Jumped = m_Jumped;
|
2017-03-21 10:24:44 +00:00
|
|
|
pChr->m_Core.m_JumpedTotal = m_JumpedTotal;
|
|
|
|
pChr->m_Core.m_Jumps = m_Jumps;
|
|
|
|
pChr->m_Core.m_HookPos = m_HookPos;
|
|
|
|
pChr->m_Core.m_HookDir = m_HookDir;
|
|
|
|
pChr->m_Core.m_HookTeleBase = m_HookTeleBase;
|
|
|
|
|
|
|
|
pChr->m_Core.m_HookTick = m_HookTick;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-06-29 20:28:26 +00:00
|
|
|
pChr->m_Core.m_HookState = m_HookState;
|
2020-06-30 18:13:45 +00:00
|
|
|
if(m_HookedPlayer != -1 && pChr->Teams()->m_Core.Team(m_HookedPlayer) != Team)
|
|
|
|
{
|
|
|
|
pChr->m_Core.m_HookedPlayer = -1;
|
|
|
|
pChr->m_Core.m_HookState = HOOK_FLYING;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pChr->m_Core.m_HookedPlayer = m_HookedPlayer;
|
|
|
|
}
|
2020-06-29 20:28:26 +00:00
|
|
|
pChr->m_Core.m_NewHook = m_NewHook;
|
|
|
|
pChr->m_SavedInput.m_Direction = m_InputDirection;
|
|
|
|
pChr->m_SavedInput.m_Jump = m_InputJump;
|
|
|
|
pChr->m_SavedInput.m_Fire = m_InputFire;
|
|
|
|
pChr->m_SavedInput.m_Hook = m_InputHook;
|
|
|
|
|
2020-06-30 21:49:40 +00:00
|
|
|
pChr->m_ReloadTimer = m_ReloadTimer;
|
2014-07-26 15:56:51 +00:00
|
|
|
|
2019-04-23 16:43:24 +00:00
|
|
|
pChr->SetSolo(m_IsSolo);
|
2020-06-24 19:19:46 +00:00
|
|
|
|
2022-01-09 21:13:27 +00:00
|
|
|
if(!IsSwap)
|
|
|
|
{
|
|
|
|
// Always create a rescue tee at the exact location we loaded from so that
|
|
|
|
// the old one gets overwritten.
|
|
|
|
pChr->SetRescue();
|
|
|
|
}
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
char *CSaveTee::GetString(const CSaveTeam *pTeam)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-06-29 20:28:26 +00:00
|
|
|
int HookedPlayer = -1;
|
|
|
|
if(m_HookedPlayer != -1)
|
|
|
|
{
|
|
|
|
for(int n = 0; n < pTeam->GetMembersCount(); n++)
|
|
|
|
{
|
|
|
|
if(m_HookedPlayer == pTeam->m_pSavedTees[n].GetClientID())
|
|
|
|
{
|
|
|
|
HookedPlayer = n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 11:58:37 +00:00
|
|
|
str_format(m_aString, sizeof(m_aString),
|
2020-09-26 19:41:58 +00:00
|
|
|
"%s\t%d\t%d\t%d\t%d\t%d\t"
|
|
|
|
// weapons
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t"
|
|
|
|
// tee stats
|
2022-01-09 17:39:13 +00:00
|
|
|
"%d\t%d\t%d\t%d\t%d\t%d\t%d\t" // m_SuperJump
|
2020-09-26 19:41:58 +00:00
|
|
|
"%d\t%d\t%d\t%d\t%d\t%d\t%d\t" // m_DDRaceState
|
|
|
|
"%d\t%d\t%d\t%d\t" // m_Pos.x
|
|
|
|
"%d\t%d\t" // m_TeleCheckpoint
|
|
|
|
"%d\t%d\t%f\t%f\t" // m_CorePos.x
|
|
|
|
"%d\t%d\t%d\t%d\t" // m_ActiveWeapon
|
|
|
|
"%d\t%d\t%f\t%f\t" // m_HookPos.x
|
|
|
|
"%d\t%d\t%d\t%d\t" // m_HookTeleBase.x
|
|
|
|
// time checkpoints
|
|
|
|
"%d\t%d\t%d\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
2021-07-20 20:16:40 +00:00
|
|
|
"%d\t" // m_NotEligibleForFinish
|
|
|
|
"%d\t%d\t%d\t" // tele weapons
|
|
|
|
"%s\t" // m_aGameUuid
|
2021-07-22 23:17:06 +00:00
|
|
|
"%d\t%d\t" // m_HookedPlayer, m_NewHook
|
2021-07-20 20:16:40 +00:00
|
|
|
"%d\t%d\t%d\t%d\t" // input stuff
|
|
|
|
"%d\t" // m_ReloadTimer
|
2022-01-10 19:31:43 +00:00
|
|
|
"%d\t" // m_TeeStarted
|
2022-01-09 17:39:13 +00:00
|
|
|
"%d", //m_LiveFreeze
|
2020-09-26 19:41:58 +00:00
|
|
|
m_aName, m_Alive, m_Paused, m_NeededFaketuning, m_TeeFinished, m_IsSolo,
|
|
|
|
// weapons
|
|
|
|
m_aWeapons[0].m_AmmoRegenStart, m_aWeapons[0].m_Ammo, m_aWeapons[0].m_Ammocost, m_aWeapons[0].m_Got,
|
|
|
|
m_aWeapons[1].m_AmmoRegenStart, m_aWeapons[1].m_Ammo, m_aWeapons[1].m_Ammocost, m_aWeapons[1].m_Got,
|
|
|
|
m_aWeapons[2].m_AmmoRegenStart, m_aWeapons[2].m_Ammo, m_aWeapons[2].m_Ammocost, m_aWeapons[2].m_Got,
|
|
|
|
m_aWeapons[3].m_AmmoRegenStart, m_aWeapons[3].m_Ammo, m_aWeapons[3].m_Ammocost, m_aWeapons[3].m_Got,
|
|
|
|
m_aWeapons[4].m_AmmoRegenStart, m_aWeapons[4].m_Ammo, m_aWeapons[4].m_Ammocost, m_aWeapons[4].m_Got,
|
|
|
|
m_aWeapons[5].m_AmmoRegenStart, m_aWeapons[5].m_Ammo, m_aWeapons[5].m_Ammocost, m_aWeapons[5].m_Got,
|
|
|
|
m_LastWeapon, m_QueuedWeapon,
|
|
|
|
// tee states
|
2022-01-09 17:39:13 +00:00
|
|
|
m_SuperJump, m_Jetpack, m_NinjaJetpack, m_FreezeTime, m_FreezeTick, m_DeepFreeze, m_EndlessHook,
|
2021-03-16 22:31:16 +00:00
|
|
|
m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, m_Time,
|
2020-09-26 19:41:58 +00:00
|
|
|
(int)m_Pos.x, (int)m_Pos.y, (int)m_PrevPos.x, (int)m_PrevPos.y,
|
|
|
|
m_TeleCheckpoint, m_LastPenalty,
|
|
|
|
(int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y,
|
|
|
|
m_ActiveWeapon, m_Jumped, m_JumpedTotal, m_Jumps,
|
|
|
|
(int)m_HookPos.x, (int)m_HookPos.y, m_HookDir.x, m_HookDir.y,
|
|
|
|
(int)m_HookTeleBase.x, (int)m_HookTeleBase.y, m_HookTick, m_HookState,
|
|
|
|
// time checkpoints
|
|
|
|
m_CpTime, m_CpActive, m_CpLastBroadcast,
|
|
|
|
m_aCpCurrent[0], m_aCpCurrent[1], m_aCpCurrent[2], m_aCpCurrent[3], m_aCpCurrent[4],
|
|
|
|
m_aCpCurrent[5], m_aCpCurrent[6], m_aCpCurrent[7], m_aCpCurrent[8], m_aCpCurrent[9],
|
|
|
|
m_aCpCurrent[10], m_aCpCurrent[11], m_aCpCurrent[12], m_aCpCurrent[13], m_aCpCurrent[14],
|
|
|
|
m_aCpCurrent[15], m_aCpCurrent[16], m_aCpCurrent[17], m_aCpCurrent[18], m_aCpCurrent[19],
|
|
|
|
m_aCpCurrent[20], m_aCpCurrent[21], m_aCpCurrent[22], m_aCpCurrent[23], m_aCpCurrent[24],
|
|
|
|
m_NotEligibleForFinish,
|
|
|
|
m_HasTelegunGun, m_HasTelegunLaser, m_HasTelegunGrenade,
|
|
|
|
m_aGameUuid,
|
|
|
|
HookedPlayer, m_NewHook,
|
|
|
|
m_InputDirection, m_InputJump, m_InputFire, m_InputHook,
|
2021-07-20 20:16:40 +00:00
|
|
|
m_ReloadTimer,
|
2022-01-09 17:39:13 +00:00
|
|
|
m_TeeStarted,
|
|
|
|
m_LiveFreeze);
|
2020-05-22 11:58:37 +00:00
|
|
|
return m_aString;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
int CSaveTee::FromString(const char *String)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
|
|
|
int Num;
|
2020-05-21 16:02:17 +00:00
|
|
|
Num = sscanf(String,
|
2020-09-26 19:41:58 +00:00
|
|
|
"%[^\t]\t%d\t%d\t%d\t%d\t%d\t"
|
|
|
|
// weapons
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t%d\t%d\t"
|
|
|
|
"%d\t%d\t"
|
|
|
|
// tee states
|
2022-01-09 17:39:13 +00:00
|
|
|
"%d\t%d\t%d\t%d\t%d\t%d\t%d\t" // m_SuperJump
|
2020-09-26 19:41:58 +00:00
|
|
|
"%d\t%d\t%d\t%d\t%d\t%d\t%d\t" // m_DDRaceState
|
|
|
|
"%f\t%f\t%f\t%f\t" // m_Pos.x
|
|
|
|
"%d\t%d\t" // m_TeleCheckpoint
|
|
|
|
"%f\t%f\t%f\t%f\t" // m_CorePos.x
|
|
|
|
"%d\t%d\t%d\t%d\t" // m_ActiveWeapon
|
|
|
|
"%f\t%f\t%f\t%f\t" // m_HookPos.x
|
|
|
|
"%f\t%f\t%d\t%d\t" // m_HookTeleBase.x
|
|
|
|
// time checkpoints
|
|
|
|
"%d\t%d\t%d\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
|
|
|
"%f\t%f\t%f\t%f\t%f\t"
|
2021-07-20 20:16:40 +00:00
|
|
|
"%d\t" // m_NotEligibleForFinish
|
|
|
|
"%d\t%d\t%d\t" // tele weapons
|
|
|
|
"%36s\t" // m_aGameUuid
|
2021-07-22 23:17:06 +00:00
|
|
|
"%d\t%d\t" // m_HookedPlayer, m_NewHook
|
2021-07-20 20:16:40 +00:00
|
|
|
"%d\t%d\t%d\t%d\t" // input stuff
|
|
|
|
"%d\t" // m_ReloadTimer
|
2022-01-10 19:40:22 +00:00
|
|
|
"%d\t" // m_TeeStarted
|
2022-01-09 17:39:13 +00:00
|
|
|
"%d", // m_LiveFreeze
|
2020-09-26 19:41:58 +00:00
|
|
|
m_aName, &m_Alive, &m_Paused, &m_NeededFaketuning, &m_TeeFinished, &m_IsSolo,
|
|
|
|
// weapons
|
|
|
|
&m_aWeapons[0].m_AmmoRegenStart, &m_aWeapons[0].m_Ammo, &m_aWeapons[0].m_Ammocost, &m_aWeapons[0].m_Got,
|
|
|
|
&m_aWeapons[1].m_AmmoRegenStart, &m_aWeapons[1].m_Ammo, &m_aWeapons[1].m_Ammocost, &m_aWeapons[1].m_Got,
|
|
|
|
&m_aWeapons[2].m_AmmoRegenStart, &m_aWeapons[2].m_Ammo, &m_aWeapons[2].m_Ammocost, &m_aWeapons[2].m_Got,
|
|
|
|
&m_aWeapons[3].m_AmmoRegenStart, &m_aWeapons[3].m_Ammo, &m_aWeapons[3].m_Ammocost, &m_aWeapons[3].m_Got,
|
|
|
|
&m_aWeapons[4].m_AmmoRegenStart, &m_aWeapons[4].m_Ammo, &m_aWeapons[4].m_Ammocost, &m_aWeapons[4].m_Got,
|
|
|
|
&m_aWeapons[5].m_AmmoRegenStart, &m_aWeapons[5].m_Ammo, &m_aWeapons[5].m_Ammocost, &m_aWeapons[5].m_Got,
|
|
|
|
&m_LastWeapon, &m_QueuedWeapon,
|
|
|
|
// tee states
|
2022-01-09 17:39:13 +00:00
|
|
|
&m_SuperJump, &m_Jetpack, &m_NinjaJetpack, &m_FreezeTime, &m_FreezeTick, &m_DeepFreeze, &m_EndlessHook,
|
2020-09-26 19:41:58 +00:00
|
|
|
&m_DDRaceState, &m_Hit, &m_Collision, &m_TuneZone, &m_TuneZoneOld, &m_Hook, &m_Time,
|
|
|
|
&m_Pos.x, &m_Pos.y, &m_PrevPos.x, &m_PrevPos.y,
|
|
|
|
&m_TeleCheckpoint, &m_LastPenalty,
|
|
|
|
&m_CorePos.x, &m_CorePos.y, &m_Vel.x, &m_Vel.y,
|
|
|
|
&m_ActiveWeapon, &m_Jumped, &m_JumpedTotal, &m_Jumps,
|
|
|
|
&m_HookPos.x, &m_HookPos.y, &m_HookDir.x, &m_HookDir.y,
|
|
|
|
&m_HookTeleBase.x, &m_HookTeleBase.y, &m_HookTick, &m_HookState,
|
|
|
|
// time checkpoints
|
|
|
|
&m_CpTime, &m_CpActive, &m_CpLastBroadcast,
|
|
|
|
&m_aCpCurrent[0], &m_aCpCurrent[1], &m_aCpCurrent[2], &m_aCpCurrent[3], &m_aCpCurrent[4],
|
|
|
|
&m_aCpCurrent[5], &m_aCpCurrent[6], &m_aCpCurrent[7], &m_aCpCurrent[8], &m_aCpCurrent[9],
|
|
|
|
&m_aCpCurrent[10], &m_aCpCurrent[11], &m_aCpCurrent[12], &m_aCpCurrent[13], &m_aCpCurrent[14],
|
|
|
|
&m_aCpCurrent[15], &m_aCpCurrent[16], &m_aCpCurrent[17], &m_aCpCurrent[18], &m_aCpCurrent[19],
|
|
|
|
&m_aCpCurrent[20], &m_aCpCurrent[21], &m_aCpCurrent[22], &m_aCpCurrent[23], &m_aCpCurrent[24],
|
|
|
|
&m_NotEligibleForFinish,
|
|
|
|
&m_HasTelegunGun, &m_HasTelegunLaser, &m_HasTelegunGrenade,
|
2020-10-19 13:21:23 +00:00
|
|
|
m_aGameUuid,
|
2020-09-26 19:41:58 +00:00
|
|
|
&m_HookedPlayer, &m_NewHook,
|
|
|
|
&m_InputDirection, &m_InputJump, &m_InputFire, &m_InputHook,
|
2021-07-20 20:16:40 +00:00
|
|
|
&m_ReloadTimer,
|
2022-01-09 17:39:13 +00:00
|
|
|
&m_TeeStarted,
|
|
|
|
&m_LiveFreeze);
|
2019-04-06 18:28:45 +00:00
|
|
|
switch(Num) // Don't forget to update this when you save / load more / less.
|
2018-12-17 13:55:58 +00:00
|
|
|
{
|
2019-04-06 18:28:45 +00:00
|
|
|
case 96:
|
2018-12-17 13:55:58 +00:00
|
|
|
m_NotEligibleForFinish = false;
|
2020-05-22 11:58:37 +00:00
|
|
|
// fall through
|
2019-04-06 18:28:45 +00:00
|
|
|
case 97:
|
2019-08-19 08:09:53 +00:00
|
|
|
m_HasTelegunGrenade = 0;
|
|
|
|
m_HasTelegunLaser = 0;
|
|
|
|
m_HasTelegunGun = 0;
|
2020-12-28 16:48:27 +00:00
|
|
|
FormatUuid(CalculateUuid("game-uuid-nonexistent@ddnet.tw"), m_aGameUuid, sizeof(m_aGameUuid));
|
2020-05-22 11:58:37 +00:00
|
|
|
// fall through
|
2020-10-19 13:21:23 +00:00
|
|
|
case 101:
|
2020-06-29 20:28:26 +00:00
|
|
|
m_HookedPlayer = -1;
|
|
|
|
m_NewHook = false;
|
|
|
|
if(m_HookState == HOOK_GRABBED)
|
|
|
|
m_HookState = HOOK_FLYING;
|
|
|
|
m_InputDirection = 0;
|
|
|
|
m_InputJump = 0;
|
|
|
|
m_InputFire = 0;
|
|
|
|
m_InputHook = 0;
|
2020-06-30 21:49:40 +00:00
|
|
|
m_ReloadTimer = 0;
|
2020-06-29 20:28:26 +00:00
|
|
|
// fall through
|
2020-10-19 13:21:23 +00:00
|
|
|
case 108:
|
2021-07-20 20:16:40 +00:00
|
|
|
m_TeeStarted = true;
|
|
|
|
// fall through
|
|
|
|
case 109:
|
2022-01-09 17:39:13 +00:00
|
|
|
m_LiveFreeze = 0;
|
|
|
|
// fall through
|
|
|
|
case 110:
|
2014-07-26 12:46:31 +00:00
|
|
|
return 0;
|
2019-04-06 18:28:45 +00:00
|
|
|
default:
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("load", "failed to load tee-string");
|
|
|
|
dbg_msg("load", "loaded %d vars", Num);
|
2020-05-22 21:59:47 +00:00
|
|
|
return Num + 1; // never 0 here
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 20:28:26 +00:00
|
|
|
void CSaveTee::LoadHookedPlayer(const CSaveTeam *pTeam)
|
|
|
|
{
|
|
|
|
if(m_HookedPlayer == -1)
|
|
|
|
return;
|
|
|
|
m_HookedPlayer = pTeam->m_pSavedTees[m_HookedPlayer].GetClientID();
|
|
|
|
}
|
|
|
|
|
2021-01-23 15:49:50 +00:00
|
|
|
bool CSaveTee::IsHooking() const
|
|
|
|
{
|
|
|
|
return m_HookState == HOOK_GRABBED || m_HookState == HOOK_FLYING;
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CSaveTeam::CSaveTeam(IGameController *Controller)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
|
|
|
m_pController = Controller;
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pSwitchers = 0;
|
|
|
|
m_pSavedTees = 0;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CSaveTeam::~CSaveTeam()
|
|
|
|
{
|
2022-01-22 12:47:37 +00:00
|
|
|
delete[] m_pSwitchers;
|
|
|
|
delete[] m_pSavedTees;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2021-02-16 17:15:50 +00:00
|
|
|
int CSaveTeam::Save(int Team)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2022-02-19 14:20:32 +00:00
|
|
|
if(g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO || (Team > 0 && Team < MAX_CLIENTS))
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
CGameTeams *Teams = &(((CGameControllerDDRace *)m_pController)->m_Teams);
|
2015-07-09 00:08:14 +00:00
|
|
|
|
2014-10-07 13:20:38 +00:00
|
|
|
m_MembersCount = Teams->Count(Team);
|
|
|
|
if(m_MembersCount <= 0)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_TeamState = Teams->GetTeamState(Team);
|
2015-07-09 21:47:36 +00:00
|
|
|
|
|
|
|
if(m_TeamState != CGameTeams::TEAMSTATE_STARTED)
|
|
|
|
{
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
2014-07-26 12:46:31 +00:00
|
|
|
m_NumSwitchers = m_pController->GameServer()->Collision()->m_NumSwitchers;
|
|
|
|
m_TeamLocked = Teams->TeamLocked(Team);
|
2020-05-22 21:59:47 +00:00
|
|
|
m_Practice = Teams->IsPractice(Team);
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pSavedTees = new CSaveTee[m_MembersCount];
|
2014-07-26 12:46:31 +00:00
|
|
|
int j = 0;
|
2020-06-28 11:14:55 +00:00
|
|
|
CCharacter *p = (CCharacter *)m_pController->GameServer()->m_World.FindFirst(CGameWorld::ENTTYPE_CHARACTER);
|
|
|
|
for(; p; p = (CCharacter *)p->TypeNext())
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-06-28 11:14:55 +00:00
|
|
|
if(Teams->m_Core.Team(p->GetPlayer()->GetCID()) != Team)
|
|
|
|
continue;
|
2020-07-02 14:34:36 +00:00
|
|
|
if(m_MembersCount == j)
|
|
|
|
return 3;
|
2021-02-16 17:15:50 +00:00
|
|
|
m_pSavedTees[j].Save(p);
|
2020-06-28 11:14:55 +00:00
|
|
|
j++;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
2020-06-28 11:14:55 +00:00
|
|
|
if(m_MembersCount != j)
|
|
|
|
return 3;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
|
|
|
if(m_pController->GameServer()->Collision()->m_NumSwitchers)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSwitchers = new SSimpleSwitchers[m_pController->GameServer()->Collision()->m_NumSwitchers + 1];
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int i = 1; i < m_pController->GameServer()->Collision()->m_NumSwitchers + 1; i++)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pSwitchers[i].m_Status = m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Status[Team];
|
2014-07-26 12:46:31 +00:00
|
|
|
if(m_pController->GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team])
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pSwitchers[i].m_EndTime = m_pController->Server()->Tick() - m_pController->GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team];
|
2014-07-26 12:46:31 +00:00
|
|
|
else
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pSwitchers[i].m_EndTime = 0;
|
|
|
|
m_pSwitchers[i].m_Type = m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Type[Team];
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
2014-07-28 13:42:57 +00:00
|
|
|
return 1;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2020-02-13 16:04:58 +00:00
|
|
|
bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameContext)
|
|
|
|
{
|
|
|
|
switch(Result)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
case 0:
|
|
|
|
return false;
|
|
|
|
case 1:
|
|
|
|
pGameContext->SendChatTarget(ClientID, "You have to be in a team (from 1-63)");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
pGameContext->SendChatTarget(ClientID, "Could not find your Team");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
pGameContext->SendChatTarget(ClientID, "Unable to find all Characters");
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
pGameContext->SendChatTarget(ClientID, "Your team has not started yet");
|
|
|
|
break;
|
|
|
|
default: // this state should never be reached
|
|
|
|
pGameContext->SendChatTarget(ClientID, "Unknown error while saving");
|
|
|
|
break;
|
2020-02-13 16:04:58 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-16 17:15:50 +00:00
|
|
|
void CSaveTeam::Load(int Team, bool KeepCurrentWeakStrong)
|
2020-06-02 14:27:31 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
CGameTeams *pTeams = &(((CGameControllerDDRace *)m_pController)->m_Teams);
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2017-05-18 18:01:10 +00:00
|
|
|
pTeams->ChangeTeamState(Team, m_TeamState);
|
|
|
|
pTeams->SetTeamLock(Team, m_TeamLocked);
|
2022-03-25 09:09:45 +00:00
|
|
|
pTeams->SetPractice(Team, m_Practice);
|
2017-05-18 18:01:10 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int i = m_MembersCount; i-- > 0;)
|
2014-12-17 19:38:05 +00:00
|
|
|
{
|
2020-06-03 20:08:21 +00:00
|
|
|
int ClientID = m_pSavedTees[i].GetClientID();
|
|
|
|
if(m_pController->GameServer()->m_apPlayers[ClientID] && pTeams->m_Core.Team(ClientID) == Team)
|
2017-05-14 20:36:44 +00:00
|
|
|
{
|
2020-08-13 13:37:27 +00:00
|
|
|
CCharacter *pChr = MatchCharacter(m_pSavedTees[i].GetClientID(), i, KeepCurrentWeakStrong);
|
2021-02-16 17:15:50 +00:00
|
|
|
m_pSavedTees[i].Load(pChr, Team);
|
2017-05-14 20:36:44 +00:00
|
|
|
}
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
2014-12-17 19:38:05 +00:00
|
|
|
|
|
|
|
if(m_pController->GameServer()->Collision()->m_NumSwitchers)
|
2020-06-03 20:08:21 +00:00
|
|
|
{
|
2021-11-23 17:48:07 +00:00
|
|
|
for(int i = 1; i < minimum(m_NumSwitchers, m_pController->GameServer()->Collision()->m_NumSwitchers) + 1; i++)
|
2014-12-17 19:38:05 +00:00
|
|
|
{
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Status[Team] = m_pSwitchers[i].m_Status;
|
|
|
|
if(m_pSwitchers[i].m_EndTime)
|
|
|
|
m_pController->GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team] = m_pController->Server()->Tick() - m_pSwitchers[i].m_EndTime;
|
|
|
|
m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Type[Team] = m_pSwitchers[i].m_Type;
|
2014-12-17 19:38:05 +00:00
|
|
|
}
|
2017-05-14 20:36:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CCharacter *CSaveTeam::MatchCharacter(int ClientID, int SaveID, bool KeepCurrentCharacter)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-08-13 13:37:27 +00:00
|
|
|
if(KeepCurrentCharacter && m_pController->GameServer()->m_apPlayers[ClientID]->GetCharacter())
|
|
|
|
{
|
|
|
|
// keep old character to retain current weak/strong order
|
|
|
|
return m_pController->GameServer()->m_apPlayers[ClientID]->GetCharacter();
|
|
|
|
}
|
2020-06-28 11:14:55 +00:00
|
|
|
m_pController->GameServer()->m_apPlayers[ClientID]->KillCharacter(WEAPON_GAME);
|
|
|
|
return m_pController->GameServer()->m_apPlayers[ClientID]->ForceSpawn(m_pSavedTees[SaveID].GetPos());
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
char *CSaveTeam::GetString()
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-05-22 21:59:47 +00:00
|
|
|
str_format(m_aString, sizeof(m_aString), "%d\t%d\t%d\t%d\t%d", m_TeamState, m_MembersCount, m_NumSwitchers, m_TeamLocked, m_Practice);
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-05-22 21:59:47 +00:00
|
|
|
for(int i = 0; i < m_MembersCount; i++)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
|
|
|
char aBuf[1024];
|
2020-06-29 20:28:26 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "\n%s", m_pSavedTees[i].GetString(this));
|
2020-05-22 11:58:37 +00:00
|
|
|
str_append(m_aString, aBuf, sizeof(m_aString));
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2020-05-22 21:59:47 +00:00
|
|
|
if(m_pSwitchers && m_NumSwitchers)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int i = 1; i < m_NumSwitchers + 1; i++)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
|
|
|
char aBuf[64];
|
2020-05-22 21:59:47 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "\n%d\t%d\t%d", m_pSwitchers[i].m_Status, m_pSwitchers[i].m_EndTime, m_pSwitchers[i].m_Type);
|
|
|
|
str_append(m_aString, aBuf, sizeof(m_aString));
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
2020-05-22 21:59:47 +00:00
|
|
|
}
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-05-22 11:58:37 +00:00
|
|
|
return m_aString;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
int CSaveTeam::FromString(const char *String)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2021-09-13 09:47:47 +00:00
|
|
|
char aTeamStats[MAX_CLIENTS];
|
|
|
|
char aSwitcher[64];
|
|
|
|
char aSaveTee[1024];
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
char *CopyPos;
|
2014-07-26 12:46:31 +00:00
|
|
|
unsigned int Pos = 0;
|
|
|
|
unsigned int LastPos = 0;
|
|
|
|
unsigned int StrSize;
|
|
|
|
|
2020-05-22 11:58:37 +00:00
|
|
|
str_copy(m_aString, String, sizeof(m_aString));
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
while(m_aString[Pos] != '\n' && Pos < sizeof(m_aString) && m_aString[Pos]) // find next \n or \0
|
2014-07-26 12:46:31 +00:00
|
|
|
Pos++;
|
|
|
|
|
2020-05-22 11:58:37 +00:00
|
|
|
CopyPos = m_aString + LastPos;
|
2014-07-26 12:46:31 +00:00
|
|
|
StrSize = Pos - LastPos + 1;
|
2020-05-22 11:58:37 +00:00
|
|
|
if(m_aString[Pos] == '\n')
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
|
|
|
Pos++; // skip \n
|
|
|
|
LastPos = Pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(StrSize <= 0)
|
|
|
|
{
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("load", "savegame: wrong format (couldn't load teamstats)");
|
2014-07-26 12:46:31 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-09-13 09:47:47 +00:00
|
|
|
if(StrSize < sizeof(aTeamStats))
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2021-09-13 09:47:47 +00:00
|
|
|
str_copy(aTeamStats, CopyPos, StrSize);
|
|
|
|
int Num = sscanf(aTeamStats, "%d\t%d\t%d\t%d\t%d", &m_TeamState, &m_MembersCount, &m_NumSwitchers, &m_TeamLocked, &m_Practice);
|
2020-05-22 21:59:47 +00:00
|
|
|
switch(Num) // Don't forget to update this when you save / load more / less.
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
case 4:
|
|
|
|
m_Practice = false;
|
|
|
|
// fallthrough
|
|
|
|
case 5:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dbg_msg("load", "failed to load teamstats");
|
|
|
|
dbg_msg("load", "loaded %d vars", Num);
|
|
|
|
return Num + 1; // never 0 here
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("load", "savegame: wrong format (couldn't load teamstats, too big)");
|
2014-07-26 12:46:31 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-05-22 11:58:37 +00:00
|
|
|
if(m_pSavedTees)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
delete[] m_pSavedTees;
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pSavedTees = 0;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(m_MembersCount)
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pSavedTees = new CSaveTee[m_MembersCount];
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int n = 0; n < m_MembersCount; n++)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
while(m_aString[Pos] != '\n' && Pos < sizeof(m_aString) && m_aString[Pos]) // find next \n or \0
|
2014-07-26 12:46:31 +00:00
|
|
|
Pos++;
|
|
|
|
|
2020-05-22 11:58:37 +00:00
|
|
|
CopyPos = m_aString + LastPos;
|
2014-07-26 12:46:31 +00:00
|
|
|
StrSize = Pos - LastPos + 1;
|
2020-05-22 11:58:37 +00:00
|
|
|
if(m_aString[Pos] == '\n')
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
|
|
|
Pos++; // skip \n
|
|
|
|
LastPos = Pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(StrSize <= 0)
|
|
|
|
{
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("load", "savegame: wrong format (couldn't load tee)");
|
2014-07-26 12:46:31 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-09-13 09:47:47 +00:00
|
|
|
if(StrSize < sizeof(aSaveTee))
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2021-09-13 09:47:47 +00:00
|
|
|
str_copy(aSaveTee, CopyPos, StrSize);
|
|
|
|
int Num = m_pSavedTees[n].FromString(aSaveTee);
|
2014-07-26 12:46:31 +00:00
|
|
|
if(Num)
|
|
|
|
{
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("load", "failed to load tee");
|
2020-09-26 19:41:58 +00:00
|
|
|
dbg_msg("load", "loaded %d vars", Num - 1);
|
2014-07-26 12:46:31 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("load", "savegame: wrong format (couldn't load tee, too big)");
|
2014-07-26 12:46:31 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-22 11:58:37 +00:00
|
|
|
if(m_pSwitchers)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
delete[] m_pSwitchers;
|
2020-05-22 11:58:37 +00:00
|
|
|
m_pSwitchers = 0;
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(m_NumSwitchers)
|
2020-09-26 19:41:58 +00:00
|
|
|
m_pSwitchers = new SSimpleSwitchers[m_NumSwitchers + 1];
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int n = 1; n < m_NumSwitchers + 1; n++)
|
2020-06-26 18:49:48 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
while(m_aString[Pos] != '\n' && Pos < sizeof(m_aString) && m_aString[Pos]) // find next \n or \0
|
2020-06-26 18:49:48 +00:00
|
|
|
Pos++;
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-06-26 18:49:48 +00:00
|
|
|
CopyPos = m_aString + LastPos;
|
|
|
|
StrSize = Pos - LastPos + 1;
|
|
|
|
if(m_aString[Pos] == '\n')
|
|
|
|
{
|
|
|
|
Pos++; // skip \n
|
|
|
|
LastPos = Pos;
|
|
|
|
}
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2020-06-26 18:49:48 +00:00
|
|
|
if(StrSize <= 0)
|
|
|
|
{
|
|
|
|
dbg_msg("load", "savegame: wrong format (couldn't load switcher)");
|
|
|
|
return 1;
|
|
|
|
}
|
2014-07-26 12:46:31 +00:00
|
|
|
|
2021-09-13 09:47:47 +00:00
|
|
|
if(StrSize < sizeof(aSwitcher))
|
2020-06-26 18:49:48 +00:00
|
|
|
{
|
2021-09-13 09:47:47 +00:00
|
|
|
str_copy(aSwitcher, CopyPos, StrSize);
|
|
|
|
int Num = sscanf(aSwitcher, "%d\t%d\t%d", &(m_pSwitchers[n].m_Status), &(m_pSwitchers[n].m_EndTime), &(m_pSwitchers[n].m_Type));
|
2020-06-26 18:49:48 +00:00
|
|
|
if(Num != 3)
|
2014-07-26 12:46:31 +00:00
|
|
|
{
|
2020-06-26 18:49:48 +00:00
|
|
|
dbg_msg("load", "failed to load switcher");
|
2020-09-26 19:41:58 +00:00
|
|
|
dbg_msg("load", "loaded %d vars", Num - 1);
|
2014-07-26 12:46:31 +00:00
|
|
|
}
|
|
|
|
}
|
2020-06-26 18:49:48 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_msg("load", "savegame: wrong format (couldn't load switcher, too big)");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2014-07-26 12:46:31 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-06-03 20:08:21 +00:00
|
|
|
|
|
|
|
bool CSaveTeam::MatchPlayers(const char (*paNames)[MAX_NAME_LENGTH], const int *pClientID, int NumPlayer, char *pMessage, int MessageLen)
|
|
|
|
{
|
|
|
|
if(NumPlayer > m_MembersCount)
|
|
|
|
{
|
|
|
|
str_format(pMessage, MessageLen, "Too many players in this team, should be %d", m_MembersCount);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// check for wrong players
|
|
|
|
for(int i = 0; i < NumPlayer; i++)
|
|
|
|
{
|
|
|
|
int Found = false;
|
|
|
|
for(int j = 0; j < m_MembersCount; j++)
|
|
|
|
{
|
2020-06-22 10:46:42 +00:00
|
|
|
if(str_comp(paNames[i], m_pSavedTees[j].GetName()) == 0)
|
2020-06-03 20:08:21 +00:00
|
|
|
{
|
|
|
|
Found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!Found)
|
|
|
|
{
|
2020-10-19 13:23:48 +00:00
|
|
|
str_format(pMessage, MessageLen, "'%s' doesn't belong to this team", paNames[i]);
|
2020-06-03 20:08:21 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// check for missing players
|
|
|
|
for(int i = 0; i < m_MembersCount; i++)
|
|
|
|
{
|
|
|
|
int Found = false;
|
|
|
|
for(int j = 0; j < NumPlayer; j++)
|
|
|
|
{
|
2020-06-22 10:46:42 +00:00
|
|
|
if(str_comp(m_pSavedTees[i].GetName(), paNames[j]) == 0)
|
2020-06-03 20:08:21 +00:00
|
|
|
{
|
|
|
|
m_pSavedTees[i].SetClientID(pClientID[j]);
|
|
|
|
Found = true;
|
2020-06-26 18:45:18 +00:00
|
|
|
break;
|
2020-06-03 20:08:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!Found)
|
|
|
|
{
|
|
|
|
str_format(pMessage, MessageLen, "'%s' has to be in this team", m_pSavedTees[i].GetName());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-06-29 20:28:26 +00:00
|
|
|
// match hook to correct ClientID
|
2020-09-26 19:41:58 +00:00
|
|
|
for(int n = 0; n < m_MembersCount; n++)
|
2020-06-29 20:28:26 +00:00
|
|
|
m_pSavedTees[n].LoadHookedPlayer(this);
|
2020-06-03 20:08:21 +00:00
|
|
|
return true;
|
|
|
|
}
|