nuclide/src/gs-entbase/shared/NSIO.qc

345 lines
8.1 KiB
Plaintext

/*
* Copyright (c) 2016-2021 Marco Cawthorne <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.
*/
#ifdef CLIENT
string __fullspawndata;
#endif
void
NSIO::Init(void)
{
#ifdef CLIENT
isCSQC = TRUE;
effects |= EF_NOSHADOW;
#endif
#ifdef CLIENT
for (int i = 0; i < (tokenize(__fullspawndata) - 1); i += 2)
SpawnKey(argv(i), argv(i+1));
#else
for (int i = 1; i < (tokenize(__fullspawndata) - 1); i += 2)
SpawnKey(argv(i), argv(i+1));
#endif
#ifdef SERVER
/* some entity might involuntarily call SpawnInit as part of being
a member of NSIO. So we need to make sure that it doesn't
inherit stuff from the last previously loaded entity */
__fullspawndata = "";
#endif
Initialized();
}
void
NSIO::Initialized(void)
{
}
#ifdef SERVER
/* Input/Output system */
void
NSIO::UseOutput(entity act, string outname)
{
if (!outname)
return;
for (entity f = world; (f = find(f, ::targetname, outname));) {
NSOutput op = (NSOutput)f;
/* no more tries and not -1 (infinite) */
if (op.m_iCount == 0) {
return;
}
op.m_eActivator = act;
op.think = NSOutput::TriggerOutput;
op.nextthink = time + op.m_flDelay;
}
}
/* input is a 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
NSIO::CreateOutput(string outmsg)
{
static int outcount = 0;
string outname = "";
float c;
if (!outmsg)
return ("");
outname = sprintf("output_%i\n", outcount);
outcount++;
/* to make sure tokenize 'remembers' to tell us about the commonly
empty data string, we prepared the output string beforehand to
at least contain a _ symbol after the comma separator... now
we gotta clip that away using substring(). messy, but that's the
only way to keep them at 5 argv() calls per output */
c = tokenizebyseparator(outmsg, ",");
for (float i = 1; i < c; i+=5) {
NSOutput new_minion = spawn(NSOutput);
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 */
NSLog("^2%s::CreateOutput report:", classname);
NSLog("Target: %s", new_minion.m_strTarget);
NSLog("Input: %s", new_minion.m_strInput);
NSLog("Data Message: %s", new_minion.m_strData);
NSLog("Delay: %f", new_minion.m_flDelay);
NSLog("Uses: %i\n", new_minion.m_iCount);
}
/* return the name that'll act as the trigger for all outputs */
return outname;
}
string
NSIO::PrepareOutput(string strOut, string strValue)
{
strValue = strreplace(",", ",_", strValue);
strOut = strcat(strOut, ",_", strValue);
return strOut;
}
/* entities receive the inputs here and need to act on intype and data
accordingly. this is just a stub for unknown event troubleshooting */
void
NSIO::Input(entity eAct, string strInput, string strData)
{
switch (strInput) {
case "FireUser1":
UseOutput(eAct, m_strOnUser1);
break;
case "FireUser2":
UseOutput(eAct, m_strOnUser2);
break;
case "FireUser3":
UseOutput(eAct, m_strOnUser3);
break;
case "FireUser4":
UseOutput(eAct, m_strOnUser4);
break;
default:
if (strData != "")
print(sprintf("^2%s::^3Input^7: Receives input %s from %s with data %s\n",
this.classname, strInput, eAct.classname, strData));
else
print(sprintf("^2%s::^3Input^7: Receives input %s from %s\n",
this.classname, strInput, eAct.classname));
}
}
void
NSIO::Respawn(void)
{
// Respawn code goes here...
}
void
NSIO::SaveBool(float handle, string key, bool value)
{
if (value)
fputs(handle, sprintf("%S \"%f\"\n", key, value));
}
void
NSIO::SaveFloat(float handle, string key, float value)
{
if (value)
fputs(handle, sprintf("%S \"%f\"\n", key, value));
}
void
NSIO::SaveInt(float handle, string key, int value)
{
if (value)
fputs(handle, sprintf("%S \"%i\"\n", key, value));
}
void
NSIO::SaveString(float handle, string key, string value)
{
if (value && value != "")
fputs(handle, sprintf("%S %S\n", key, value));
}
void
NSIO::SaveVector(float handle, string key, vector value)
{
if (value)
fputs(handle, sprintf("%S \"%v\"\n", key, value));
}
float
NSIO::ReadBool(string strValue)
{
if (strValue && strValue != "")
return stof(strValue);
return __NULL__;
}
float
NSIO::ReadFloat(string strValue)
{
if (strValue && strValue != "")
return stof(strValue);
return __NULL__;
}
int
NSIO::ReadInt(string strValue)
{
if (strValue && strValue != "")
return stoi(strValue);
return __NULL__;
}
string
NSIO::ReadString(string strValue)
{
if (strValue && strValue != "")
return strValue;
return __NULL__;
}
vector
NSIO::ReadVector(string strValue)
{
if (strValue && strValue != "")
return stov(strValue);
return __NULL__;
}
void
NSIO::Save(float handle)
{
SaveFloat(handle, "nextthink", bound(0, (nextthink - time), nextthink));
SaveString(handle, "think", getentityfieldstring(findentityfield("think"), this));
SaveString(handle, "OnTrigger", m_strOnTrigger);
SaveString(handle, "OnUser1", m_strOnUser1);
SaveString(handle, "OnUser2", m_strOnUser2);
SaveString(handle, "OnUser3", m_strOnUser3);
SaveString(handle, "OnUser4", m_strOnUser4);
SaveString(handle, "targetname", targetname);
}
void
NSIO::Restore(string strKey, string strValue)
{
switch (strKey) {
case "targetname":
targetname = ReadString(strValue);
break;
case "nextthink":
float nt = ReadFloat(strValue);
if (nt)
nextthink = time + stof(strValue);
break;
case "think":
think = externvalue(-1, strValue);
break;
case "OnTrigger":
m_strOnTrigger = ReadString(strValue);
break;
case "OnUser1":
m_strOnUser1 = ReadString(strValue);
break;
case "OnUser2":
m_strOnUser2 = ReadString(strValue);
break;
case "OnUser3":
m_strOnUser3 = ReadString(strValue);
break;
case "OnUser4":
m_strOnUser4 = ReadString(strValue);
break;
}
}
#endif
/*
============
NSIO::SpawnKey
note that the engine still likes to try and map key/value
pairs on its own, but we can at least undo some of that in
here if needed
============
*/
void
NSIO::SpawnKey(string strKey, string strValue)
{
/* we do re-read a lot of the builtin fields in case we want to set
defaults. just in case anybody is wondering. */
switch (strKey) {
case "zhlt_lightflags":
case "classname":
case "spawnflags":
break;
case "targetname":
targetname = strValue;
break;
#ifdef SERVER
/* Input/Output system */
case "OnTrigger":
m_strOnTrigger = PrepareOutput(m_strOnTrigger, strValue);
break;
case "OnUser1":
m_strOnUser1 = PrepareOutput(m_strOnUser1, strValue);
break;
case "OnUser2":
m_strOnUser2 = PrepareOutput(m_strOnUser2, strValue);
break;
case "OnUser3":
m_strOnUser3 = PrepareOutput(m_strOnUser3, strValue);
break;
case "OnUser4":
m_strOnUser4 = PrepareOutput(m_strOnUser4, strValue);
break;
#endif
default:
NSLog("^3%s^7::SpawnKey:: Unknown key '%s' with value '%s'",
classname, strKey, strValue);
break;
}
}
void
NSIO::NSIO(void)
{
#ifdef SERVER
/* Not in Deathmatch */
if (spawnflags & 2048) {
if (cvar("sv_playerslots") > 1) {
remove(this);
return;
}
}
Init();
/* assign our onuser crap */
m_strOnUser1 = CreateOutput(m_strOnUser1);
m_strOnUser2 = CreateOutput(m_strOnUser2);
m_strOnUser3 = CreateOutput(m_strOnUser3);
m_strOnUser4 = CreateOutput(m_strOnUser4);
#endif
}