diff --git a/gamedata/srccoop.games.txt b/gamedata/srccoop.games.txt index 0f1da043..ade9b216 100644 --- a/gamedata/srccoop.games.txt +++ b/gamedata/srccoop.games.txt @@ -598,6 +598,13 @@ } } } + "CPropChargerBase::ChargerThink" + { + "offset" "CPropChargerBase::ChargerThink" + "hooktype" "entity" + "return" "void" + "this" "entity" + } // _____ ______ _______ ____ _ _ _____ // | __ \| ____|__ __/ __ \| | | | __ \ @@ -1360,6 +1367,11 @@ "windows" "39" "linux" "40" } + "CPropChargerBase::ChargerThink" // CPropChargerBase::ChargerThink() + { + "windows" "239" + "linux" "289" + } } } diff --git a/scripting/include/srccoop/blackmesa/classdef.inc b/scripting/include/srccoop/blackmesa/classdef.inc index 1b89cf6c..d0a33228 100644 --- a/scripting/include/srccoop/blackmesa/classdef.inc +++ b/scripting/include/srccoop/blackmesa/classdef.inc @@ -217,4 +217,37 @@ methodmap CWeapon_Crossbow < CBlackMesaBaseCombatWeaponIronsights { SetEntProp(this.GetEntIndex(), Prop_Send, "m_bNeedsToRearm", bRearming); } -} \ No newline at end of file +} + +methodmap CPropChargerBase < CBaseAnimating +{ + public CPropChargerBase(const int iEntIndex = -1) + { + return view_as(CBaseAnimating(iEntIndex)); + } + + public float GetChargerRate() + { + return GetEntPropFloat(this.GetEntIndex(), Prop_Data, "m_flChargerRate"); + } + public void SetChargerRate(const float flChargeRate) + { + SetEntPropFloat(this.GetEntIndex(), Prop_Data, "m_flChargerRate", flChargeRate); + } + public float GetChargerAmount() + { + return GetEntPropFloat(this.GetEntIndex(), Prop_Data, "m_flChargerAmt"); + } + public void SetChargerAmount(const float flChargeAmount) + { + SetEntPropFloat(this.GetEntIndex(), Prop_Data, "m_flChargerAmt", flChargeAmount); + } + public float GetChargerRange() + { + return GetEntPropFloat(this.GetEntIndex(), Prop_Data, "m_flChargerRange"); + } + public void SetChargerRange(const float flChargerRange) + { + SetEntPropFloat(this.GetEntIndex(), Prop_Data, "m_flChargerRange", flChargerRange); + } +} diff --git a/scripting/include/srccoop/blackmesa/entitypatch.inc b/scripting/include/srccoop/blackmesa/entitypatch.inc index c4da6148..1bffcffc 100644 --- a/scripting/include/srccoop/blackmesa/entitypatch.inc +++ b/scripting/include/srccoop/blackmesa/entitypatch.inc @@ -114,21 +114,101 @@ public Action Hook_PuffballFungusDmg(int victim, int &attacker, int &inflictor, } //------------------------------------------------------ -// CPropRadiationCharger - prop_radiation_charger, CPropHevCharger - prop_hev_charger -// ToDo: reimplement on serverside (avoid clientside effects) +// CPropRadiationCharger - prop_radiation_charger +// +// # TODO +// +// - Set "m_bChargingPlayers" on charger to apply screen space effects (currently this will apply to all players) +// - Apply laser particle effect on each individual player within radius +// - Play active sound +// - Call `CBlackMesaPlayer::SetRadiationChargeState` on players to apply gluon weapon effects //------------------------------------------------------ -/** -public MRESReturn Hook_PropChargerThink(int _this) +public MRESReturn Hook_PropRadiationChargerThink(int _this) { - SetLocalPlayerEntity(_this); - return MRES_Ignored; + CPropChargerBase pCharger = CPropChargerBase(_this); + + //float flArmorCharge = (pCharger.GetChargerRate() / pCharger.GetChargerAmount()) * GetGameFrameTime(); + + float flChargerRange = pCharger.GetChargerRange(); + float vec3EntityPosition[3]; + pCharger.GetAbsOrigin(vec3EntityPosition); + + for (int i = 1; i <= MaxClients; ++i) + { + CBlackMesaPlayer pPlayer = CBlackMesaPlayer(i); + if (pPlayer != NULL_CBASEENTITY) + { + float vec3PlayerPosition[3]; + pPlayer.GetAbsOrigin(vec3PlayerPosition); + + if (GetVectorDistance(vec3EntityPosition, vec3PlayerPosition) <= flChargerRange) + { + // Naive reimplementation of `CPropRadiationCharger::ShouldApplyEffect` and `CPropRadiationCharger::ApplyEffect`. + // The original implementation uses frametime while thinking every 0.1 seconds. + // As a result, the value will always increment by 1 unless + // `m_flChargerRate` is set to a extremely high value or `m_flChargerAmt` is set to a extremely low value. + // The original implementation also will overflow the max amount if the increment value is over `1`. + int iAmmoEnergy = pPlayer.GetAmmoFromIndex(AMMO_ENERGY); + if (iAmmoEnergy < 100) // TODO: Programatically get the max reserve ammo. + { + pPlayer.SetAmmoFromIndex(AMMO_ENERGY, iAmmoEnergy + 1); + } + } + } + } + + pCharger.SetNextThink(GetGameTime() + 0.1); + + return MRES_Supercede; } -public MRESReturn Hook_PropChargerThinkPost(int _this) + +//------------------------------------------------------ +// CPropHevCharger - prop_hev_charger +// +// # TODO +// +// - Set "m_bChargingPlayers" on charger to apply screen space effects (currently this will apply to all players) +// - Apply laser particle effect on each individual player within radius +// - Play active sound +//------------------------------------------------------ +public MRESReturn Hook_PropHevChargerThink(int _this) { - ClearLocalPlayerEntity(); - return MRES_Ignored; + CPropChargerBase pCharger = CPropChargerBase(_this); + + //float flArmorCharge = (pCharger.GetChargerRate() / pCharger.GetChargerAmount()) * GetGameFrameTime(); + + float flChargerRange = pCharger.GetChargerRange(); + float vec3EntityPosition[3]; + pCharger.GetAbsOrigin(vec3EntityPosition); + + for (int i = 1; i <= MaxClients; ++i) + { + CBlackMesaPlayer pPlayer = CBlackMesaPlayer(i); + if (pPlayer != NULL_CBASEENTITY) + { + float vec3PlayerPosition[3]; + pPlayer.GetAbsOrigin(vec3PlayerPosition); + + if (GetVectorDistance(vec3EntityPosition, vec3PlayerPosition) <= flChargerRange) + { + // Naive reimplementation of `CPropHevCharger::ShouldApplyEffect` and `CPropHevCharger::ApplyEffect`. + // The original implementation uses frametime while thinking every 0.1 seconds. + // As a result, the value will always increment by 1 unless + // `m_flChargerRate` is set to a extremely high value or `m_flChargerAmt` is set to a extremely low value. + // The original implementation also will overflow the max amount if the increment value is over `1`. + int iArmor = pPlayer.GetArmor(); + if (pPlayer.HasSuit() && iArmor < 100) + { + pPlayer.SetArmor(iArmor + 1); + } + } + } + } + + pCharger.SetNextThink(GetGameTime() + 0.1); + + return MRES_Supercede; } -**/ //------------------------------------------------------ // TE - BlackMesa Shot diff --git a/scripting/include/srccoop/classdef.inc b/scripting/include/srccoop/classdef.inc index 8b5171c1..51d58211 100644 --- a/scripting/include/srccoop/classdef.inc +++ b/scripting/include/srccoop/classdef.inc @@ -34,6 +34,7 @@ Handle g_pShouldPlayerAvoid; Handle g_pRemoveAllItems; Handle g_pCreateViewModel; Handle g_pSetNextThink; +Handle g_pSetNextThinkNullString; Handle g_pIsNPC; Handle g_pSendWeaponAnim; Handle g_pWeaponSwitch; @@ -90,6 +91,16 @@ stock void InitClassdef(GameData pGameConfig) if (!(g_pSetNextThink = EndPrepSDKCall())) SetFailState("Could not prep SDK call %s", szSetNextThink); } + StartPrepSDKCall(SDKCall_Entity); + if (!PrepSDKCall_SetFromConf(pGameConfig, SDKConf_Signature, szSetNextThink)) + LogMessage("Could not obtain gamedata signature %s", szSetNextThink); + else + { + PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); + PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); + if (!(g_pSetNextThinkNullString = EndPrepSDKCall())) SetFailState("Could not prep SDK call %s", szSetNextThink); + } + char szIsNpc[] = "CBaseEntity::IsNPC"; StartPrepSDKCall(SDKCall_Entity); if (!PrepSDKCall_SetFromConf(pGameConfig, SDKConf_Virtual, szIsNpc)) @@ -598,6 +609,14 @@ methodmap CBaseEntity { SetEntProp(this.GetEntIndex(), Prop_Data, "m_iEFlags", iEFlags); } + public void AddEFlags(const int iEFlags) + { + this.SetEFlags(this.GetEFlags() | iEFlags); + } + public void RemoveEFlags(const int iEFlags) + { + this.SetEFlags(this.GetEFlags() & ~iEFlags); + } public int GetEdictFlags() { return GetEdictFlags(this.GetEntIndex()); @@ -781,11 +800,18 @@ methodmap CBaseEntity public float GetNextThink() { int iThinkTick = this.GetNextThinkTick(); - return iThinkTick == -1? -1.0 : TICKS_TO_TIME(iThinkTick); + return iThinkTick == -1 ? -1.0 : TICKS_TO_TIME(iThinkTick); } public void SetNextThink(float flNextThink, const char[] szContext = NULL_STRING) { - SDKCall(g_pSetNextThink, this.entindex, flNextThink, szContext); + if (strlen(szContext) > 0) + { + SDKCall(g_pSetNextThink, this.entindex, flNextThink, szContext); + } + else + { + SDKCall(g_pSetNextThinkNullString, this.entindex, flNextThink, 0); + } } public int GetSimulationTick() { diff --git a/scripting/include/srccoop/globals.inc b/scripting/include/srccoop/globals.inc index 0b32cea0..203086c9 100644 --- a/scripting/include/srccoop/globals.inc +++ b/scripting/include/srccoop/globals.inc @@ -157,6 +157,10 @@ DynamicDetour hkIgnorePredictionCull; DynamicHook hkDispatchEffect; #endif +#if defined ENTPATCH_BM_PROP_CHARGERS +DynamicHook hkPropChargerThink; +#endif + #if defined SRCCOOP_HL2DM && defined PLAYERPATCH_SERVERSIDE_RAGDOLLS DynamicHook hkCreateRagdollEntity; #endif diff --git a/scripting/include/srccoop/playerpatch.inc b/scripting/include/srccoop/playerpatch.inc index 0d7a418e..40d276cb 100644 --- a/scripting/include/srccoop/playerpatch.inc +++ b/scripting/include/srccoop/playerpatch.inc @@ -58,7 +58,6 @@ public void Hook_PlayerPreThinkPost(int iClient) if (pPlayer.IsAlive()) { #if defined SRCCOOP_BLACKMESA - CBaseCombatWeapon pWeapon = pPlayer.GetActiveWeapon(); if (pWeapon.IsValid()) { @@ -86,7 +85,6 @@ public void Hook_PlayerPreThinkPost(int iClient) } } } - #endif // SRCCOOP_BLACKMESA } else // Dead diff --git a/scripting/srccoop.sp b/scripting/srccoop.sp index 63cda031..18fee2ac 100644 --- a/scripting/srccoop.sp +++ b/scripting/srccoop.sp @@ -107,6 +107,10 @@ void LoadGameData() LoadDHookVirtual(pGameConfig, hkBaseNpcRunTask, "CAI_BaseNPC::RunTask"); #endif + #if defined ENTPATCH_BM_PROP_CHARGERS + LoadDHookVirtual(pGameConfig, hkPropChargerThink, "CPropChargerBase::ChargerThink"); + #endif + #if defined SRCCOOP_HL2DM && defined PLAYERPATCH_SERVERSIDE_RAGDOLLS LoadDHookVirtual(pGameConfig, hkCreateRagdollEntity, "CBasePlayer::CreateRagdollEntity"); #endif @@ -856,10 +860,15 @@ public void OnEntityCreated(int iEntIndex, const char[] szClassname) #endif #if defined ENTPATCH_BM_PROP_CHARGERS - if (strcmp(szClassname, "prop_hev_charger") == 0 || strcmp(szClassname, "prop_radiation_charger") == 0) + if (strcmp(szClassname, "prop_radiation_charger") == 0) + { + DHookEntity(hkPropChargerThink, false, iEntIndex, _, Hook_PropRadiationChargerThink); + return; + } + + if (strcmp(szClassname, "prop_hev_charger") == 0) { - //DHookEntity(hkThink, false, iEntIndex, _, Hook_PropChargerThink); - //DHookEntity(hkThink, true, iEntIndex, _, Hook_PropChargerThinkPost); + DHookEntity(hkPropChargerThink, false, iEntIndex, _, Hook_PropHevChargerThink); return; } #endif