CBaseTrigger: Added CBaseOutput, in preparation to support Source styled input/output mechanisms

This commit is contained in:
Marco Cawthorne 2020-09-20 10:43:13 +02:00
parent 9f95fd11c2
commit dc47146e99
8 changed files with 325 additions and 38 deletions

View File

@ -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

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
*
* 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;
}

View File

@ -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);
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
*
* 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;
};

View File

@ -15,6 +15,7 @@
*/
#include "baseentity.h"
#include "basetrigger.h"
void FX_Spark(vector, vector);
void FX_BreakModel(int, vector, vector, vector, float);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}