From 0b84fccdd26358b5d5b047e7da1899b402b0ec5e Mon Sep 17 00:00:00 2001 From: Marc Chapman Date: Wed, 26 Jul 2017 20:18:27 +0100 Subject: [PATCH] substitutions -- Implementation of special substitution statements on datablock fields. --- Engine/source/T3D/debris.cpp | 7 + Engine/source/T3D/fx/explosion.cpp | 10 + Engine/source/T3D/fx/particleEmitter.cpp | 7 + Engine/source/T3D/projectile.cpp | 11 + Engine/source/T3D/shapeBase.cpp | 8 + Engine/source/console/compiledEval.cpp | 13 + Engine/source/console/consoleObject.cpp | 37 +++ Engine/source/console/consoleObject.h | 10 + Engine/source/console/simDatablock.cpp | 291 ++++++++++++++++++++++- Engine/source/console/simDatablock.h | 30 +++ Engine/source/console/simObject.cpp | 37 +++ Engine/source/sfx/sfxProfile.cpp | 7 + 12 files changed, 464 insertions(+), 4 deletions(-) diff --git a/Engine/source/T3D/debris.cpp b/Engine/source/T3D/debris.cpp index 61ec9c3b6..3d7554f26 100644 --- a/Engine/source/T3D/debris.cpp +++ b/Engine/source/T3D/debris.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "T3D/debris.h" @@ -269,6 +273,9 @@ void DebrisData::initPersistFields() addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData), "If true, this debris object will not collide with water, acting as if the water is not there."); endGroup("Behavior"); + // disallow some field substitutions + onlyKeepClearSubstitutions("emitters"); // subs resolving to "~~", or "~0" are OK + onlyKeepClearSubstitutions("explosion"); Parent::initPersistFields(); } diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index e4daaf077..7eca7f532 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "T3D/fx/explosion.h" @@ -412,6 +416,12 @@ void ExplosionData::initPersistFields() "Distance (in the explosion normal direction) of the PointLight position " "from the explosion center." ); + // disallow some field substitutions + onlyKeepClearSubstitutions("debris"); // subs resolving to "~~", or "~0" are OK + onlyKeepClearSubstitutions("emitter"); + onlyKeepClearSubstitutions("particleEmitter"); + onlyKeepClearSubstitutions("soundProfile"); + onlyKeepClearSubstitutions("subExplosion"); Parent::initPersistFields(); } diff --git a/Engine/source/T3D/fx/particleEmitter.cpp b/Engine/source/T3D/fx/particleEmitter.cpp index 63d590bad..0f164f42b 100644 --- a/Engine/source/T3D/fx/particleEmitter.cpp +++ b/Engine/source/T3D/fx/particleEmitter.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "T3D/fx/particleEmitter.h" @@ -293,6 +297,9 @@ void ParticleEmitterData::initPersistFields() endGroup( "ParticleEmitterData" ); + // disallow some field substitutions + disableFieldSubstitutions("particles"); + onlyKeepClearSubstitutions("poolData"); // subs resolving to "~~", or "~0" are OK Parent::initPersistFields(); } diff --git a/Engine/source/T3D/projectile.cpp b/Engine/source/T3D/projectile.cpp index 61821cbde..f5003c1e5 100644 --- a/Engine/source/T3D/projectile.cpp +++ b/Engine/source/T3D/projectile.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "T3D/projectile.h" @@ -274,6 +278,13 @@ void ProjectileData::initPersistFields() "A value of 1.0 will assume \"normal\" influence upon it.\n" "The magnitude of gravity is assumed to be 9.81 m/s/s\n\n" "@note ProjectileData::isBallistic must be true for this to have any affect."); + // disallow some field substitutions + onlyKeepClearSubstitutions("explosion"); + onlyKeepClearSubstitutions("particleEmitter"); + onlyKeepClearSubstitutions("particleWaterEmitter"); + onlyKeepClearSubstitutions("sound"); + onlyKeepClearSubstitutions("splash"); + onlyKeepClearSubstitutions("waterExplosion"); Parent::initPersistFields(); } diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index e612aa148..445ca4b90 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "T3D/shapeBase.h" @@ -586,6 +590,10 @@ void ShapeBaseData::initPersistFields() endGroup( "Reflection" ); + // disallow some field substitutions + onlyKeepClearSubstitutions("debris"); // subs resolving to "~~", or "~0" are OK + onlyKeepClearSubstitutions("explosion"); + onlyKeepClearSubstitutions("underwaterExplosion"); Parent::initPersistFields(); } diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index f189d6268..38f1ec589 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "console/console.h" @@ -858,6 +862,7 @@ breakContinue: Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-datablock class %s.", getFileLine(ip), (const char*)callArgv[1]); // Clean up... delete object; + currentNewObject = NULL; ip = failJump; break; } @@ -893,6 +898,14 @@ breakContinue: currentNewObject->setCopySource( parent ); currentNewObject->assignFieldsFrom( parent ); + // copy any substitution statements + SimDataBlock* parent_db = dynamic_cast(parent); + if (parent_db) + { + SimDataBlock* currentNewObject_db = dynamic_cast(currentNewObject); + if (currentNewObject_db) + currentNewObject_db->copySubstitutionsFrom(parent_db); + } } else { diff --git a/Engine/source/console/consoleObject.cpp b/Engine/source/console/consoleObject.cpp index 2d9ba2a1e..d75405376 100644 --- a/Engine/source/console/consoleObject.cpp +++ b/Engine/source/console/consoleObject.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "console/consoleObject.h" @@ -681,6 +685,39 @@ AbstractClassRep* ConsoleObject::getClassRep() const return NULL; } +bool ConsoleObject::disableFieldSubstitutions(const char* fieldname) +{ + StringTableEntry slotname = StringTable->insert(fieldname); + + for (U32 i = 0; i < sg_tempFieldList.size(); i++) + { + if (sg_tempFieldList[i].pFieldname == slotname) + { + sg_tempFieldList[i].doNotSubstitute = true; + sg_tempFieldList[i].keepClearSubsOnly = false; + return true; + } + } + + return false; +} + +bool ConsoleObject::onlyKeepClearSubstitutions(const char* fieldname) +{ + StringTableEntry slotname = StringTable->insert(fieldname); + + for (U32 i = 0; i < sg_tempFieldList.size(); i++) + { + if (sg_tempFieldList[i].pFieldname == slotname) + { + sg_tempFieldList[i].doNotSubstitute = false; + sg_tempFieldList[i].keepClearSubsOnly = true; + return true; + } + } + + return false; +} String ConsoleObject::_getLogMessage(const char* fmt, va_list args) const { String objClass = "UnknownClass"; diff --git a/Engine/source/console/consoleObject.h b/Engine/source/console/consoleObject.h index adef30e77..36ec5d718 100644 --- a/Engine/source/console/consoleObject.h +++ b/Engine/source/console/consoleObject.h @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #ifndef _CONSOLEOBJECT_H_ #define _CONSOLEOBJECT_H_ @@ -492,6 +496,7 @@ public: setDataFn( NULL ), getDataFn( NULL ) { + doNotSubstitute = keepClearSubsOnly = false; } StringTableEntry pFieldname; ///< Name of the field. @@ -509,6 +514,8 @@ public: TypeValidator *validator; ///< Validator, if any. SetDataNotify setDataFn; ///< Set data notify Fn GetDataNotify getDataFn; ///< Get data notify Fn + bool doNotSubstitute; + bool keepClearSubsOnly; WriteDataNotify writeDataFn; ///< Function to determine whether data should be written or not. }; typedef Vector FieldList; @@ -1054,6 +1061,9 @@ public: static ConsoleObject* __findObject( const char* ) { return NULL; } static const char* __getObjectId( ConsoleObject* ) { return ""; } +protected: + static bool disableFieldSubstitutions(const char* in_pFieldname); + static bool onlyKeepClearSubstitutions(const char* in_pFieldname); }; #define addNamedField(fieldName,type,className) addField(#fieldName, type, Offset(fieldName,className)) diff --git a/Engine/source/console/simDatablock.cpp b/Engine/source/console/simDatablock.cpp index a10ba1761..ce689c054 100644 --- a/Engine/source/console/simDatablock.cpp +++ b/Engine/source/console/simDatablock.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "console/simDatablock.h" @@ -29,6 +33,8 @@ #include "T3D/gameBase/gameConnectionEvents.h" #include "T3D/gameBase/gameConnection.h" +#include "core/stream/bitStream.h" +#include "console/compiler.h" IMPLEMENT_CO_DATABLOCK_V1(SimDataBlock); SimObjectId SimDataBlock::sNextObjectId = DataBlockObjectIdFirst; @@ -52,6 +58,260 @@ SimDataBlock::SimDataBlock() setModDynamicFields(true); setModStaticFields(true); } +// this implements a simple structure for managing substitution statements. + +SimDataBlock::SubstitutionStatement::SubstitutionStatement(StringTableEntry slot, S32 idx, const char* value) +{ + this->slot = slot; + this->idx = idx; + this->value = dStrdup(value); +} + +SimDataBlock::SubstitutionStatement::~SubstitutionStatement() +{ + dFree(value); +} + +void SimDataBlock::SubstitutionStatement::replaceValue(const char* value) +{ + dFree(this->value); + this->value = dStrdup(value); +} + +// this is the copy-constructor for creating temp-clones. +SimDataBlock::SimDataBlock(const SimDataBlock& other, bool temp_clone) : SimObject(other, temp_clone) +{ + modifiedKey = other.modifiedKey; +} + +// a destructor is added to SimDataBlock so that we can delete any substitutions. +SimDataBlock::~SimDataBlock() +{ + clear_substitutions(); +} + +void SimDataBlock::clear_substitutions() +{ + for (S32 i = 0; i < substitutions.size(); i++) + delete substitutions[i]; + substitutions.clear(); +} + +void SimDataBlock::addSubstitution(StringTableEntry slot, S32 idx, const char* subst) +{ + AssertFatal(subst != 0 && subst[0] == '$' && subst[1] == '$', "Bad substition statement string added"); + + subst += 2; + while (dIsspace(*subst)) + subst++; + + bool empty_subs = (*subst == '\0'); + + for (S32 i = 0; i < substitutions.size(); i++) + { + if (substitutions[i] && substitutions[i]->slot == slot && substitutions[i]->idx == idx) + { + if (empty_subs) + { + delete substitutions[i]; + substitutions[i] = 0; + onRemoveSubstitution(slot, idx); + } + else + { + substitutions[i]->replaceValue(subst); + onAddSubstitution(slot, idx, subst); + } + return; + } + } + + if (!empty_subs) + { + substitutions.push_back(new SubstitutionStatement(slot, idx, subst)); + onAddSubstitution(slot, idx, subst); + } +} + +const char* SimDataBlock::getSubstitution(StringTableEntry slot, S32 idx) +{ + for (S32 i = 0; i < substitutions.size(); i++) + { + if (substitutions[i] && substitutions[i]->slot == slot && substitutions[i]->idx == idx) + return substitutions[i]->value; + } + + return 0; +} + +bool SimDataBlock::fieldHasSubstitution(StringTableEntry slot) +{ + for (S32 i = 0; i < substitutions.size(); i++) + if (substitutions[i] && substitutions[i]->slot == slot) + return true; + return false; +} + +void SimDataBlock::printSubstitutions() +{ + for (S32 i = 0; i < substitutions.size(); i++) + if (substitutions[i]) + Con::errorf("SubstitutionStatement[%s] = \"%s\" -- %d", substitutions[i]->slot, substitutions[i]->value, i); +} + +void SimDataBlock::copySubstitutionsFrom(SimDataBlock* other) +{ + clear_substitutions(); + if (!other) + return; + + for (S32 i = 0; i < other->substitutions.size(); i++) + { + if (other->substitutions[i]) + { + SubstitutionStatement* subs = other->substitutions[i]; + substitutions.push_back(new SubstitutionStatement(subs->slot, subs->idx, subs->value)); + } + } +} + + +// This is the method that evaluates any substitution statements on a datablock and does the +// actual replacement of substituted datablock fields. +// +// Much of the work is done by passing the statement to Con::evaluate() but first there are +// some key operations performed on the statement. +// -- Instances of "%%" in the statement are replaced with the id of the object. +// -- Instances of "##" are replaced with the value of . +// +// There are also some return values that get special treatment. +// -- An empty result will produce a realtime error message. +// -- A result of "~~" will leave the original field value unchanged. +// -- A result of "~0" will clear the original field to "" without producing an error message. +// +void SimDataBlock::performSubstitutions(SimDataBlock* dblock, const SimObject* obj, S32 index) +{ + if (!dblock || !dblock->getClassRep()) + { + // error message + return; + } + + char obj_str[32]; + dStrcpy(obj_str, Con::getIntArg(obj->getId())); + + char index_str[32]; + dStrcpy(index_str, Con::getIntArg(index)); + + for (S32 i = 0; i < substitutions.size(); i++) + { + if (substitutions[i]) + { + static char buffer[1024]; + static char* b_oob = &buffer[1024]; + char* b = buffer; + + // perform special token expansion (%% and ##) + const char* v = substitutions[i]->value; + while (*v != '\0') + { + // identify "%%" tokens and replace with id + if (v[0] == '%' && v[1] == '%') + { + const char* s = obj_str; + while (*s != '\0') + { + b[0] = s[0]; + b++; + s++; + } + v += 2; + } + // identify "##" tokens and replace with value + else if (v[0] == '#' && v[1] == '#') + { + const char* s = index_str; + while (*s != '\0') + { + b[0] = s[0]; + b++; + s++; + } + v += 2; + } + else + { + b[0] = v[0]; + b++; + v++; + } + } + + AssertFatal((uintptr_t)b < (uintptr_t)b_oob, "Substitution buffer overflowed"); + + b[0] = '\0'; + + // perform the statement evaluation + Compiler::gSyntaxError = false; + //Con::errorf("EVAL [%s]", avar("return %s;", buffer)); + const char *result = Con::evaluate(avar("return %s;", buffer), false, 0); + if (Compiler::gSyntaxError) + { + Con::errorf("Field Substitution Failed: field=\"%s\" substitution=\"%s\" -- syntax error", + substitutions[i]->slot, substitutions[i]->value); + Compiler::gSyntaxError = false; + return; + } + + // output a runtime console error when a substitution produces and empty result. + if (result == 0 || result[0] == '\0') + { + Con::errorf("Field Substitution Failed: field=\"%s\" substitution=\"%s\" -- empty result", + substitutions[i]->slot, substitutions[i]->value); + return; + } + + // handle special return values + if (result[0] == '~') + { + // if value is "~~" then keep the existing value + if (result[1] == '~' && result[2] == '\0') + continue; + // if "~0" then clear it + if (result[1] == '0' && result[2] == '\0') + result = ""; + } + + const AbstractClassRep::Field* field = dblock->getClassRep()->findField(substitutions[i]->slot); + if (!field) + { + // this should be very unlikely... + Con::errorf("Field Substitution Failed: unknown field, \"%s\".", substitutions[i]->slot); + continue; + } + + if (field->keepClearSubsOnly && result[0] != '\0') + { + Con::errorf("Field Substitution Failed: field \"%s\" of datablock %s only allows \"$$ ~~\" (keep) and \"$$ ~0\" (clear) field substitutions. [%s]", + substitutions[i]->slot, this->getClassName(), this->getName()); + continue; + } + + // substitute the field value with its replacement + Con::setData(field->type, (void*)(((const char*)(dblock)) + field->offset), substitutions[i]->idx, 1, &result, field->table, field->flag); + + //dStrncpy(buffer, result, 255); + //Con::errorf("SUBSTITUTION %s.%s[%d] = %s idx=%s", Con::getIntArg(getId()), substitutions[i]->slot, substitutions[i]->idx, buffer, index_str); + + // notify subclasses of a field modification + dblock->onStaticModified(substitutions[i]->slot); + } + } + + // notify subclasses of substitution operation + if (substitutions.size() > 0) + dblock->onPerformSubstitutions(); +} //----------------------------------------------------------------------------- @@ -96,14 +356,37 @@ void SimDataBlock::onStaticModified(const char* slotName, const char* newValue) //----------------------------------------------------------------------------- -void SimDataBlock::packData(BitStream*) +// packData() and unpackData() do nothing in the stock implementation, but here +// they've been modified to pack and unpack any substitution statements. +// +void SimDataBlock::packData(BitStream* stream) { + for (S32 i = 0; i < substitutions.size(); i++) + { + if (substitutions[i]) + { + stream->writeFlag(true); + stream->writeString(substitutions[i]->slot); + stream->write(substitutions[i]->idx); + stream->writeString(substitutions[i]->value); + } + } + stream->writeFlag(false); } -//----------------------------------------------------------------------------- - -void SimDataBlock::unpackData(BitStream*) +void SimDataBlock::unpackData(BitStream* stream) { + clear_substitutions(); + while(stream->readFlag()) + { + char slotName[256]; + S32 idx; + char value[256]; + stream->readString(slotName); + stream->read(&idx); + stream->readString(value); + substitutions.push_back(new SubstitutionStatement(StringTable->insert(slotName), idx, value)); + } } //----------------------------------------------------------------------------- diff --git a/Engine/source/console/simDatablock.h b/Engine/source/console/simDatablock.h index 3d7acc777..d6a7dcd52 100644 --- a/Engine/source/console/simDatablock.h +++ b/Engine/source/console/simDatablock.h @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #ifndef _SIMDATABLOCK_H_ #define _SIMDATABLOCK_H_ @@ -172,6 +176,32 @@ public: /// Used by the console system to automatically tell datablock classes apart /// from non-datablock classes. static const bool __smIsDatablock = true; +protected: + struct SubstitutionStatement + { + StringTableEntry slot; + S32 idx; + char* value; + SubstitutionStatement(StringTableEntry slot, S32 idx, const char* value); + ~SubstitutionStatement(); + void replaceValue(const char* value); + }; + Vector substitutions; + void clear_substitutions(); +public: + /*C*/ SimDataBlock(const SimDataBlock&, bool = false); + /*D*/ ~SimDataBlock(); + + void addSubstitution(StringTableEntry field, S32 idx, const char* subst); + const char* getSubstitution(StringTableEntry field, S32 idx); + S32 getSubstitutionCount() { return substitutions.size(); } + void performSubstitutions(SimDataBlock*, const SimObject*, S32 index=0); + void copySubstitutionsFrom(SimDataBlock* other); + void printSubstitutions(); + bool fieldHasSubstitution(StringTableEntry slot); + virtual void onAddSubstitution(StringTableEntry, S32 idx, const char* subst) { } + virtual void onRemoveSubstitution(StringTableEntry, S32 idx) { } + virtual void onPerformSubstitutions() { } }; //--------------------------------------------------------------------------- diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index 73a1ffa3d..22e496bbf 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "platform/platformMemory.h" #include "console/simObject.h" @@ -915,6 +919,29 @@ void SimObject::setDataField(StringTableEntry slotName, const char *array, const S32 array1 = array ? dAtoi(array) : 0; + // Here we check to see if is a datablock and if + // starts with "$$". If both true than save value as a runtime substitution. + if (dynamic_cast(this) && value[0] == '$' && value[1] == '$') + { + if (!this->allowSubstitutions()) + { + Con::errorf("Substitution Error: %s datablocks do not allow \"$$\" field substitutions. [%s]", + this->getClassName(), this->getName()); + return; + } + + if (fld->doNotSubstitute) + { + Con::errorf("Substitution Error: field \"%s\" of datablock %s prohibits \"$$\" field substitutions. [%s]", + slotName, this->getClassName(), this->getName()); + return; + } + + // add the substitution + ((SimDataBlock*)this)->addSubstitution(slotName, array1, value); + return; + } + if(array1 >= 0 && array1 < fld->elementCount && fld->elementCount >= 1) { // If the set data notify callback returns true, then go ahead and @@ -2612,6 +2639,16 @@ DefineEngineMethod( SimObject, dump, void, ( bool detailed ), ( false ), } } + // If the object is a datablock with substitution statements, + // they get printed out as part of the dump. + if (dynamic_cast(object)) + { + if (((SimDataBlock*)object)->getSubstitutionCount() > 0) + { + Con::printf("Substitution Fields:"); + ((SimDataBlock*)object)->printSubstitutions(); + } + } Con::printf( "Dynamic Fields:" ); if(object->getFieldDictionary()) object->getFieldDictionary()->printFields(object); diff --git a/Engine/source/sfx/sfxProfile.cpp b/Engine/source/sfx/sfxProfile.cpp index bbb40c249..2fff6854d 100644 --- a/Engine/source/sfx/sfxProfile.cpp +++ b/Engine/source/sfx/sfxProfile.cpp @@ -20,6 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// +// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames +// Copyright (C) 2015 Faust Logic, Inc. +//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// #include "platform/platform.h" #include "sfx/sfxProfile.h" @@ -114,6 +118,9 @@ void SFXProfile::initPersistFields() endGroup( "Sound" ); + // disallow some field substitutions + disableFieldSubstitutions("description"); + Parent::initPersistFields(); }