From 4532a64b2b992835c41bb351164a8d999a9e600a Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 28 Feb 2024 20:56:43 +0300 Subject: [PATCH] Add rescuemodes May `/rescuemode` be case-insensitive clang-tidy conflicts clang-tidy `CMDFLAG_PRACTICE` --- src/game/server/ddracechat.cpp | 72 ++++++++++++++++++++++- src/game/server/entities/character.cpp | 80 +++++++++++++++----------- src/game/server/entities/character.h | 9 +-- src/game/server/gamecontext.cpp | 7 +-- src/game/server/gamecontext.h | 1 + src/game/server/player.cpp | 1 + src/game/server/player.h | 2 + src/game/server/save.cpp | 3 +- src/game/server/save.h | 7 +++ src/game/server/teams.cpp | 3 +- 10 files changed, 139 insertions(+), 46 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index e6ddfa178..85cf98331 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -1652,8 +1652,76 @@ void CGameContext::ConRescue(IConsole::IResult *pResult, void *pUserData) return; } - pChr->Rescue(); - pChr->UnFreeze(); + bool GoRescue = true; + + if(pPlayer->m_RescueMode == RESCUEMODE_MANUAL) + { + // if character can't set his rescue state then we should rescue him instead + GoRescue = !pChr->TrySetRescue(RESCUEMODE_MANUAL); + } + + if(GoRescue) + { + pChr->Rescue(); + pChr->UnFreeze(); + } +} + +void CGameContext::ConRescueMode(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(!CheckClientId(pResult->m_ClientId)) + return; + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId]; + if(!pPlayer) + return; + + CGameTeams &Teams = pSelf->m_pController->Teams(); + int Team = pSelf->GetDDRaceTeam(pResult->m_ClientId); + if(!g_Config.m_SvRescue && !Teams.IsPractice(Team)) + { + pSelf->SendChatTarget(pPlayer->GetCid(), "Rescue is not enabled on this server and you're not in a team with /practice turned on. Note that you can't earn a rank with practice enabled."); + return; + } + + if(str_comp_nocase(pResult->GetString(0), "auto") == 0) + { + if(pPlayer->m_RescueMode != RESCUEMODE_AUTO) + { + pPlayer->m_RescueMode = RESCUEMODE_AUTO; + + pSelf->SendChatTarget(pPlayer->GetCid(), "Rescue mode changed to auto."); + } + + return; + } + + if(str_comp_nocase(pResult->GetString(0), "manual") == 0) + { + if(pPlayer->m_RescueMode != RESCUEMODE_MANUAL) + { + pPlayer->m_RescueMode = RESCUEMODE_MANUAL; + + pSelf->SendChatTarget(pPlayer->GetCid(), "Rescue mode changed to manual."); + } + + return; + } + + if(str_comp_nocase(pResult->GetString(0), "list") == 0) + { + pSelf->SendChatTarget(pPlayer->GetCid(), "Available rescue modes: auto, manual"); + } + else if(str_comp_nocase(pResult->GetString(0), "") == 0) + { + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "Current rescue mode: %s.", pPlayer->m_RescueMode == RESCUEMODE_MANUAL ? "manual" : "auto"); + pSelf->SendChatTarget(pPlayer->GetCid(), aBuf); + } + else + { + pSelf->SendChatTarget(pPlayer->GetCid(), "Unknown argument. Check '/rescuemode list'"); + } } void CGameContext::ConTeleTo(IConsole::IResult *pResult, void *pUserData) diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 071aaac00..7f8b5e029 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -99,6 +99,7 @@ bool CCharacter::Spawn(CPlayer *pPlayer, vec2 Pos) SendZoneMsgs(); // we want a entermessage also on spawn GameServer()->SendTuningParams(m_pPlayer->GetCid(), m_TuneZone); + TrySetRescue(RESCUEMODE_MANUAL); Server()->StartRecord(m_pPlayer->GetCid()); return true; @@ -2010,10 +2011,46 @@ void CCharacter::SetTeams(CGameTeams *pTeams) m_Core.SetTeamsCore(&m_pTeams->m_Core); } -void CCharacter::SetRescue() +bool CCharacter::TrySetRescue(int RescueMode) { - m_RescueTee.Save(this); - m_SetSavePos = true; + bool Set = false; + if(g_Config.m_SvRescue || ((g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO || Team() > TEAM_FLOCK) && Team() >= TEAM_FLOCK && Team() < TEAM_SUPER)) + { + // check for nearby health pickups (also freeze) + bool InHealthPickup = false; + if(!m_Core.m_IsInFreeze) + { + CEntity *apEnts[9]; + int Num = GameWorld()->FindEntities(m_Pos, GetProximityRadius() + CPickup::ms_CollisionExtraSize, apEnts, std::size(apEnts), CGameWorld::ENTTYPE_PICKUP); + for(int i = 0; i < Num; ++i) + { + CPickup *pPickup = static_cast(apEnts[i]); + if(pPickup->Type() == POWERUP_HEALTH) + { + // This uses a separate variable InHealthPickup instead of setting m_Core.m_IsInFreeze + // as the latter causes freezebars to flicker when standing in the freeze range of a + // health pickup. When the same code for client prediction is added, the freezebars + // still flicker, but only when standing at the edge of the health pickup's freeze range. + InHealthPickup = true; + break; + } + } + } + + if(!m_Core.m_IsInFreeze && IsGrounded() && !m_Core.m_DeepFrozen && !InHealthPickup) + { + ForceSetRescue(RescueMode); + Set = true; + } + } + + return Set; +} + +void CCharacter::ForceSetRescue(int RescueMode) +{ + m_RescueTee[RescueMode].Save(this); + m_SetSavePos[RescueMode] = true; } void CCharacter::DDRaceTick() @@ -2061,35 +2098,9 @@ void CCharacter::DDRaceTick() } } - // check for nearby health pickups (also freeze) - bool InHealthPickup = false; - if(!m_Core.m_IsInFreeze) - { - CEntity *apEnts[9]; - int Num = GameWorld()->FindEntities(m_Pos, GetProximityRadius() + CPickup::ms_CollisionExtraSize, apEnts, std::size(apEnts), CGameWorld::ENTTYPE_PICKUP); - for(int i = 0; i < Num; ++i) - { - CPickup *pPickup = static_cast(apEnts[i]); - if(pPickup->Type() == POWERUP_HEALTH) - { - // This uses a separate variable InHealthPickup instead of setting m_Core.m_IsInFreeze - // as the latter causes freezebars to flicker when standing in the freeze range of a - // health pickup. When the same code for client prediction is added, the freezebars - // still flicker, but only when standing at the edge of the health pickup's freeze range. - InHealthPickup = true; - break; - } - } - } - // look for save position for rescue feature - if(g_Config.m_SvRescue || ((g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO || Team() > TEAM_FLOCK) && Team() >= TEAM_FLOCK && Team() < TEAM_SUPER)) - { - if(!m_Core.m_IsInFreeze && IsGrounded() && !m_Core.m_DeepFrozen && !InHealthPickup) - { - SetRescue(); - } - } + // always update auto rescue + TrySetRescue(RESCUEMODE_AUTO); m_Core.m_Id = GetPlayer()->GetCid(); } @@ -2298,7 +2309,8 @@ void CCharacter::DDRaceInit() m_Paused = false; m_DDRaceState = DDRACE_NONE; m_PrevPos = m_Pos; - m_SetSavePos = false; + for(bool &Set : m_SetSavePos) + Set = false; m_LastBroadcast = 0; m_TeamBeforeSuper = 0; m_Core.m_Id = GetPlayer()->GetCid(); @@ -2348,7 +2360,7 @@ void CCharacter::DDRaceInit() void CCharacter::Rescue() { - if(m_SetSavePos && !m_Core.m_Super) + if(m_SetSavePos[GetPlayer()->m_RescueMode] && !m_Core.m_Super) { if(m_LastRescue + (int64_t)g_Config.m_SvRescueDelay * Server()->TickSpeed() > Server()->Tick()) { @@ -2359,7 +2371,7 @@ void CCharacter::Rescue() } float StartTime = m_StartTime; - m_RescueTee.Load(this, Team()); + m_RescueTee[GetPlayer()->m_RescueMode].Load(this, Team()); // Don't load these from saved tee: m_Core.m_Vel = vec2(0, 0); m_Core.m_HookState = HOOK_IDLE; diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index d9670e194..51c157196 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -166,7 +166,7 @@ private: int m_LastBroadcast; void DDRaceInit(); void HandleSkippableTiles(int Index); - void SetRescue(); + void ForceSetRescue(int RescueMode); void DDRaceTick(); void DDRacePostCoreTick(); void HandleBroadcast(); @@ -174,12 +174,13 @@ private: void SendZoneMsgs(); IAntibot *Antibot(); - bool m_SetSavePos; - CSaveTee m_RescueTee; + bool m_SetSavePos[NUM_RESCUEMODES]; + CSaveTee m_RescueTee[NUM_RESCUEMODES]; public: CGameTeams *Teams() { return m_pTeams; } void SetTeams(CGameTeams *pTeams); + bool TrySetRescue(int RescueMode); void FillAntibot(CAntibotCharacterData *pData); void Pause(bool Pause); @@ -259,7 +260,7 @@ public: bool IsSuper() { return m_Core.m_Super; } - CSaveTee &GetRescueTeeRef() { return m_RescueTee; } + CSaveTee &GetLastRescueTeeRef(int Mode = RESCUEMODE_AUTO) { return m_RescueTee[Mode]; } }; enum diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 1db4324ce..79e42af43 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3689,9 +3689,9 @@ void CGameContext::RegisterChatCommands() Console()->Register("saytimeall", "", CFGFLAG_CHAT | CFGFLAG_SERVER | CFGFLAG_NONTEEHISTORIC, ConSayTimeAll, this, "Publicly messages everyone your current time in this current running race"); Console()->Register("time", "", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTime, this, "Privately shows you your current time in this current running race in the broadcast message"); Console()->Register("timer", "?s['gametimer'|'broadcast'|'both'|'none'|'cycle']", CFGFLAG_CHAT | CFGFLAG_SERVER, ConSetTimerType, this, "Personal Setting of showing time in either broadcast or game/round timer, timer s, where s = broadcast for broadcast, gametimer for game/round timer, cycle for cycle, both for both, none for no timer and nothing to show current status"); - - Console()->Register("r", "", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConRescue, this, "Teleport yourself out of freeze (use sv_rescue 1 to enable this feature)"); - Console()->Register("rescue", "", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConRescue, this, "Teleport yourself out of freeze (use sv_rescue 1 to enable this feature)"); + Console()->Register("r", "", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConRescue, this, "Teleport yourself out of freeze if auto rescue mode is enabled, otherwise it will set position for rescuing if grounded and teleport you out of freeze if not (use sv_rescue 1 to enable this feature)"); + Console()->Register("rescue", "", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConRescue, this, "Teleport yourself out of freeze if auto rescue mode is enabled, otherwise it will set position for rescuing if grounded and teleport you out of freeze if not (use sv_rescue 1 to enable this feature)"); + Console()->Register("rescuemode", "?r['auto'|'manual']", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConRescueMode, this, "Sets one of the two rescue modes (auto or manual). Prints current mode if no arguments provided"); Console()->Register("tp", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleTo, this, "Depending on the number of supplied arguments, teleport yourself to; (0.) where you are spectating or aiming; (1.) the specified player name"); Console()->Register("teleport", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleTo, this, "Depending on the number of supplied arguments, teleport yourself to; (0.) where you are spectating or aiming; (1.) the specified player name"); Console()->Register("tpxy", "f[x] f[y]", CFGFLAG_CHAT | CFGFLAG_SERVER | CMDFLAG_PRACTICE, ConTeleXY, this, "Teleport yourself to the specified coordinates. A tilde (~) can be used to denote your current position, e.g. '/tpxy ~1 ~' to teleport one tile to the right"); @@ -3718,7 +3718,6 @@ void CGameContext::RegisterChatCommands() Console()->Register("unweapons", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeUnWeapons, this, "Removes all weapons from you"); Console()->Register("ninja", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeNinja, this, "Makes you a ninja"); Console()->Register("unninja", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeUnNinja, this, "Removes ninja from you"); - Console()->Register("kill", "", CFGFLAG_CHAT | CFGFLAG_SERVER, ConProtectedKill, this, "Kill yourself when kill-protected during a long game (use f1, kill for regular kill)"); } diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index dc1c658a6..d7ae91341 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -457,6 +457,7 @@ private: static void ConTime(IConsole::IResult *pResult, void *pUserData); static void ConSetTimerType(IConsole::IResult *pResult, void *pUserData); static void ConRescue(IConsole::IResult *pResult, void *pUserData); + static void ConRescueMode(IConsole::IResult *pResult, void *pUserData); static void ConTeleTo(IConsole::IResult *pResult, void *pUserData); static void ConTeleXY(IConsole::IResult *pResult, void *pUserData); static void ConTeleCursor(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index cf9d4b2d3..a6e33ccff 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -143,6 +143,7 @@ void CPlayer::Reset() m_VotedForPractice = false; m_SwapTargetsClientId = -1; m_BirthdayAnnounced = false; + m_RescueMode = RESCUEMODE_AUTO; } static int PlayerFlags_SixToSeven(int Flags) diff --git a/src/game/server/player.h b/src/game/server/player.h index 4d37049dd..ecda4fb99 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -225,6 +225,8 @@ public: int m_SwapTargetsClientId; //Client ID of the swap target for the given player bool m_BirthdayAnnounced; + int m_RescueMode; + CSaveTee m_LastTeleTee; }; diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index 07606adbf..fa17265e0 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -236,7 +236,8 @@ void CSaveTee::Load(CCharacter *pChr, int Team, bool IsSwap) { // Always create a rescue tee at the exact location we loaded from so that // the old one gets overwritten. - pChr->SetRescue(); + pChr->ForceSetRescue(RESCUEMODE_AUTO); + pChr->ForceSetRescue(RESCUEMODE_MANUAL); } } diff --git a/src/game/server/save.h b/src/game/server/save.h index 1b7e993cc..ab9009488 100644 --- a/src/game/server/save.h +++ b/src/game/server/save.h @@ -12,6 +12,13 @@ class CGameWorld; class CCharacter; class CSaveTeam; +enum +{ + RESCUEMODE_AUTO = 0, + RESCUEMODE_MANUAL, + NUM_RESCUEMODES +}; + class CSaveTee { public: diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index c9fd1bee2..e6f70a944 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -951,7 +951,8 @@ void CGameTeams::SwapTeamCharacters(CPlayer *pPrimaryPlayer, CPlayer *pTargetPla } std::swap(m_aTeeStarted[pPrimaryPlayer->GetCid()], m_aTeeStarted[pTargetPlayer->GetCid()]); std::swap(m_aTeeFinished[pPrimaryPlayer->GetCid()], m_aTeeFinished[pTargetPlayer->GetCid()]); - std::swap(pPrimaryPlayer->GetCharacter()->GetRescueTeeRef(), pTargetPlayer->GetCharacter()->GetRescueTeeRef()); + std::swap(pPrimaryPlayer->GetCharacter()->GetLastRescueTeeRef(RESCUEMODE_AUTO), pTargetPlayer->GetCharacter()->GetLastRescueTeeRef(RESCUEMODE_AUTO)); + std::swap(pPrimaryPlayer->GetCharacter()->GetLastRescueTeeRef(RESCUEMODE_MANUAL), pTargetPlayer->GetCharacter()->GetLastRescueTeeRef(RESCUEMODE_MANUAL)); GameServer()->m_World.SwapClients(pPrimaryPlayer->GetCid(), pTargetPlayer->GetCid());