Skip to content

Commit

Permalink
Game mode detect neo_game_config, change defaults (#769)
Browse files Browse the repository at this point in the history
* Now reads in neo_game_config entity, just takes the number
* OG:NT + NT;RE already maps 0=TDM,1=CTG,2=VIP, so no conversion needed
* Mid-round voting no longer default
* ConVars to change gamemode initalization behavior

* fixes #723
  • Loading branch information
nullsystem authored Nov 2, 2024
1 parent ebd39a8 commit 14c4282
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 38 deletions.
19 changes: 15 additions & 4 deletions mp/src/game/client/neo/ui/neo_root.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "tier1/interface.h"
#include <ctime>
#include "ui/neo_loading.h"
#include "neo_gamerules.h"
#include "neo_misc.h"

#include <vgui/IInput.h>
#include <vgui_controls/Controls.h>
Expand Down Expand Up @@ -348,9 +350,9 @@ void CNeoRoot::ApplySchemeSettings(IScheme *pScheme)
g_uiCtx.iMarginY = tall / 108;
g_iAvatar = wide / 30;
const float flWide = static_cast<float>(wide);
float flWideAs43 = static_cast<float>(tall) * (4.0f / 3.0f);
if (flWideAs43 > flWide) flWideAs43 = flWide;
g_iRootSubPanelWide = static_cast<int>(flWideAs43 * 0.9f);
m_flWideAs43 = static_cast<float>(tall) * (4.0f / 3.0f);
if (m_flWideAs43 > flWide) m_flWideAs43 = flWide;
g_iRootSubPanelWide = static_cast<int>(m_flWideAs43 * 0.9f);

constexpr int PARTITION = GSIW__TOTAL * 4;
const int iSubDiv = g_iRootSubPanelWide / PARTITION;
Expand Down Expand Up @@ -669,14 +671,23 @@ void CNeoRoot::MainLoopRoot(const MainLoopParam param)

g_uiCtx.dPanel.x = iRightXPos;
g_uiCtx.dPanel.y = iRightSideYStart;
g_uiCtx.dPanel.wide = g_iRootSubPanelWide - iRightXPos + (g_uiCtx.iMarginX * 2);
if (engine->IsInGame())
{
g_uiCtx.dPanel.wide = m_flWideAs43 * 0.7f;
g_uiCtx.flWgXPerc = 0.25f;
}
else
{
g_uiCtx.dPanel.wide = GetWide() - iRightXPos - (g_uiCtx.iMarginX * 2);
}
NeoUI::BeginSection();
{
if (engine->IsInGame())
{
// Show the current server's information
NeoUI::Label(L"Hostname:", m_wszHostname);
NeoUI::Label(L"Map:", m_wszMap);
NeoUI::Label(L"Game mode:", NEO_GAME_TYPE_DESC_STRS[NEORules()->GetGameType()].wszStr);
// TODO: more info, g_PR, scoreboard stuff, etc...
}
else
Expand Down
1 change: 1 addition & 0 deletions mp/src/game/client/neo/ui/neo_root.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class CNeoRoot : public vgui::EditablePanel, public CGameEventListener

bool m_bOnLoadingScreen = false;
int m_iSavedYOffsets[NeoUI::MAX_SECTIONS] = {};
float m_flWideAs43 = 0.0f;
};

extern CNeoRoot *g_pNeoRoot;
3 changes: 2 additions & 1 deletion mp/src/game/client/neo/ui/neo_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ void BeginContext(NeoUI::Context *ctx, const NeoUI::Mode eMode, const wchar_t *w
g_pCtx->eMode = eMode;
g_pCtx->iLayoutY = -(g_pCtx->iYOffset[0] * g_pCtx->iRowTall);
g_pCtx->iWidget = 0;
g_pCtx->iWgXPos = static_cast<int>(g_pCtx->dPanel.wide * 0.4f);
g_pCtx->iSection = 0;
g_pCtx->iHasMouseInPanel = 0;
g_pCtx->iHorizontalWidth = 0;
g_pCtx->iHorizontalMargin = 0;
g_pCtx->flWgXPerc = 0.4f;
g_pCtx->bValueEdited = false;
g_pCtx->eButtonTextStyle = TEXTSTYLE_CENTER;
g_pCtx->eLabelTextStyle = TEXTSTYLE_LEFT;
Expand Down Expand Up @@ -194,6 +194,7 @@ void BeginSection(const bool bDefaultFocus)
g_pCtx->iLayoutY = -(g_pCtx->iYOffset[g_pCtx->iSection] * g_pCtx->iRowTall);
g_pCtx->iWidget = 0;
g_pCtx->iCanActives = 0;
g_pCtx->iWgXPos = static_cast<int>(g_pCtx->dPanel.wide * g_pCtx->flWgXPerc);

g_pCtx->iMouseRelX = g_pCtx->iMouseAbsX - g_pCtx->dPanel.x;
g_pCtx->iMouseRelY = g_pCtx->iMouseAbsY - g_pCtx->dPanel.y;
Expand Down
1 change: 1 addition & 0 deletions mp/src/game/client/neo/ui/neo_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ struct Context
int iPartitionY; // Only increments when Y-pos goes down
int iLayoutX;
int iLayoutY;
float flWgXPerc;
int iWgXPos;
int iYOffset[MAX_SECTIONS] = {};
bool abYMouseDragOffset[MAX_SECTIONS] = {};
Expand Down
2 changes: 2 additions & 0 deletions mp/src/game/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,8 @@ target_sources_grouped(
neo/neo_client.cpp
neo/neo_detpack.cpp
neo/neo_detpack.h
neo/neo_game_config.cpp
neo/neo_game_config.h
neo/neo_ghost_spawn_point.cpp
neo/neo_ghost_spawn_point.h
neo/neo_grenade.cpp
Expand Down
10 changes: 10 additions & 0 deletions mp/src/game/server/neo/neo_game_config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "neo_game_config.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

LINK_ENTITY_TO_CLASS(neo_game_config, CNEOGameConfig);

BEGIN_DATADESC(CNEOGameConfig)
DEFINE_KEYFIELD(m_GameType, FIELD_INTEGER, "GameType"),
END_DATADESC()
14 changes: 14 additions & 0 deletions mp/src/game/server/neo/neo_game_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include "cbase.h"
#include "baseentity.h"
#include "neo_gamerules.h"

class CNEOGameConfig : public CLogicalEntity
{
DECLARE_CLASS(CNEOGameConfig, CBaseEntity);
DECLARE_DATADESC();

public:
int m_GameType = NEO_GAME_TYPE_TDM;
};
2 changes: 1 addition & 1 deletion mp/src/game/server/neo/neo_ghost_spawn_point.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ class CNEOGhostSpawnPoint : public CPointEntity
DECLARE_CLASS(CNEOGhostSpawnPoint, CPointEntity);
};

#endif // NEO_GHOST_SPAWN_POINT_H
#endif // NEO_GHOST_SPAWN_POINT_H
160 changes: 129 additions & 31 deletions mp/src/game/shared/neo/neo_gamerules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "inetchannelinfo.h"
#include "neo_dm_spawn.h"
#include "neo_misc.h"
#include "neo_game_config.h"

extern ConVar weaponstay;
#endif
Expand All @@ -49,14 +50,33 @@ ConVar neo_sv_clantag_allow("neo_sv_clantag_allow", "1", FCVAR_REPLICATED, "", t
ConVar neo_sv_dev_test_clantag("neo_sv_dev_test_clantag", "", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Debug-mode only - Override all clantags with this value.");
#endif

ConVar neo_vote_game_mode("neo_vote_game_mode", "1", FCVAR_USERINFO, "Vote on game mode to play. TDM=0, CTG=1, VIP=2, DM=3", true, 0, true, NEO_GAME_TYPE__TOTAL - 1);
#define STR_GAMEOPTS "TDM=0, CTG=1, VIP=2, DM=3"
#define STR_GAMEBWOPTS "TDM=1, CTG=2, VIP=4, DM=8"
ConVar neo_vote_game_mode("neo_vote_game_mode", "1", FCVAR_USERINFO, "Vote on game mode to play. " STR_GAMEOPTS, true, 0, true, NEO_GAME_TYPE__TOTAL - 1);
ConVar neo_vip_eligible("neo_cl_vip_eligible", "1", FCVAR_ARCHIVE, "Eligible for VIP", true, 0, true, 1);
#ifdef GAME_DLL
ConVar sv_neo_vip_ctg_on_death("sv_neo_vip_ctg_on_death", "0", FCVAR_ARCHIVE, "Spawn Ghost when VIP dies, continue the game", true, 0, true, 1);
#endif

#ifdef GAME_DLL
ConVar sv_neo_change_game_type_mid_round("sv_neo_change_game_type_mid_round", "1", FCVAR_REPLICATED, "Allow game type change mid-match");
// NEO TODO (nullsystem): Change how voting done from convar to menu selection
enum eGamemodeEnforcement
{
GAMEMODE_ENFORCEMENT_MAP = 0, // Only use the gamemode enforced by the map
GAMEMODE_ENFORCEMENT_SINGLE, // Only use the single gamemode enforced by the server
GAMEMODE_ENFORCEMENT_RAND, // Randomly choose a gamemode on each map initialization based on a list
GAMEMODE_ENFORCEMENT_VOTE, // Allow vote by players on pre-match

GAMEMODE_ENFORCEMENT__TOTAL,
};
ConVar neo_sv_gamemode_enforcement("neo_sv_gamemode_enforcement", "0", FCVAR_REPLICATED,
"How the gamemode are determined. 0 = By map, 1 = By neo_sv_gamemode_single, 2 = Random, 3 = Pre-match voting",
true, 0.0f, true, GAMEMODE_ENFORCEMENT__TOTAL - 1);
ConVar neo_sv_gamemode_single("neo_sv_gamemode_single", "3", FCVAR_REPLICATED, "The gamemode that is enforced by the server. " STR_GAMEOPTS,
true, 0.0f, true, NEO_GAME_TYPE__TOTAL - 1);
ConVar neo_sv_gamemode_random_allow("neo_sv_gamemode_random_allow", "11", FCVAR_REPLICATED,
"In bitwise, the gamemodes that are allowed for random selection. Default = TDM+CTG+DM. " STR_GAMEBWOPTS,
true, 1.0f, true, (1 << NEO_GAME_TYPE__TOTAL)); // Can't be zero, minimum has to set to a bitwise value
#endif

#ifdef GAME_DLL
Expand Down Expand Up @@ -244,6 +264,7 @@ extern CBaseEntity *g_pLastJinraiSpawn, *g_pLastNSFSpawn;
static const char *s_NeoPreserveEnts[] =
{
"neo_gamerules",
"neo_game_config",
"info_player_attacker",
"info_player_defender",
"info_player_start",
Expand Down Expand Up @@ -366,10 +387,7 @@ CNEORules::CNEORules()
}
}

if (GetGameType() == NEO_GAME_TYPE_CTG || GetGameType() == NEO_GAME_TYPE_VIP)
{
ResetGhostCapPoints();
}
m_nGameTypeSelected = NEO_GAME_TYPE_CTG;
#endif

ResetMapSessionCommon();
Expand Down Expand Up @@ -641,13 +659,105 @@ void CNEORules::GetDMHighestScorers(
}
}

#ifdef GAME_DLL
void CNEORules::CheckGameType()
{
// Static as CNEORules doesn't persists through map changes
static int iStaticInitOnCmd = -1;
static int iStaticInitOnRandAllow = -1;
static bool staticGamemodesCanPick[NEO_GAME_TYPE__TOTAL] = {};
static int iStaticLastPick = -1; // Mostly so it doesn't repeat on array refresh

const int iGamemodeEnforce = neo_sv_gamemode_enforcement.GetInt();
const int iGamemodeRandAllow = neo_sv_gamemode_random_allow.GetInt();
// Update on what to select on first map load or server operator changes neo_sv_gamemode_enforcement
const bool bCheckOnGameType = (!m_bGamemodeTypeBeenInitialized || iGamemodeEnforce != iStaticInitOnCmd ||
iGamemodeRandAllow != iStaticInitOnRandAllow);
if (!bCheckOnGameType)
{
return;
}

// NEO NOTE (nullsystem): CNEORules always recreated on map change, yet entities properly found
// happens later. So checking and init on game type will execute here once.
switch (iGamemodeEnforce)
{
case GAMEMODE_ENFORCEMENT_SINGLE:
{
m_nGameTypeSelected = neo_sv_gamemode_single.GetInt();
} break;
case GAMEMODE_ENFORCEMENT_RAND:
{
const int iBWAllow = neo_sv_gamemode_random_allow.GetInt(); // Min of 1, cannot be zero
Assert(iBWAllow > 0);

// Check if all are used up
{
int iAllowsPicks = 0;
for (int i = 0; i < NEO_GAME_TYPE__TOTAL; ++i)
{
iAllowsPicks += staticGamemodesCanPick[i];
}
if (iAllowsPicks == 0 || iGamemodeRandAllow != iStaticInitOnRandAllow)
{
#ifdef DEBUG
DevMsg("Array reset!\n");
#endif
// Preset true to those not-allowed, preset false to those allowed
int iTotalPicks = 0;
for (int i = 0; i < NEO_GAME_TYPE__TOTAL; ++i)
{
const bool bCanPick = (iBWAllow & (1 << i));
iTotalPicks += bCanPick;
staticGamemodesCanPick[i] = bCanPick;
}
if (iTotalPicks <= 1)
{
iStaticLastPick = -1;
}
}
}

m_nGameTypeSelected = RandomInt(0, NEO_GAME_TYPE__TOTAL - 1);
for (int iWalk = 0;
(!staticGamemodesCanPick[m_nGameTypeSelected] || m_nGameTypeSelected == iStaticLastPick) &&
iWalk < NEO_GAME_TYPE__TOTAL;
++iWalk)
{
m_nGameTypeSelected = LoopAroundInArray(m_nGameTypeSelected + 1, NEO_GAME_TYPE__TOTAL);
}

#ifdef DEBUG
for (int i = 0; i < NEO_GAME_TYPE__TOTAL; ++i)
{
DevMsg("%d | %s: %s\n", i, NEO_GAME_TYPE_DESC_STRS[i].szStr, staticGamemodesCanPick[i] ? "Allowed" : "Not allowed");
}
DevMsg("Pick: %d | Prev: %d\n", m_nGameTypeSelected.Get(), iStaticLastPick);
#endif

staticGamemodesCanPick[m_nGameTypeSelected] = false;
iStaticLastPick = m_nGameTypeSelected;
} break;
default:
{
const auto pEntGameCfg = static_cast<CNEOGameConfig *>(gEntList.FindEntityByClassname(nullptr, "neo_game_config"));
m_nGameTypeSelected = (pEntGameCfg) ? pEntGameCfg->m_GameType : NEO_GAME_TYPE_CTG;
} break;
}
m_bGamemodeTypeBeenInitialized = true;
iStaticInitOnCmd = iGamemodeEnforce;
iStaticInitOnRandAllow = iGamemodeRandAllow;
}
#endif

void CNEORules::Think(void)
{
#ifdef GAME_DLL
const bool bIsIdleState = m_nRoundStatus == NeoRoundStatus::Idle || m_nRoundStatus == NeoRoundStatus::Warmup;
bool bIsPause = m_nRoundStatus == NeoRoundStatus::Pause;
if (bIsIdleState && gpGlobals->curtime > m_flNeoNextRoundStartTime)
{
CheckGameType();
StartNextRound();
return;
}
Expand Down Expand Up @@ -1881,18 +1991,18 @@ void CNEORules::StartNextRound()

CleanUpMap();

if (neo_sv_gamemode_enforcement.GetInt() == GAMEMODE_ENFORCEMENT_VOTE && m_nRoundStatus == NeoRoundStatus::Warmup)
{
GatherGameTypeVotes();
}

// NEO TODO (nullsystem): There should be a more sophisticated logic to be able to restore XP
// for when moving from idle to preroundfreeze, or in the future, competitive with whatever
// extra stuff in there. But to keep it simple: just clear if it was a warmup.
const bool clearXP = (m_nRoundStatus == NeoRoundStatus::Warmup);
SetRoundStatus(NeoRoundStatus::PreRoundFreeze);
++m_iRoundNumber;

if (!GetGameType() || sv_neo_change_game_type_mid_round.GetBool())
{
GatherGameTypeVotes();
}

for (int i = 1; i <= gpGlobals->maxClients; i++)
{
CNEO_Player *pPlayer = (CNEO_Player*)UTIL_PlayerByIndex(i);
Expand Down Expand Up @@ -1957,11 +2067,6 @@ void CNEORules::StartNextRound()

FireLegacyEvent_NeoRoundEnd();

if (!GetGameType() || sv_neo_change_game_type_mid_round.GetBool())
{
GatherGameTypeVotes();
}

char RoundMsg[27];
static_assert(sizeof(RoundMsg) == sizeof("- CTG ROUND 99 STARTED -\n\0"), "RoundMsg requires to fit round numbers up to 2 digits");
V_sprintf_safe(RoundMsg, "- %s ROUND %d STARTED -\n", GetGameTypeName(), Min(99, m_iRoundNumber.Get()));
Expand Down Expand Up @@ -2032,23 +2137,16 @@ void CNEORules::CreateStandardEntities(void)
#endif
}

const SZWSZTexts NEO_GAME_TYPE_DESC_STRS[NEO_GAME_TYPE__TOTAL] = {
SZWSZ_INIT("Team Deathmatch"),
SZWSZ_INIT("Capture the Ghost"),
SZWSZ_INIT("Extract or Kill the VIP"),
SZWSZ_INIT("Deathmatch"),
};

const char *CNEORules::GetGameDescription(void)
{
//DevMsg("Querying CNEORules game description\n");

// NEO TODO (Rain): get a neo_game_config so we can specify better
switch(GetGameType()) {
case NEO_GAME_TYPE_TDM:
return "Team Deathmatch";
case NEO_GAME_TYPE_CTG:
return "Capture the Ghost";
case NEO_GAME_TYPE_VIP:
return "Extract or Kill the VIP";
case NEO_GAME_TYPE_DM:
return "Deathmatch";
default:
return BaseClass::GetGameDescription();
}
return NEO_GAME_TYPE_DESC_STRS[GetGameType()].szStr;
}

const CViewVectors *CNEORules::GetViewVectors() const
Expand Down
5 changes: 5 additions & 0 deletions mp/src/game/shared/neo/neo_gamerules.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "GameEventListener.h"
#include "neo_player_shared.h"
#include "neo_misc.h"

#ifdef CLIENT_DLL
#include "c_neo_player.h"
Expand Down Expand Up @@ -107,6 +108,8 @@ enum NeoGameType {
NEO_GAME_TYPE__TOTAL // Number of game types
};

extern const SZWSZTexts NEO_GAME_TYPE_DESC_STRS[NEO_GAME_TYPE__TOTAL];

enum NeoRoundStatus {
Idle = 0,
Warmup,
Expand Down Expand Up @@ -257,6 +260,7 @@ class CNEORules : public CHL2MPRules, public CGameEventListener
bool m_bIgnoreOverThreshold = false;
bool ReadyUpPlayerIsReady(CNEO_Player *pNeoPlayer) const;

void CheckGameType();
void StartNextRound();

virtual const char* GetChatFormat(bool bTeamOnly, CBasePlayer* pPlayer) OVERRIDE;
Expand Down Expand Up @@ -376,6 +380,7 @@ class CNEORules : public CHL2MPRules, public CGameEventListener
int m_arrayiEntPrevCap[MAX_PLAYERS + 1]; // This is to check for cap-prevention workaround attempts
int m_iEntPrevCapSize = 0;
int m_iPrintHelpCounter = 0;
bool m_bGamemodeTypeBeenInitialized = false;
#endif
CNetworkVar(int, m_nRoundStatus);
CNetworkVar(int, m_nGameTypeSelected);
Expand Down
Loading

0 comments on commit 14c4282

Please sign in to comment.