mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-29 08:15:44 +00:00
Other renames to ensure linux case-sensitivity compliance and casing format consistency.
This commit is contained in:
parent
93e767f0c5
commit
f5e86a83b5
33 changed files with 0 additions and 0 deletions
434
Engine/source/T3D/components/game/stateMachine.cpp
Normal file
434
Engine/source/T3D/components/game/stateMachine.cpp
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/components/game/stateMachine.h"
|
||||
|
||||
StateMachine::StateMachine()
|
||||
{
|
||||
mStateStartTime = -1;
|
||||
mStateTime = 0;
|
||||
|
||||
mStartingState = "";
|
||||
|
||||
mCurCreateState = NULL;
|
||||
}
|
||||
|
||||
StateMachine::~StateMachine()
|
||||
{
|
||||
}
|
||||
|
||||
void StateMachine::loadStateMachineFile()
|
||||
{
|
||||
if (!mXMLReader)
|
||||
{
|
||||
SimXMLDocument *xmlrdr = new SimXMLDocument();
|
||||
xmlrdr->registerObject();
|
||||
|
||||
mXMLReader = xmlrdr;
|
||||
}
|
||||
|
||||
bool hasStartState = false;
|
||||
|
||||
if (!dStrIsEmpty(mStateMachineFile))
|
||||
{
|
||||
//use our xml reader to parse the file!
|
||||
SimXMLDocument *reader = mXMLReader.getObject();
|
||||
if (!reader->loadFile(mStateMachineFile))
|
||||
Con::errorf("Could not load state machine file: &s", mStateMachineFile);
|
||||
|
||||
if (!reader->pushFirstChildElement("StateMachine"))
|
||||
return;
|
||||
|
||||
//find our starting state
|
||||
if (reader->pushFirstChildElement("StartingState"))
|
||||
{
|
||||
mStartingState = reader->getData();
|
||||
reader->popElement();
|
||||
hasStartState = true;
|
||||
}
|
||||
|
||||
readStates();
|
||||
}
|
||||
|
||||
if (hasStartState)
|
||||
mCurrentState = getStateByName(mStartingState);
|
||||
|
||||
mStateStartTime = -1;
|
||||
mStateTime = 0;
|
||||
}
|
||||
|
||||
void StateMachine::readStates()
|
||||
{
|
||||
SimXMLDocument *reader = mXMLReader.getObject();
|
||||
|
||||
//iterate through our states now!
|
||||
if (reader->pushFirstChildElement("State"))
|
||||
{
|
||||
//get our first state
|
||||
State firstState;
|
||||
|
||||
readStateName(&firstState, reader);
|
||||
readStateScriptFunction(&firstState, reader);
|
||||
|
||||
readTransitions(firstState);
|
||||
|
||||
mStates.push_back(firstState);
|
||||
|
||||
//now, iterate the siblings
|
||||
while (reader->nextSiblingElement("State"))
|
||||
{
|
||||
State newState;
|
||||
readStateName(&newState, reader);
|
||||
readStateScriptFunction(&newState, reader);
|
||||
|
||||
readTransitions(newState);
|
||||
|
||||
mStates.push_back(newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachine::readTransitions(State ¤tState)
|
||||
{
|
||||
SimXMLDocument *reader = mXMLReader.getObject();
|
||||
|
||||
//iterate through our states now!
|
||||
if (reader->pushFirstChildElement("Transition"))
|
||||
{
|
||||
//get our first state
|
||||
StateTransition firstTransition;
|
||||
|
||||
readTransitonTarget(&firstTransition, reader);
|
||||
|
||||
readConditions(firstTransition);
|
||||
|
||||
currentState.mTransitions.push_back(firstTransition);
|
||||
|
||||
//now, iterate the siblings
|
||||
while (reader->nextSiblingElement("Transition"))
|
||||
{
|
||||
StateTransition newTransition;
|
||||
readTransitonTarget(&newTransition, reader);
|
||||
|
||||
readConditions(newTransition);
|
||||
|
||||
currentState.mTransitions.push_back(newTransition);
|
||||
}
|
||||
|
||||
reader->popElement();
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachine::readConditions(StateTransition ¤tTransition)
|
||||
{
|
||||
SimXMLDocument *reader = mXMLReader.getObject();
|
||||
|
||||
//iterate through our states now!
|
||||
if (reader->pushFirstChildElement("Rule"))
|
||||
{
|
||||
//get our first state
|
||||
StateTransition::Condition firstCondition;
|
||||
StateField firstField;
|
||||
bool fieldRead = false;
|
||||
|
||||
readFieldName(&firstField, reader);
|
||||
firstCondition.field = firstField;
|
||||
|
||||
readFieldComparitor(&firstCondition, reader);
|
||||
|
||||
readFieldValue(&firstCondition.field, reader);
|
||||
|
||||
currentTransition.mTransitionRules.push_back(firstCondition);
|
||||
|
||||
//now, iterate the siblings
|
||||
while (reader->nextSiblingElement("Transition"))
|
||||
{
|
||||
StateTransition::Condition newCondition;
|
||||
StateField newField;
|
||||
|
||||
readFieldName(&newField, reader);
|
||||
newCondition.field = newField;
|
||||
|
||||
readFieldComparitor(&newCondition, reader);
|
||||
|
||||
readFieldValue(&newCondition.field, reader);
|
||||
|
||||
currentTransition.mTransitionRules.push_back(newCondition);
|
||||
}
|
||||
|
||||
reader->popElement();
|
||||
}
|
||||
}
|
||||
|
||||
S32 StateMachine::parseComparitor(const char* comparitorName)
|
||||
{
|
||||
S32 targetType = -1;
|
||||
|
||||
if (!dStrcmp("GreaterThan", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::GeaterThan;
|
||||
else if (!dStrcmp("GreaterOrEqual", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::GreaterOrEqual;
|
||||
else if (!dStrcmp("LessThan", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::LessThan;
|
||||
else if (!dStrcmp("LessOrEqual", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::LessOrEqual;
|
||||
else if (!dStrcmp("Equals", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::Equals;
|
||||
else if (!dStrcmp("True", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::True;
|
||||
else if (!dStrcmp("False", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::False;
|
||||
else if (!dStrcmp("Negative", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::Negative;
|
||||
else if (!dStrcmp("Positive", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::Positive;
|
||||
else if (!dStrcmp("DoesNotEqual", comparitorName))
|
||||
targetType = StateMachine::StateTransition::Condition::DoesNotEqual;
|
||||
|
||||
return targetType;
|
||||
}
|
||||
|
||||
void StateMachine::update()
|
||||
{
|
||||
//we always check if there's a timout transition, as that's the most generic transition possible.
|
||||
F32 curTime = Sim::getCurrentTime();
|
||||
|
||||
if (mStateStartTime == -1)
|
||||
mStateStartTime = curTime;
|
||||
|
||||
mStateTime = curTime - mStateStartTime;
|
||||
|
||||
char buffer[64];
|
||||
dSprintf(buffer, sizeof(buffer), "%g", mStateTime);
|
||||
|
||||
checkTransitions("stateTime", buffer);
|
||||
}
|
||||
|
||||
void StateMachine::checkTransitions(const char* slotName, const char* newValue)
|
||||
{
|
||||
//because we use our current state's fields as dynamic fields on the instance
|
||||
//we'll want to catch any fields being set so we can treat changes as transition triggers if
|
||||
//any of the transitions on this state call for it
|
||||
|
||||
//One example would be in order to implement burst fire on a weapon state machine.
|
||||
//The behavior instance has a dynamic variable set up like: GunStateMachine.burstShotCount = 0;
|
||||
|
||||
//We also have a transition in our fire state, as: GunStateMachine.addTransition("FireState", "burstShotCount", "DoneShooting", 3);
|
||||
//What that does is for our fire state, we check the dynamicField burstShotCount if it's equal or greater than 3. If it is, we perform the transition.
|
||||
|
||||
//As state fields are handled as dynamicFields for the instance, regular dynamicFields are processed as well as state fields. So we can use the regular
|
||||
//dynamic fields for our transitions, to act as 'global' variables that are state-agnostic. Alternately, we can use state-specific fields, such as a transition
|
||||
//like this:
|
||||
//GunStateMachine.addTransition("IdleState", "Fidget", "Timeout", ">=", 5000);
|
||||
|
||||
//That uses the the timeout field, which is reset each time the state changes, and so state-specific, to see if it's been 5 seconds. If it has been, we transition
|
||||
//to our fidget state
|
||||
|
||||
//so, lets check our current transitions
|
||||
//now that we have the type, check our transitions!
|
||||
for (U32 t = 0; t < mCurrentState.mTransitions.size(); t++)
|
||||
{
|
||||
//if (!dStrcmp(mCurrentState.mTransitions[t]., slotName))
|
||||
{
|
||||
//found a transition looking for this variable, so do work
|
||||
//first, figure out what data type thie field is
|
||||
//S32 type = getVariableType(newValue);
|
||||
|
||||
bool fail = false;
|
||||
bool match = false;
|
||||
S32 ruleCount = mCurrentState.mTransitions[t].mTransitionRules.size();
|
||||
|
||||
for (U32 r = 0; r < ruleCount; r++)
|
||||
{
|
||||
const char* fieldName = mCurrentState.mTransitions[t].mTransitionRules[r].field.name;
|
||||
if (!dStrcmp(fieldName, slotName))
|
||||
{
|
||||
match = true;
|
||||
//now, check the value with the comparitor and see if we do the transition.
|
||||
if (!passComparitorCheck(newValue, mCurrentState.mTransitions[t].mTransitionRules[r]))
|
||||
{
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If we do have a transition rule for this field, and we didn't fail on the condition, go ahead and switch states
|
||||
if (match && !fail)
|
||||
{
|
||||
setState(mCurrentState.mTransitions[t].mStateTarget);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StateMachine::passComparitorCheck(const char* var, StateTransition::Condition transitionRule)
|
||||
{
|
||||
F32 num = dAtof(var);
|
||||
switch (transitionRule.field.fieldType)
|
||||
{
|
||||
case StateField::Type::VectorType:
|
||||
switch (transitionRule.triggerComparitor)
|
||||
{
|
||||
case StateTransition::Condition::Equals:
|
||||
case StateTransition::Condition::GeaterThan:
|
||||
case StateTransition::Condition::GreaterOrEqual:
|
||||
case StateTransition::Condition::LessThan:
|
||||
case StateTransition::Condition::LessOrEqual:
|
||||
case StateTransition::Condition::DoesNotEqual:
|
||||
//do
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
case StateField::Type::StringType:
|
||||
switch (transitionRule.triggerComparitor)
|
||||
{
|
||||
case StateTransition::Condition::Equals:
|
||||
if (!dStrcmp(var, transitionRule.field.triggerStringVal))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::DoesNotEqual:
|
||||
if (dStrcmp(var, transitionRule.field.triggerStringVal))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
case StateField::Type::BooleanType:
|
||||
switch (transitionRule.triggerComparitor)
|
||||
{
|
||||
case StateTransition::Condition::TriggerValueTarget::True:
|
||||
if (dAtob(var))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::TriggerValueTarget::False:
|
||||
if (dAtob(var))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
case StateField::Type::NumberType:
|
||||
switch (transitionRule.triggerComparitor)
|
||||
{
|
||||
case StateTransition::Condition::TriggerValueTarget::Equals:
|
||||
if (num == transitionRule.field.triggerNumVal)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::TriggerValueTarget::GeaterThan:
|
||||
if (num > transitionRule.field.triggerNumVal)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::TriggerValueTarget::GreaterOrEqual:
|
||||
if (num >= transitionRule.field.triggerNumVal)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::TriggerValueTarget::LessThan:
|
||||
if (num < transitionRule.field.triggerNumVal)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::TriggerValueTarget::LessOrEqual:
|
||||
if (num <= transitionRule.field.triggerNumVal)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::TriggerValueTarget::DoesNotEqual:
|
||||
if (num != transitionRule.field.triggerNumVal)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::TriggerValueTarget::Positive:
|
||||
if (num > 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
case StateTransition::Condition::TriggerValueTarget::Negative:
|
||||
if (num < 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
void StateMachine::setState(const char* stateName, bool clearFields)
|
||||
{
|
||||
State oldState = mCurrentState;
|
||||
StringTableEntry sName = StringTable->insert(stateName);
|
||||
for (U32 i = 0; i < mStates.size(); i++)
|
||||
{
|
||||
//if(!dStrcmp(mStates[i]->stateName, stateName))
|
||||
if (!dStrcmp(mStates[i].stateName,sName))
|
||||
{
|
||||
mCurrentState = mStates[i];
|
||||
mStateStartTime = Sim::getCurrentTime();
|
||||
|
||||
onStateChanged.trigger(this, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* StateMachine::getStateByIndex(S32 index)
|
||||
{
|
||||
if (index >= 0 && mStates.size() > index)
|
||||
return mStates[index].stateName;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
StateMachine::State& StateMachine::getStateByName(const char* name)
|
||||
{
|
||||
StringTableEntry stateName = StringTable->insert(name);
|
||||
|
||||
for (U32 i = 0; i < mStates.size(); i++)
|
||||
{
|
||||
if (!dStrcmp(stateName, mStates[i].stateName))
|
||||
return mStates[i];
|
||||
}
|
||||
}
|
||||
|
||||
S32 StateMachine::findFieldByName(const char* name)
|
||||
{
|
||||
for (U32 i = 0; i < mFields.size(); i++)
|
||||
{
|
||||
if (!dStrcmp(mFields[i].name, name))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
259
Engine/source/T3D/components/game/stateMachine.h
Normal file
259
Engine/source/T3D/components/game/stateMachine.h
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef STATE_MACHINE_H
|
||||
#define STATE_MACHINE_H
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
#ifndef _OBJECTTYPES_H_
|
||||
#include "T3D/objectTypes.h"
|
||||
#endif
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _XMLDOC_H_
|
||||
#include "console/SimXMLDocument.h"
|
||||
#endif
|
||||
|
||||
class StateMachine
|
||||
{
|
||||
public:
|
||||
struct StateField
|
||||
{
|
||||
StringTableEntry name;
|
||||
|
||||
bool triggerBoolVal;
|
||||
float triggerNumVal;
|
||||
Point3F triggerVectorVal;
|
||||
String triggerStringVal;
|
||||
|
||||
enum Type
|
||||
{
|
||||
BooleanType = 0,
|
||||
NumberType,
|
||||
VectorType,
|
||||
StringType
|
||||
}fieldType;
|
||||
};
|
||||
|
||||
struct UniqueReference
|
||||
{
|
||||
SimObject* referenceObj;
|
||||
const char* referenceVar;
|
||||
const char* uniqueName;
|
||||
};
|
||||
|
||||
struct StateTransition
|
||||
{
|
||||
struct Condition
|
||||
{
|
||||
enum TriggerValueTarget
|
||||
{
|
||||
Equals = 0,
|
||||
GeaterThan,
|
||||
LessThan,
|
||||
GreaterOrEqual,
|
||||
LessOrEqual,
|
||||
True,
|
||||
False,
|
||||
Positive,
|
||||
Negative,
|
||||
DoesNotEqual
|
||||
};
|
||||
|
||||
StateField field;
|
||||
|
||||
TriggerValueTarget triggerComparitor;
|
||||
|
||||
UniqueReference *valUniqueRef;
|
||||
};
|
||||
|
||||
StringTableEntry mName;
|
||||
StringTableEntry mStateTarget;
|
||||
Vector<Condition> mTransitionRules;
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
Vector<StateTransition> mTransitions;
|
||||
|
||||
StringTableEntry stateName;
|
||||
|
||||
StringTableEntry callbackName;
|
||||
};
|
||||
|
||||
StringTableEntry mStateMachineFile;
|
||||
|
||||
protected:
|
||||
Vector<State> mStates;
|
||||
|
||||
Vector<StateField> mFields;
|
||||
|
||||
Vector<UniqueReference> mUniqueReferences;
|
||||
|
||||
State mCurrentState;
|
||||
|
||||
F32 mStateStartTime;
|
||||
F32 mStateTime;
|
||||
|
||||
StringTableEntry mStartingState;
|
||||
|
||||
State *mCurCreateSuperState;
|
||||
State *mCurCreateState;
|
||||
|
||||
SimObjectPtr<SimXMLDocument> mXMLReader;
|
||||
|
||||
public:
|
||||
StateMachine();
|
||||
virtual ~StateMachine();
|
||||
|
||||
void update();
|
||||
|
||||
void loadStateMachineFile();
|
||||
void readStates();
|
||||
void readTransitions(State ¤tState);
|
||||
void readConditions(StateTransition &newTransition);
|
||||
|
||||
void setState(const char* stateName, bool clearFields = true);
|
||||
|
||||
const char* getCurrentStateName() { return mCurrentState.stateName; }
|
||||
State& getCurrentState() {
|
||||
return mCurrentState;
|
||||
}
|
||||
|
||||
S32 getStateCount() { return mStates.size(); }
|
||||
const char* getStateByIndex(S32 index);
|
||||
State& getStateByName(const char* name);
|
||||
|
||||
void checkTransitions(const char* slotName, const char* newValue);
|
||||
|
||||
bool passComparitorCheck(const char* var, StateTransition::Condition transitionRule);
|
||||
|
||||
S32 findFieldByName(const char* name);
|
||||
|
||||
S32 getFieldsCount() { return mFields.size(); }
|
||||
|
||||
StateField getField(U32 index)
|
||||
{
|
||||
if (index <= mFields.size())
|
||||
return mFields[index];
|
||||
}
|
||||
|
||||
Signal< void(StateMachine*, S32 stateIdx) > StateMachine::onStateChanged;
|
||||
|
||||
//
|
||||
inline bool readStateName(State* state, SimXMLDocument* reader)
|
||||
{
|
||||
if (reader->pushFirstChildElement("Name"))
|
||||
{
|
||||
state->stateName = reader->getData();
|
||||
reader->popElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
inline bool readStateScriptFunction(State* state, SimXMLDocument* reader)
|
||||
{
|
||||
if (reader->pushFirstChildElement("ScriptFunction"))
|
||||
{
|
||||
state->callbackName = reader->getData();
|
||||
reader->popElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
inline bool readTransitonTarget(StateTransition* transition, SimXMLDocument* reader)
|
||||
{
|
||||
if (reader->pushFirstChildElement("StateTarget"))
|
||||
{
|
||||
transition->mStateTarget = reader->getData();
|
||||
reader->popElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//
|
||||
inline bool readFieldName(StateField* newField, SimXMLDocument* reader)
|
||||
{
|
||||
if (reader->pushFirstChildElement("FieldName"))
|
||||
{
|
||||
newField->name = reader->getData();
|
||||
reader->popElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
inline bool readFieldComparitor(StateTransition::Condition* condition, SimXMLDocument* reader)
|
||||
{
|
||||
if (reader->pushFirstChildElement("Comparitor"))
|
||||
{
|
||||
S32 compIdx = parseComparitor(reader->getData());
|
||||
condition->triggerComparitor = static_cast<StateTransition::Condition::TriggerValueTarget>(compIdx);
|
||||
reader->popElement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
inline bool readFieldValue(StateField* field, SimXMLDocument* reader)
|
||||
{
|
||||
if (reader->pushFirstChildElement("NumValue"))
|
||||
{
|
||||
field->fieldType = StateField::NumberType;
|
||||
field->triggerNumVal = dAtof(reader->getData());
|
||||
reader->popElement();
|
||||
return true;
|
||||
}
|
||||
else if (reader->pushFirstChildElement("StringValue"))
|
||||
{
|
||||
field->fieldType = StateField::StringType;
|
||||
field->triggerStringVal = reader->getData();
|
||||
reader->popElement();
|
||||
return true;
|
||||
}
|
||||
else if (reader->pushFirstChildElement("BoolValue"))
|
||||
{
|
||||
field->fieldType = StateField::BooleanType;
|
||||
field->triggerBoolVal = dAtob(reader->getData());
|
||||
reader->popElement();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
S32 parseComparitor(const char* comparitorName);
|
||||
};
|
||||
|
||||
#endif
|
||||
215
Engine/source/T3D/components/game/stateMachineComponent.cpp
Normal file
215
Engine/source/T3D/components/game/stateMachineComponent.cpp
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "T3D/components/game/StateMachinecomponent.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "core/resourceManager.h"
|
||||
#include "core/stream/fileStream.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "ts/tsShapeInstance.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "gfx/gfxTransformSaver.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "lighting/lightQuery.h"
|
||||
|
||||
IMPLEMENT_CALLBACK( StateMachineComponent, onStateChange, void, (), (),
|
||||
"@brief Called when we collide with another object.\n\n"
|
||||
"@param obj The ShapeBase object\n"
|
||||
"@param collObj The object we collided with\n"
|
||||
"@param vec Collision impact vector\n"
|
||||
"@param len Length of the impact vector\n" );
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
StateMachineComponent::StateMachineComponent() : Component()
|
||||
{
|
||||
mFriendlyName = "State Machine";
|
||||
mComponentType = "Game";
|
||||
|
||||
mDescription = getDescriptionText("A generic state machine.");
|
||||
|
||||
mStateMachineFile = "";
|
||||
|
||||
//doesn't need to be networked
|
||||
mNetworked = false;
|
||||
mNetFlags.clear();
|
||||
}
|
||||
|
||||
StateMachineComponent::~StateMachineComponent()
|
||||
{
|
||||
for(S32 i = 0;i < mFields.size();++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(StateMachineComponent);
|
||||
|
||||
bool StateMachineComponent::onAdd()
|
||||
{
|
||||
if(! Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// Register for the resource change signal.
|
||||
ResourceManager::get().getChangedSignal().notify(this, &StateMachineComponent::_onResourceChanged);
|
||||
|
||||
mStateMachine.onStateChanged.notify(this, &StateMachineComponent::onStateChanged);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StateMachineComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
U32 StateMachineComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void StateMachineComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
}
|
||||
|
||||
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
|
||||
void StateMachineComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
}
|
||||
|
||||
void StateMachineComponent::onComponentRemove()
|
||||
{
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void StateMachineComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addProtectedField("stateMachineFile", TypeFilename, Offset(mStateMachineFile, StateMachineComponent),
|
||||
&_setSMFile, &defaultProtectedGetFn, "The sim time of when we started this state");
|
||||
}
|
||||
|
||||
bool StateMachineComponent::_setSMFile(void *object, const char *index, const char *data)
|
||||
{
|
||||
StateMachineComponent* smComp = static_cast<StateMachineComponent*>(object);
|
||||
if (smComp)
|
||||
{
|
||||
smComp->setStateMachineFile(data);
|
||||
smComp->loadStateMachineFile();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void StateMachineComponent::_onResourceChanged(const Torque::Path &path)
|
||||
{
|
||||
if (path != Torque::Path(mStateMachineFile))
|
||||
return;
|
||||
|
||||
loadStateMachineFile();
|
||||
}
|
||||
|
||||
void StateMachineComponent::loadStateMachineFile()
|
||||
{
|
||||
if (!dStrIsEmpty(mStateMachineFile))
|
||||
{
|
||||
mStateMachine.mStateMachineFile = mStateMachineFile;
|
||||
mStateMachine.loadStateMachineFile();
|
||||
|
||||
//now that it's loaded, we need to parse the SM's fields and set them as script vars on ourselves
|
||||
S32 smFieldCount = mStateMachine.getFieldsCount();
|
||||
|
||||
for (U32 i = 0; i < smFieldCount; i++)
|
||||
{
|
||||
StateMachine::StateField field = mStateMachine.getField(i);
|
||||
|
||||
char buffer[128];
|
||||
|
||||
if (field.fieldType == StateMachine::StateField::BooleanType)
|
||||
{
|
||||
dSprintf(buffer, sizeof(buffer), "%b", field.triggerBoolVal);
|
||||
setDataField(field.name, NULL, buffer);
|
||||
}
|
||||
else if (field.fieldType == StateMachine::StateField::NumberType)
|
||||
{
|
||||
dSprintf(buffer, sizeof(buffer), "%g", field.triggerNumVal);
|
||||
setDataField(field.name, NULL, buffer);
|
||||
}
|
||||
else if (field.fieldType == StateMachine::StateField::StringType)
|
||||
{
|
||||
setDataField(field.name, NULL, field.triggerStringVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StateMachineComponent::processTick()
|
||||
{
|
||||
if (!isServerObject() || !isActive())
|
||||
return;
|
||||
|
||||
mStateMachine.update();
|
||||
}
|
||||
|
||||
void StateMachineComponent::onDynamicModified( const char* slotName, const char* newValue )
|
||||
{
|
||||
Parent::onDynamicModified(slotName, newValue);
|
||||
|
||||
StringTableEntry fieldName = StringTable->insert(slotName);
|
||||
mStateMachine.checkTransitions(fieldName, newValue);
|
||||
}
|
||||
|
||||
void StateMachineComponent::onStaticModified( const char* slotName, const char* newValue )
|
||||
{
|
||||
Parent::onStaticModified(slotName, newValue);
|
||||
|
||||
StringTableEntry fieldName = StringTable->insert(slotName);
|
||||
mStateMachine.checkTransitions(fieldName, newValue);
|
||||
}
|
||||
|
||||
void StateMachineComponent::onStateChanged(StateMachine* sm, S32 stateIdx)
|
||||
{
|
||||
//do a script callback, if we have one
|
||||
//check if we have a function for that, and then also check if our owner does
|
||||
StringTableEntry callbackName = mStateMachine.getCurrentState().callbackName;
|
||||
|
||||
if (isMethod(callbackName))
|
||||
Con::executef(this, callbackName);
|
||||
|
||||
if (mOwner->isMethod(callbackName))
|
||||
Con::executef(mOwner, callbackName);
|
||||
}
|
||||
81
Engine/source/T3D/components/game/stateMachineComponent.h
Normal file
81
Engine/source/T3D/components/game/stateMachineComponent.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (c) 2012 GarageGames, LLC
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef STATE_MACHINE_COMPONENT_H
|
||||
#define STATE_MACHINE_COMPONENT_H
|
||||
|
||||
#ifndef COMPONENT_H
|
||||
#include "T3D/components/component.h"
|
||||
#endif
|
||||
#ifndef STATE_MACHINE_H
|
||||
#include "T3D/components/game/stateMachine.h"
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class StateMachineComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
public:
|
||||
StateMachine mStateMachine;
|
||||
|
||||
protected:
|
||||
StringTableEntry mStateMachineFile;
|
||||
|
||||
public:
|
||||
StateMachineComponent();
|
||||
virtual ~StateMachineComponent();
|
||||
DECLARE_CONOBJECT(StateMachineComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void onComponentRemove();
|
||||
|
||||
void _onResourceChanged(const Torque::Path &path);
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
virtual void processTick();
|
||||
|
||||
virtual void onDynamicModified(const char* slotName, const char* newValue);
|
||||
virtual void onStaticModified(const char* slotName, const char* newValue);
|
||||
|
||||
virtual void loadStateMachineFile();
|
||||
|
||||
void setStateMachineFile(const char* fileName) { mStateMachineFile = StringTable->insert(fileName); }
|
||||
|
||||
static bool _setSMFile(void *object, const char *index, const char *data);
|
||||
|
||||
void onStateChanged(StateMachine* sm, S32 stateIdx);
|
||||
|
||||
//Callbacks
|
||||
DECLARE_CALLBACK(void, onStateChange, ());
|
||||
};
|
||||
|
||||
#endif
|
||||
358
Engine/source/T3D/components/game/triggerComponent.cpp
Normal file
358
Engine/source/T3D/components/game/triggerComponent.cpp
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "console/consoleTypes.h"
|
||||
#include "T3D/components/game/Triggercomponent.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "core/stream/bitStream.h"
|
||||
#include "console/engineAPI.h"
|
||||
#include "sim/netConnection.h"
|
||||
#include "T3D/gameBase/gameConnection.h"
|
||||
#include "T3D/components/coreInterfaces.h"
|
||||
#include "math/mathUtils.h"
|
||||
#include "collision/concretePolyList.h"
|
||||
#include "collision/clippedPolyList.h"
|
||||
|
||||
#include "gfx/sim/debugDraw.h"
|
||||
|
||||
IMPLEMENT_CALLBACK( TriggerComponent, onEnterViewCmd, void,
|
||||
( Entity* cameraEnt, bool firstTimeSeeing ), ( cameraEnt, firstTimeSeeing ),
|
||||
"@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
|
||||
|
||||
"@param trigger the Trigger instance whose volume the object entered\n"
|
||||
"@param obj the object that entered the volume of the Trigger instance\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( TriggerComponent, onExitViewCmd, void,
|
||||
( Entity* cameraEnt ), ( cameraEnt ),
|
||||
"@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
|
||||
|
||||
"@param trigger the Trigger instance whose volume the object entered\n"
|
||||
"@param obj the object that entered the volume of the Trigger instance\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( TriggerComponent, onUpdateInViewCmd, void,
|
||||
( Entity* cameraEnt ), ( cameraEnt ),
|
||||
"@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
|
||||
|
||||
"@param trigger the Trigger instance whose volume the object entered\n"
|
||||
"@param obj the object that entered the volume of the Trigger instance\n" );
|
||||
|
||||
IMPLEMENT_CALLBACK( TriggerComponent, onUpdateOutOfViewCmd, void,
|
||||
( Entity* cameraEnt ), ( cameraEnt ),
|
||||
"@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n"
|
||||
|
||||
"@param trigger the Trigger instance whose volume the object entered\n"
|
||||
"@param obj the object that entered the volume of the Trigger instance\n" );
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/Destructor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TriggerComponent::TriggerComponent() : Component()
|
||||
{
|
||||
mObjectList.clear();
|
||||
|
||||
mVisible = false;
|
||||
|
||||
mFriendlyName = "Trigger";
|
||||
mComponentType = "Trigger";
|
||||
|
||||
mDescription = getDescriptionText("Calls trigger events when a client starts and stops seeing it. Also ticks while visible to clients.");
|
||||
}
|
||||
|
||||
TriggerComponent::~TriggerComponent()
|
||||
{
|
||||
for(S32 i = 0;i < mFields.size();++i)
|
||||
{
|
||||
ComponentField &field = mFields[i];
|
||||
SAFE_DELETE_ARRAY(field.mFieldDescription);
|
||||
}
|
||||
|
||||
SAFE_DELETE_ARRAY(mDescription);
|
||||
}
|
||||
|
||||
IMPLEMENT_CO_NETOBJECT_V1(TriggerComponent);
|
||||
|
||||
|
||||
bool TriggerComponent::onAdd()
|
||||
{
|
||||
if(! Parent::onAdd())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TriggerComponent::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//This is mostly a catch for situations where the behavior is re-added to the object and the like and we may need to force an update to the behavior
|
||||
void TriggerComponent::onComponentAdd()
|
||||
{
|
||||
Parent::onComponentAdd();
|
||||
|
||||
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
|
||||
|
||||
if(colInt)
|
||||
{
|
||||
colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerComponent::onComponentRemove()
|
||||
{
|
||||
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
|
||||
|
||||
if(colInt)
|
||||
{
|
||||
colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
|
||||
}
|
||||
|
||||
Parent::onComponentRemove();
|
||||
}
|
||||
|
||||
void TriggerComponent::componentAddedToOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId())
|
||||
return;
|
||||
|
||||
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
|
||||
|
||||
if (colInt)
|
||||
{
|
||||
colInt->onCollisionSignal.notify(this, &TriggerComponent::potentialEnterObject);
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerComponent::componentRemovedFromOwner(Component *comp)
|
||||
{
|
||||
if (comp->getId() == getId()) //?????????
|
||||
return;
|
||||
|
||||
CollisionInterface *colInt = mOwner->getComponent<CollisionInterface>();
|
||||
|
||||
if (colInt)
|
||||
{
|
||||
colInt->onCollisionSignal.remove(this, &TriggerComponent::potentialEnterObject);
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerComponent::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("visibile", TypeBool, Offset( mVisible, TriggerComponent ), "" );
|
||||
|
||||
addField("onEnterViewCmd", TypeCommand, Offset(mEnterCommand, TriggerComponent), "");
|
||||
addField("onExitViewCmd", TypeCommand, Offset(mOnExitCommand, TriggerComponent), "");
|
||||
addField("onUpdateInViewCmd", TypeCommand, Offset(mOnUpdateInViewCmd, TriggerComponent), "");
|
||||
}
|
||||
|
||||
U32 TriggerComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
|
||||
{
|
||||
U32 retMask = Parent::packUpdate(con, mask, stream);
|
||||
return retMask;
|
||||
}
|
||||
|
||||
void TriggerComponent::unpackUpdate(NetConnection *con, BitStream *stream)
|
||||
{
|
||||
Parent::unpackUpdate(con, stream);
|
||||
}
|
||||
|
||||
void TriggerComponent::potentialEnterObject(SceneObject *collider)
|
||||
{
|
||||
if(testObject(collider))
|
||||
{
|
||||
bool found = false;
|
||||
for(U32 i=0; i < mObjectList.size(); i++)
|
||||
{
|
||||
if(mObjectList[i]->getId() == collider->getId())
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
mObjectList.push_back(collider);
|
||||
|
||||
if (!mEnterCommand.isEmpty())
|
||||
{
|
||||
String command = String("%obj = ") + collider->getIdString() + ";" +
|
||||
String("%this = ") + getIdString() + ";" + mEnterCommand;
|
||||
Con::evaluate(command.c_str());
|
||||
}
|
||||
|
||||
//onEnterTrigger_callback(this, enter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TriggerComponent::testObject(SceneObject* enter)
|
||||
{
|
||||
//First, test to early out
|
||||
Box3F enterBox = enter->getWorldBox();
|
||||
|
||||
//if(!mOwner->getWorldBox().intersect(enterBox) || !)
|
||||
// return false;
|
||||
|
||||
//We're still here, so we should do actual work
|
||||
//We're going to be
|
||||
ConcretePolyList mClippedList;
|
||||
|
||||
SphereF sphere;
|
||||
sphere.center = (mOwner->getWorldBox().minExtents + mOwner->getWorldBox().maxExtents) * 0.5;
|
||||
VectorF bv = mOwner->getWorldBox().maxExtents - sphere.center;
|
||||
sphere.radius = bv.len();
|
||||
|
||||
Entity* enterEntity = dynamic_cast<Entity*>(enter);
|
||||
if(enterEntity)
|
||||
{
|
||||
//quick early out. If the bounds don't overlap, it cannot be colliding or inside
|
||||
if (!mOwner->getWorldBox().isOverlapped(enterBox))
|
||||
return false;
|
||||
|
||||
//check if the entity has a collision shape
|
||||
CollisionInterface *cI = enterEntity->getComponent<CollisionInterface>();
|
||||
if (cI)
|
||||
{
|
||||
cI->buildPolyList(PLC_Collision, &mClippedList, mOwner->getWorldBox(), sphere);
|
||||
|
||||
if (!mClippedList.isEmpty())
|
||||
{
|
||||
//well, it's clipped with, or inside, our bounds
|
||||
//now to test the clipped list against our own collision mesh
|
||||
CollisionInterface *myCI = mOwner->getComponent<CollisionInterface>();
|
||||
|
||||
//wait, how would we NOT have this?
|
||||
if (myCI)
|
||||
{
|
||||
//anywho, build our list and then we'll check intersections
|
||||
ClippedPolyList myList;
|
||||
|
||||
myList.setTransform(&(mOwner->getTransform()), mOwner->getScale());
|
||||
myList.setObject(mOwner);
|
||||
|
||||
myCI->buildPolyList(PLC_Collision, &myList, enterBox, sphere);
|
||||
|
||||
bool test = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mClippedList.isEmpty() == false;
|
||||
}
|
||||
|
||||
void TriggerComponent::processTick()
|
||||
{
|
||||
Parent::processTick();
|
||||
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
//get our list of active clients, and see if they have cameras, if they do, build a frustum and see if we exist inside that
|
||||
mVisible = false;
|
||||
if(isServerObject())
|
||||
{
|
||||
for(U32 i=0; i < mObjectList.size(); i++)
|
||||
{
|
||||
if(!testObject(mObjectList[i]))
|
||||
{
|
||||
if (!mOnExitCommand.isEmpty())
|
||||
{
|
||||
String command = String("%obj = ") + mObjectList[i]->getIdString() + ";" +
|
||||
String("%this = ") + getIdString() + ";" + mOnExitCommand;
|
||||
Con::evaluate(command.c_str());
|
||||
}
|
||||
|
||||
mObjectList.erase(i);
|
||||
//mDataBlock->onLeaveTrigger_callback( this, remove );
|
||||
//onLeaveTrigger_callback(this, remove);
|
||||
}
|
||||
}
|
||||
|
||||
/*if (!mTickCommand.isEmpty())
|
||||
Con::evaluate(mTickCommand.c_str());
|
||||
|
||||
if (mObjects.size() != 0)
|
||||
onTickTrigger_callback(this);*/
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerComponent::visualizeFrustums(F32 renderTimeMS)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GameConnection* TriggerComponent::getConnection(S32 connectionID)
|
||||
{
|
||||
for(NetConnection *conn = NetConnection::getConnectionList(); conn; conn = conn->getNext())
|
||||
{
|
||||
GameConnection* gameConn = dynamic_cast<GameConnection*>(conn);
|
||||
|
||||
if (!gameConn || (gameConn && gameConn->isAIControlled()))
|
||||
continue;
|
||||
|
||||
if(connectionID == gameConn->getId())
|
||||
return gameConn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TriggerComponent::addClient(S32 clientID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TriggerComponent::removeClient(S32 clientID)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DefineEngineMethod( TriggerComponent, addClient, void,
|
||||
( S32 clientID ), ( -1 ),
|
||||
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||
|
||||
"@param objB Object to mount onto us\n"
|
||||
"@param slot Mount slot ID\n"
|
||||
"@param txfm (optional) mount offset transform\n"
|
||||
"@return true if successful, false if failed (objB is not valid)" )
|
||||
{
|
||||
if(clientID == -1)
|
||||
return;
|
||||
|
||||
object->addClient( clientID );
|
||||
}
|
||||
|
||||
DefineEngineMethod( TriggerComponent, removeClient, void,
|
||||
( S32 clientID ), ( -1 ),
|
||||
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||
|
||||
"@param objB Object to mount onto us\n"
|
||||
"@param slot Mount slot ID\n"
|
||||
"@param txfm (optional) mount offset transform\n"
|
||||
"@return true if successful, false if failed (objB is not valid)" )
|
||||
{
|
||||
if(clientID == -1)
|
||||
return;
|
||||
|
||||
object->removeClient( clientID );
|
||||
}
|
||||
|
||||
DefineEngineMethod( TriggerComponent, visualizeFrustums, void,
|
||||
(F32 renderTime), (1000),
|
||||
"@brief Mount objB to this object at the desired slot with optional transform.\n\n"
|
||||
|
||||
"@param objB Object to mount onto us\n"
|
||||
"@param slot Mount slot ID\n"
|
||||
"@param txfm (optional) mount offset transform\n"
|
||||
"@return true if successful, false if failed (objB is not valid)" )
|
||||
{
|
||||
object->visualizeFrustums(renderTime);
|
||||
}
|
||||
74
Engine/source/T3D/components/game/triggerComponent.h
Normal file
74
Engine/source/T3D/components/game/triggerComponent.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _TRIGGER_COMPONENT_H_
|
||||
#define _TRIGGER_COMPONENT_H_
|
||||
|
||||
#ifndef _COMPONENT_H_
|
||||
#include "T3D/components/component.h"
|
||||
#endif
|
||||
|
||||
#ifndef _ENTITY_H_
|
||||
#include "T3D/entity.h"
|
||||
#endif
|
||||
|
||||
#ifndef _COLLISION_INTERFACES_H_
|
||||
#include "T3D/components/collision/collisionInterfaces.h"
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class TriggerComponent : public Component
|
||||
{
|
||||
typedef Component Parent;
|
||||
|
||||
protected:
|
||||
Vector<SceneObject*> mObjectList;
|
||||
|
||||
bool mVisible;
|
||||
|
||||
String mEnterCommand;
|
||||
String mOnExitCommand;
|
||||
String mOnUpdateInViewCmd;
|
||||
|
||||
public:
|
||||
TriggerComponent();
|
||||
virtual ~TriggerComponent();
|
||||
DECLARE_CONOBJECT(TriggerComponent);
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
static void initPersistFields();
|
||||
|
||||
virtual void onComponentAdd();
|
||||
virtual void onComponentRemove();
|
||||
|
||||
virtual void componentAddedToOwner(Component *comp);
|
||||
virtual void componentRemovedFromOwner(Component *comp);
|
||||
|
||||
virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
|
||||
virtual void unpackUpdate(NetConnection *con, BitStream *stream);
|
||||
|
||||
void potentialEnterObject(SceneObject *collider);
|
||||
|
||||
bool testObject(SceneObject* enter);
|
||||
|
||||
virtual void processTick();
|
||||
|
||||
GameConnection* getConnection(S32 connectionID);
|
||||
|
||||
void addClient(S32 clientID);
|
||||
void removeClient(S32 clientID);
|
||||
|
||||
void visualizeFrustums(F32 renderTimeMS);
|
||||
|
||||
DECLARE_CALLBACK(void, onEnterViewCmd, (Entity* cameraEnt, bool firstTimeSeeing));
|
||||
DECLARE_CALLBACK(void, onExitViewCmd, (Entity* cameraEnt));
|
||||
DECLARE_CALLBACK(void, onUpdateInViewCmd, (Entity* cameraEnt));
|
||||
DECLARE_CALLBACK(void, onUpdateOutOfViewCmd, (Entity* cameraEnt));
|
||||
};
|
||||
|
||||
#endif // _EXAMPLEBEHAVIOR_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue