From dc47146e99268e3496fa110c081a3f02e8fda34b Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Sun, 20 Sep 2020 10:43:13 +0200 Subject: [PATCH] CBaseTrigger: Added CBaseOutput, in preparation to support Source styled input/output mechanisms --- src/gs-entbase/server.src | 1 + src/gs-entbase/server/baseoutput.cpp | 69 +++++++++ src/gs-entbase/server/basetrigger.cpp | 160 ++++++++++++++++----- src/gs-entbase/server/basetrigger.h | 63 ++++++++ src/gs-entbase/server/defs.h | 1 + src/gs-entbase/server/trigger_multiple.cpp | 18 ++- src/gs-entbase/server/trigger_once.cpp | 25 ++++ src/server/valve/item_suit.cpp | 26 +++- 8 files changed, 325 insertions(+), 38 deletions(-) create mode 100644 src/gs-entbase/server/baseoutput.cpp create mode 100644 src/gs-entbase/server/basetrigger.h diff --git a/src/gs-entbase/server.src b/src/gs-entbase/server.src index 29fb310d..ac8e0cd5 100644 --- a/src/gs-entbase/server.src +++ b/src/gs-entbase/server.src @@ -7,6 +7,7 @@ baseentity.h decals.h materials.h server/baseentity.cpp +server/baseoutput.cpp server/basetrigger.cpp server/basemonster.cpp server/basenpc.cpp diff --git a/src/gs-entbase/server/baseoutput.cpp b/src/gs-entbase/server/baseoutput.cpp new file mode 100644 index 00000000..daecdf15 --- /dev/null +++ b/src/gs-entbase/server/baseoutput.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* inspired by https://developer.valvesoftware.com/wiki/Inputs_and_Outputs */ + +/* modern trigger architecture */ +class CBaseOutput:CBaseEntity +{ + entity m_eActivator; + string m_strTarget; + string m_strInput; + string m_strData; + float m_flDelay; + int m_iCount; + int m_iOldCount; + + void(void) CBaseOutput; + virtual void(void) TriggerOutput; + virtual void(void) SpawnInit; + virtual void(void) Respawn; +}; + +void +CBaseOutput::TriggerOutput(void) +{ + /* plug into all the inputs. */ + for (entity f = world; (f = find(f, ::targetname, m_strTarget));) { + CBaseTrigger trigger = (CBaseTrigger)f; + + if (trigger.Input != __NULL__) { + trigger.Input(m_eActivator, m_strInput, m_strData); + } + } + + /* we're not -1 (infinite) and we've still got one use to deduct */ + if (m_iCount > 0) + m_iCount--; +} + +void +CBaseOutput::SpawnInit(void) +{ + /* EMPTY! */ +} + +void +CBaseOutput::Respawn(void) +{ + m_iCount = m_iOldCount; +} + +void +CBaseOutput::CBaseOutput(void) +{ + gflags = GF_CANRESPAWN; +} diff --git a/src/gs-entbase/server/basetrigger.cpp b/src/gs-entbase/server/basetrigger.cpp index c50d52a7..3b745a79 100644 --- a/src/gs-entbase/server/basetrigger.cpp +++ b/src/gs-entbase/server/basetrigger.cpp @@ -14,41 +14,87 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -enum -{ - USE_TOGGLE, - USE_CONTINOUS -}; - -enum +/* modern trigger architecture */ +void +CBaseTrigger::UseOutput(entity act, string outname) { - TRIG_OFF, - TRIG_ON, - TRIG_TOGGLE -}; + for (entity f = world; (f = find(f, ::targetname, outname));) { + CBaseOutput op = (CBaseOutput)f; + /* no more tries and not -1 (infinite) */ + if (op.m_iCount == 0) { + return; + } -class CBaseTrigger:CBaseEntity + op.m_eActivator = act; + op.think = CBaseOutput::TriggerOutput; + op.nextthink = time + op.m_flDelay; + } +} + +/* input is a 4-5 parameter, commar separated string, output is the targetname + of a minion entity that'll handle the triggering (and counting down of uses) + as defined in the Source Input/Output specs */ +string +CBaseTrigger::CreateOutput(string outmsg) { - int m_strGlobalState; - string m_strKillTarget; - string m_strMessage; - string m_strMaster; - float m_flDelay; - int m_iUseType; - int m_iTeam; - int m_iValue; + static int outcount = 0; + float c; - void(void) CBaseTrigger; + if (!outmsg) + return ""; - virtual void(entity, int) Trigger; - virtual void(entity, int) UseTargets; - virtual void(entity, int, float) UseTargets_Delay; - virtual int(void) GetValue; - virtual int(void) GetMaster; - virtual void(void) InitBrushTrigger; - virtual void(void) InitPointTrigger; - virtual void(string, string) SpawnKey; -}; + string outname = sprintf("output_%i\n", outcount); + outcount++; + + c = tokenizebyseparator(outmsg, ","); + for (float i = 1; i < c; i+=5) { + CBaseOutput new_minion = spawn(CBaseOutput); + + new_minion.classname = "triggerminion"; + new_minion.targetname = outname; + new_minion.m_strTarget = substring(argv(i), 1,-1); + new_minion.m_strInput = substring(argv(i+1), 1,-1); + new_minion.m_strData = substring(argv(i+2), 1,-1); + new_minion.m_flDelay = stof(substring(argv(i+3), 1,-1)); + new_minion.m_iCount = stoi(substring(argv(i+4), 1,-1)); + new_minion.m_iOldCount = new_minion.m_iCount; + } + + /* print final debug output */ + for (entity f = world; (f = find(f, ::targetname, outname));) { + CBaseOutput new_minion = (CBaseOutput)f; + print(sprintf("^2%s::OnTrigger report:\n", classname)); + print(sprintf("Target: %s\n", new_minion.m_strTarget)); + print(sprintf("Input: %s\n", new_minion.m_strInput)); + print(sprintf("Data Message: %s\n", new_minion.m_strData)); + print(sprintf("Delay: %f\n", new_minion.m_flDelay)); + print(sprintf("Uses: %i\n\n", new_minion.m_iCount)); + } + + /* return the minion's name that'll act as the trigger */ + return outname; +} + +/* entities receive the inputs here and need to act on intype and data + accordingly. this is just a stub for unknown event troubleshooting */ +void +CBaseTrigger::Input(entity act, string intype, string data) +{ + if (data != "") + dprint(sprintf("^2%s::^3Input^7: Receives input %s from %s with data %s\n", + this.classname, intype, act.classname, data)); + else + dprint(sprintf("^2%s::^3Input^7: Receives input %s from %s\n", + this.classname, intype, act.classname)); +} + +/* legacy trigger architecture */ +void +CBaseTrigger::Trigger(entity act, int state) +{ + dprint(sprintf("^2%s::^3Input^7: Triggerd by %s with no consequence\n", + this.classname, act.classname)); +} void CBaseTrigger::UseTargets(entity act, int state) @@ -136,12 +182,6 @@ CBaseTrigger::GetMaster(void) return t.GetValue(); } -void -CBaseTrigger::Trigger(entity act, int state) -{ - -} - void CBaseTrigger::InitPointTrigger(void) { @@ -158,6 +198,48 @@ CBaseTrigger::InitBrushTrigger(void) SetRenderMode(RM_TRIGGER); } +void CompilerTest(void) +{ + string outmsg = ",_control_retinal2,_BeginSequence,_,_0,_-1,_control_retinal3,_BeginSequence,_,_0,_-1,_control_retinal1,_BeginSequence,_,_0,_-1"; + + string out_targetname; + string out_name; + string out_in; + string out_data; + float out_delay; + int out_uses; + float c; + static int outcount = 0; + + out_targetname = sprintf("output_%i\n", outcount); + outcount++; + + c = tokenizebyseparator(outmsg, ","); + for (float i = 1; i < c; i+=5) { + out_name = substring(argv(i), 1,-1); + out_in = substring(argv(i+1), 1,-1); + out_data = substring(argv(i+2), 1,-1); + out_delay = stof(substring(argv(i+3), 1,-1)); + out_uses = stoi(substring(argv(i+4), 1,-1)); + + print(sprintf("%d: %s\n", i, out_name)); + print(sprintf("%d: %s\n", i, out_in)); + print(sprintf("%d: %s\n", i, out_data)); + print(sprintf("%d: %d\n", i, out_delay)); + print(sprintf("%d: %i\n", i, out_uses)); + + CBaseOutput new_minion = spawn(CBaseOutput); + new_minion.classname = "triggerminion"; + new_minion.targetname = out_targetname; + new_minion.m_strTarget = out_name; + new_minion.m_strInput = out_in; + new_minion.m_strData = out_data; + new_minion.m_flDelay = out_delay; + new_minion.m_iCount = out_uses; + new_minion.m_iOldCount = out_uses; + } +} + void CBaseTrigger::SpawnKey(string strKey, string strValue) { @@ -177,6 +259,10 @@ CBaseTrigger::SpawnKey(string strKey, string strValue) case "delay": m_flDelay = stof(strValue); break; + case "OnTrigger": + strValue = strreplace(",", ",_", strValue); + m_strOnTrigger = strcat(m_strOnTrigger, ",_", strValue); + break; default: CBaseEntity::SpawnKey(strKey, strValue); break; @@ -187,4 +273,6 @@ void CBaseTrigger::CBaseTrigger(void) { CBaseEntity::CBaseEntity(); + + m_strOnTrigger = CreateOutput(m_strOnTrigger); } diff --git a/src/gs-entbase/server/basetrigger.h b/src/gs-entbase/server/basetrigger.h new file mode 100644 index 00000000..df54f0ed --- /dev/null +++ b/src/gs-entbase/server/basetrigger.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +enum +{ + USE_TOGGLE, + USE_CONTINOUS +}; + +enum +{ + TRIG_OFF, + TRIG_ON, + TRIG_TOGGLE +}; + +class CBaseTrigger:CBaseEntity +{ + int m_strGlobalState; + string m_strKillTarget; + string m_strMessage; + string m_strMaster; + int m_iUseType; + int m_iTeam; + int m_iValue; + + void(void) CBaseTrigger; + + /* modern trigger architecture */ + string m_strOnTrigger; + virtual void(entity, string) UseOutput; + virtual string(string) CreateOutput; + virtual void(entity, string, string) Input; + + /* legacy trigger architecture */ + float m_flDelay; + virtual void(entity, int) Trigger; + virtual void(entity, int) UseTargets; + virtual void(entity, int, float) UseTargets_Delay; + + /* master feature */ + virtual int(void) GetValue; + virtual int(void) GetMaster; + + /* spawn setup helpers */ + virtual void(void) InitBrushTrigger; + virtual void(void) InitPointTrigger; + + virtual void(string, string) SpawnKey; +}; diff --git a/src/gs-entbase/server/defs.h b/src/gs-entbase/server/defs.h index 661576a3..ee8679b7 100644 --- a/src/gs-entbase/server/defs.h +++ b/src/gs-entbase/server/defs.h @@ -15,6 +15,7 @@ */ #include "baseentity.h" +#include "basetrigger.h" void FX_Spark(vector, vector); void FX_BreakModel(int, vector, vector, vector, float); diff --git a/src/gs-entbase/server/trigger_multiple.cpp b/src/gs-entbase/server/trigger_multiple.cpp index ff2857de..c0ad944e 100644 --- a/src/gs-entbase/server/trigger_multiple.cpp +++ b/src/gs-entbase/server/trigger_multiple.cpp @@ -36,8 +36,9 @@ enumflags class trigger_multiple:CBaseTrigger { - float m_flDelay; float m_flWait; + string m_strOnStartTouch; + void(void) trigger_multiple; virtual void(void) touch; virtual void(void) Respawn; @@ -61,6 +62,13 @@ trigger_multiple::touch(void) } } + /* modern */ + if (!target) { + UseOutput(other, m_strOnStartTouch); + return; + } + + /* legacy */ if (m_flDelay > 0) { UseTargets_Delay(other, TRIG_TOGGLE, m_flDelay); } else { @@ -88,6 +96,11 @@ trigger_multiple::SpawnKey(string strKey, string strValue) case "wait": m_flWait = stof(strValue); break; + case "OnStartTouch": + case "OnStartTouchAll": + strValue = strreplace(",", ",_", strValue); + m_strOnStartTouch = strcat(m_strOnStartTouch, ",_", strValue); + break; default: CBaseTrigger::SpawnKey(strKey, strValue); } @@ -97,4 +110,7 @@ void trigger_multiple::trigger_multiple(void) { CBaseTrigger::CBaseTrigger(); + + if (m_strOnStartTouch) + m_strOnStartTouch = CreateOutput(m_strOnStartTouch); } diff --git a/src/gs-entbase/server/trigger_once.cpp b/src/gs-entbase/server/trigger_once.cpp index 439da757..f7967e3e 100644 --- a/src/gs-entbase/server/trigger_once.cpp +++ b/src/gs-entbase/server/trigger_once.cpp @@ -35,10 +35,13 @@ enumflags class trigger_once:CBaseTrigger { + string m_strOnStartTouch; + void(void) trigger_once; virtual void(void) touch; virtual void(void) Respawn; + virtual void(string, string) SpawnKey; }; void @@ -54,6 +57,11 @@ trigger_once::touch(void) solid = SOLID_NOT; /* make inactive */ m_iValue = 1; + if (!target) { + UseOutput(other, m_strOnStartTouch); + return; + } + if (m_flDelay > 0) { CBaseTrigger::UseTargets_Delay(other, TRIG_TOGGLE, m_flDelay); } else { @@ -70,8 +78,25 @@ trigger_once::Respawn(void) InitBrushTrigger(); } +void +trigger_once::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "OnStartTouch": + strValue = strreplace(",", ",_", strValue); + m_strOnStartTouch = strcat(m_strOnStartTouch, ",_", strValue); + break; + default: + CBaseTrigger::SpawnKey(strKey, strValue); + break; + } +} + void trigger_once::trigger_once(void) { CBaseTrigger::CBaseTrigger(); + + if (m_strOnStartTouch) + m_strOnStartTouch = CreateOutput(m_strOnStartTouch); } diff --git a/src/server/valve/item_suit.cpp b/src/server/valve/item_suit.cpp index d5e61be1..c839a089 100644 --- a/src/server/valve/item_suit.cpp +++ b/src/server/valve/item_suit.cpp @@ -24,10 +24,13 @@ Provides the player with armor, a flashlight and a Heads-Up-Display. */ class item_suit:CBaseTrigger { + string m_strOnPlayerTouch; + void(void) item_suit; virtual void(void) touch; virtual void(void) Respawn; + virtual void(string, string) SpawnKey; }; void item_suit::touch(void) @@ -47,7 +50,12 @@ void item_suit::touch(void) pl.g_items |= ITEM_SUIT; m_iValue = TRUE; - CBaseTrigger::UseTargets(other, TRIG_TOGGLE); + if (!target) { + UseOutput(other, m_strOnPlayerTouch); + return; + } else { + CBaseTrigger::UseTargets(other, TRIG_TOGGLE); + } if (real_owner || cvar("sv_playerslots") == 1) { remove(self); @@ -74,6 +82,20 @@ void item_suit::Respawn(void) Sound_Play(this, CHAN_ITEM, "item.respawn"); } +void +item_suit::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "OnPlayerTouch": + strValue = strreplace(",", ",_", strValue); + m_strOnPlayerTouch = strcat(m_strOnPlayerTouch, ",_", strValue); + break; + default: + CBaseTrigger::SpawnKey(strKey, strValue); + break; + } +} + void item_suit::item_suit(void) { model = "models/w_suit.mdl"; @@ -81,4 +103,6 @@ void item_suit::item_suit(void) precache_sound("fvox/hev_logon.wav"); precache_sound("fvox/bell.wav"); CBaseTrigger::CBaseTrigger(); + + m_strOnPlayerTouch = CreateOutput(m_strOnPlayerTouch); }