diff --git a/edt/bms/bm_c1a1a.edt b/edt/bms/bm_c1a1a.edt index 342261d8..9ee10710 100644 --- a/edt/bms/bm_c1a1a.edt +++ b/edt/bms/bm_c1a1a.edt @@ -18,16 +18,6 @@ { "targetname" "global_newgame_spawner_suit" } - // don't know why this is not in the level - "add" - { - "classname" "env_message" - "targetname" "intro_title_custom" - "spawnflags" "0" - "messagevolume" "10" - "messageattenuation" "0" - "message" "CHAPTER3_TITLE" - } "modify" { "classname" "logic_auto" @@ -39,13 +29,6 @@ "target" "!player" "input" "SetHealth" } - "add" - { - "output" "OnMapSpawn" - "target" "intro_title_custom" - "input" "ShowMessage" - "delay" "0.75" - } } } // delay map start diff --git a/scripting/include/srccoop/classdef.inc b/scripting/include/srccoop/classdef.inc index 7c9499e8..58d3d071 100644 --- a/scripting/include/srccoop/classdef.inc +++ b/scripting/include/srccoop/classdef.inc @@ -638,6 +638,14 @@ methodmap CBaseEntity { SetEntityFlags(this.GetEntIndex(), iFlags); } + public void AddFlags(const int iFlags) + { + this.SetFlags(this.GetFlags() | iFlags); + } + public void RemoveFlags(const int iFlags) + { + this.SetFlags(this.GetFlags() & ~iFlags); + } public int GetEFlags() { return GetEntProp(this.GetEntIndex(), Prop_Data, "m_iEFlags"); @@ -1306,6 +1314,48 @@ methodmap CPointEntity < CBaseEntity } } +methodmap CWorld < CBaseEntity +{ + public CWorld() + { + return view_as(CBaseEntity(0)); + } + + public bool IsValid() + { + return true; + } + public bool GetChapterTitle(char[] szBuffer, const int iMaxLength) + { + return GetEntPropString(this.GetEntIndex(), Prop_Data, "m_iszChapterTitle", szBuffer, iMaxLength) != 0; + } +} + +methodmap CMessage < CPointEntity +{ + public CMessage(const int iEntIndex = -1) + { + return view_as(CPointEntity(iEntIndex)); + } + public static CMessage Create() + { + return CMessage(CreateEntityByName("env_message")); + } + + public bool GetMessage(char[] szBuffer, const int iMaxLength) + { + return GetEntPropString(this.GetEntIndex(), Prop_Data, "m_iszMessage", szBuffer, iMaxLength) != 0; + } + public void SetMessage(const char[] szMessage) + { + SetEntPropString(this.GetEntIndex(), Prop_Data, "m_iszMessage", szMessage); + } + public void ShowMessage() + { + this.AcceptInputStr("ShowMessage"); + } +} + methodmap CSceneEntity < CPointEntity { public CSceneEntity(const int iEntIndex = -1) @@ -2262,7 +2312,7 @@ methodmap CBasePlayer < CBaseCombatCharacter public void StartDucking() // (taken from point_teleport code) { this.SetButtons(this.GetButtons() | IN_DUCK); - this.SetFlags(this.GetFlags() | FL_DUCKING); + this.AddFlags(FL_DUCKING); int iClient = this.GetEntIndex(); SetEntProp(iClient, Prop_Send, "m_bDucked", true); SetEntProp(iClient, Prop_Send, "m_bDucking", true); @@ -2275,7 +2325,7 @@ methodmap CBasePlayer < CBaseCombatCharacter public void StopDucking() { this.SetButtons(this.GetButtons() & ~IN_DUCK); - this.SetFlags(this.GetFlags() & ~FL_DUCKING); + this.RemoveFlags(FL_DUCKING); int iClient = this.GetEntIndex(); SetEntProp(iClient, Prop_Send, "m_bDucked", false); SetEntProp(iClient, Prop_Send, "m_bDucking", false); diff --git a/scripting/include/srccoop/manager.inc b/scripting/include/srccoop/manager.inc index 756ee1bc..2d1ac1d5 100644 --- a/scripting/include/srccoop/manager.inc +++ b/scripting/include/srccoop/manager.inc @@ -17,6 +17,7 @@ enum struct CoopManagerData int m_iEnabledFeatures; bool m_bIsCoopMap; bool m_bStarted; + char m_szChapterTitle[64]; bool m_bMapHasDelayedOutput; int m_iSpawnedPlayerCountLastMap; int m_iSpawnedPlayerCount; @@ -51,6 +52,7 @@ methodmap CoopManager data.m_hStartTimer = null; data.m_pChangeLevelTimer = null; data.m_bStarted = false; + data.m_szChapterTitle = ""; data.m_iSpawnedPlayerCountLastMap = data.m_iSpawnedPlayerCount; data.m_iSpawnedPlayerCount = 0; data.m_iSecondsToStart = cellmax; @@ -83,6 +85,19 @@ methodmap CoopManager return false; } + public static void CheckAndAssignChapterTitle() + { + // We need to get the chapter title from `CWorld` when a `env_message` is created + // as the chapter title gets unassigned after the message is created. + // https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/mp/src/game/server/world.cpp#L681 + char szChapterTitle[64]; + CWorld().GetChapterTitle(szChapterTitle, sizeof(szChapterTitle)); + if (strlen(szChapterTitle) > 0) + { + strcopy(data.m_szChapterTitle, sizeof(data.m_szChapterTitle), szChapterTitle); + } + } + public static void EntitySpawnPost(CBaseEntity pEntity) { // find and hook output hooks for entity @@ -186,6 +201,18 @@ methodmap CoopManager Timer_DecrementUntilMapStart(null); } } + + public static void OnPlayerRunCmd(CBasePlayer pPlayer) + { + if (!data.m_bStarted) + { + CBaseCombatWeapon pWeapon = pPlayer.GetActiveWeapon(); + if (pWeapon != NULL_CBASEENTITY) + { + PreventWeaponFiring(pWeapon, 0.25); + } + } + } public static void OnPlayerSpawned(CBasePlayer pPlayer) { @@ -236,12 +263,12 @@ methodmap CoopManager { if (g_pLevelLump.m_iIntroType == INTRO_FADE) { + pPlayer.AddFlags(FL_ATCONTROLS | FL_FROZEN); // Freeze controls and angles. Client_ScreenFade(pPlayer.GetEntIndex(), 0, FFADE_STAYOUT); - pPlayer.SetFlags(pPlayer.GetFlags() | FL_ATCONTROLS); } else if (g_pLevelLump.m_iIntroType == INTRO_FREEZE) { - pPlayer.SetFlags(pPlayer.GetFlags() | FL_ATCONTROLS); + pPlayer.AddFlags(FL_ATCONTROLS); // Freeze controls. Client_ScreenFade(pPlayer.GetEntIndex(), Conf.FROZEN_FADE_DUR_IN, FFADE_OUT | FFADE_STAYOUT, _, Conf.FROZEN_FADE_COLOR[0], Conf.FROZEN_FADE_COLOR[1], Conf.FROZEN_FADE_COLOR[2], Conf.FROZEN_FADE_COLOR[3]); } @@ -539,6 +566,11 @@ public Action Timer_DecrementUntilMapStart(Handle hTimer) data.m_bStarted = true; data.m_flStartTime = GetGameTime(); + if (strlen(data.m_szChapterTitle) > 0) + { + CreateTimer(1.0, Timer_ShowChapterTitle, _, TIMER_FLAG_NO_MAPCHANGE); + } + if (g_pLevelLump.m_iIntroType == INTRO_FADE || g_pLevelLump.m_iIntroType == INTRO_FREEZE) { for (int i = 1; i <= MaxClients; i++) @@ -546,7 +578,10 @@ public Action Timer_DecrementUntilMapStart(Handle hTimer) if (IsClientInGame(i) && !IsFakeClient(i)) { CBasePlayer pClient = CBasePlayer(i); - pClient.SetFlags(pClient.GetFlags() & ~FL_ATCONTROLS); + // TODO: Look into this. + // `FL_FROZEN` is being removed even without the below line in Black Mesa (i doubt this affects other games). + // `CBM_MP_GameRules.SetStateEndTime` might be the culprit and might have side effects for other behaviors too. + pClient.RemoveFlags(FL_ATCONTROLS | FL_FROZEN); if (g_pLevelLump.m_iIntroType == INTRO_FREEZE) { Client_ScreenFade(pClient.GetEntIndex(), Conf.FROZEN_FADE_DUR_OUT, FFADE_PURGE | FFADE_IN, 1, @@ -647,5 +682,16 @@ public Action Callback_DeleteEntOnDelayedOutputFire(const char[] szOutput, int i } } +public void Timer_ShowChapterTitle(Handle hTimer) +{ + CMessage pMessage = CMessage.Create(); + if (pMessage != NULL_CBASEENTITY) + { + pMessage.SetMessage(data.m_szChapterTitle); + pMessage.SetSpawnFlags(SF_MESSAGE_ONCE | SF_MESSAGE_ALL); + pMessage.ShowMessage(); + } +} + // end shortcut #undef data \ No newline at end of file diff --git a/scripting/include/srccoop/playerpatch.inc b/scripting/include/srccoop/playerpatch.inc index 22aa2195..a9bf3b27 100644 --- a/scripting/include/srccoop/playerpatch.inc +++ b/scripting/include/srccoop/playerpatch.inc @@ -157,10 +157,10 @@ public Action OnPlayerRunCmd(int iClient, int &iButtons, int &iImpulse, float fV if (mouse[0] || mouse[1]) g_bPostTeamSelect[iClient] = true; - // Spectator fixes - // Credit: harper - - Obs_Mode iObsMode = view_as (GetEntProp(iClient, Prop_Send, "m_iObserverMode")); + CBasePlayer pClient = CBasePlayer(iClient); + + // Spectator fixes; Credits: harper + Obs_Mode iObsMode = view_as(GetEntProp(iClient, Prop_Send, "m_iObserverMode")); if (iObsMode > OBS_MODE_DEATHCAM) { // hide bugged ctrl menu @@ -168,7 +168,6 @@ public Action OnPlayerRunCmd(int iClient, int &iButtons, int &iImpulse, float fV ShowVGUIPanel(iClient, "specmenu", _, false); CBasePlayer pTarget = CBasePlayer(GetEntPropEnt(iClient, Prop_Send, "m_hObserverTarget")); - CBasePlayer pClient = CBasePlayer(iClient); // Make sure target is not an info_observer_menu // force free-look where appropriate - this removes the extra (pointless) third person spec mode @@ -178,6 +177,8 @@ public Action OnPlayerRunCmd(int iClient, int &iButtons, int &iImpulse, float fV } } + CoopManager.OnPlayerRunCmd(pClient); + if (g_iAddButtons[iClient]) { iButtons |= g_iAddButtons[iClient]; diff --git a/scripting/include/srccoop/typedef_game.inc b/scripting/include/srccoop/typedef_game.inc index 1cba5ed1..d38548a8 100644 --- a/scripting/include/srccoop/typedef_game.inc +++ b/scripting/include/srccoop/typedef_game.inc @@ -65,6 +65,7 @@ #define SF_PLAYER_EQUIP_STRIP_WEAPONS 2 #define SF_PLAYER_EQUIP_STRIP_SUIT 4 +#define SF_MESSAGE_ONCE 0x0001 // Fade in, not out #define SF_MESSAGE_ALL 0x0002 // Pickupables spawnflags diff --git a/scripting/srccoop.sp b/scripting/srccoop.sp index bfee77f0..07c74c4c 100644 --- a/scripting/srccoop.sp +++ b/scripting/srccoop.sp @@ -713,6 +713,12 @@ public void OnEntityCreated(int iEntIndex, const char[] szClassname) } #endif + if (strcmp(szClassname, "env_message", false) == 0) + { + CoopManager.CheckAndAssignChapterTitle(); + return; + } + #if defined ENTPATCH_TRIGGER_CHANGELEVEL if (strcmp(szClassname, "trigger_changelevel") == 0) {