Torque3D/Engine/source/afx/ce/afxPhraseEffect.cpp

314 lines
11 KiB
C++

//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
// Copyright (C) 2015 Faust Logic, Inc.
//
// 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 "afx/arcaneFX.h"
#include "console/engineAPI.h"
#include "afx/afxEffectWrapper.h"
#include "afx/ce/afxPhraseEffect.h"
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// afxPhraseEffectData::ewValidator
//
// When an effect is added using "addEffect", this validator intercepts the value
// and adds it to the dynamic effects list.
//
void afxPhraseEffectData::ewValidator::validateType(SimObject* object, void* typePtr)
{
afxPhraseEffectData* eff_data = dynamic_cast<afxPhraseEffectData*>(object);
afxEffectBaseData** ew = (afxEffectBaseData**)(typePtr);
if (eff_data && ew)
{
eff_data->fx_list.push_back(*ew);
*ew = 0;
}
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
// afxPhraseEffectData
IMPLEMENT_CO_DATABLOCK_V1(afxPhraseEffectData);
ConsoleDocClass( afxPhraseEffectData,
"@brief A datablock that specifies a Phrase Effect, a grouping of other effects.\n\n"
"A Phrase Effect is a grouping or phrase of effects that do nothing until certain trigger events occur. It's like having a whole "
"Effectron organized as an individual effect."
"\n\n"
"Phrase effects can respond to a number of different kinds of triggers:\n"
" -- Player triggers such as footsteps, jumps, landings, and idle triggers.\n"
" -- Arbitrary animation triggers on dts-based scene objects.\n"
" -- Arbitrary trigger bits assigned to active choreographer objects."
"\n\n"
"@ingroup afxEffects\n"
"@ingroup AFX\n"
"@ingroup Datablocks\n"
);
afxPhraseEffectData::afxPhraseEffectData()
{
duration = 0.0f;
n_loops = 1;
// dummy entry holds effect-wrapper pointer while a special validator
// grabs it and adds it to an appropriate effects list
dummy_fx_entry = NULL;
// marked true if datablock ids need to
// be converted into pointers
do_id_convert = false;
trigger_mask = 0;
match_type = MATCH_ANY;
match_state = STATE_ON;
phrase_type = PHRASE_TRIGGERED;
no_choreographer_trigs = false;
no_cons_trigs = false;
no_player_trigs = false;
on_trig_cmd = ST_NULLSTRING;
}
afxPhraseEffectData::afxPhraseEffectData(const afxPhraseEffectData& other, bool temp_clone) : GameBaseData(other, temp_clone)
{
duration = other.duration;
n_loops = other.n_loops;
dummy_fx_entry = other.dummy_fx_entry;
do_id_convert = other.do_id_convert; // --
trigger_mask = other.trigger_mask;
match_type = other.match_type;
match_state = other.match_state;
phrase_type = other.phrase_type;
no_choreographer_trigs = other.no_choreographer_trigs;
no_cons_trigs = other.no_cons_trigs;
no_player_trigs = other.no_player_trigs;
on_trig_cmd = other.on_trig_cmd;
// fx_list; // -- ??
}
void afxPhraseEffectData::reloadReset()
{
fx_list.clear();
}
ImplementEnumType( afxPhraseEffect_MatchType, "Possible phrase effect match types.\n" "@ingroup afxPhraseEffect\n\n" )
{ afxPhraseEffectData::MATCH_ANY, "any", "..." },
{ afxPhraseEffectData::MATCH_ALL, "all", "..." },
EndImplementEnumType;
ImplementEnumType( afxPhraseEffect_StateType, "Possible phrase effect state types.\n" "@ingroup afxPhraseEffect\n\n" )
{ afxPhraseEffectData::STATE_ON, "on", "..." },
{ afxPhraseEffectData::STATE_OFF, "off", "..." },
{ afxPhraseEffectData::STATE_ON_AND_OFF, "both", "..." },
EndImplementEnumType;
ImplementEnumType( afxPhraseEffect_PhraseType, "Possible phrase effect types.\n" "@ingroup afxPhraseEffect\n\n" )
{ afxPhraseEffectData::PHRASE_TRIGGERED, "triggered", "..." },
{ afxPhraseEffectData::PHRASE_CONTINUOUS, "continuous", "..." },
EndImplementEnumType;
#define myOffset(field) Offset(field, afxPhraseEffectData)
void afxPhraseEffectData::initPersistFields()
{
docsURL;
addField("duration", TypeF32, myOffset(duration),
"Specifies a duration for the phrase-effect. If set to infinity, the phrase-effect "
"needs to have a phraseType of continuous. Set infinite duration using "
"$AFX::INFINITE_TIME.");
addField("numLoops", TypeS32, myOffset(n_loops),
"Specifies the number of times the phrase-effect should loop. If set to infinity, "
"the phrase-effect needs to have a phraseType of continuous. Set infinite looping "
"using $AFX::INFINITE_REPEATS.");
addField("triggerMask", TypeS32, myOffset(trigger_mask),
"Sets which bits to consider in the current trigger-state which consists of 32 "
"trigger-bits combined from (possibly overlapping) player trigger bits, constraint "
"trigger bits, and choreographer trigger bits.");
addField("matchType", TYPEID<afxPhraseEffectData::MatchType>(), myOffset(match_type),
"Selects what combination of bits in triggerMask lead to a trigger. When set to "
"'any', any bit in triggerMask matching the current trigger-state will cause a "
"trigger. If set to 'all', every bit in triggerMask must match the trigger-state. "
"Possible values: any or all.");
addField("matchState", TYPEID<afxPhraseEffectData::StateType>(), myOffset(match_state),
"Selects which bit-state(s) of bits in the triggerMask to consider when comparing to "
"the current trigger-state. Possible values: on, off, or both.");
addField("phraseType", TYPEID<afxPhraseEffectData::PhraseType>(), myOffset(phrase_type),
"Selects between triggered and continuous types of phrases. When set to 'triggered', "
"the phrase-effect is triggered when the relevant trigger-bits change state. When set "
"to 'continuous', the phrase-effect will stay active as long as the trigger-bits "
"remain in a matching state. Possible values: triggered or continuous.");
addField("ignoreChoreographerTriggers", TypeBool, myOffset(no_choreographer_trigs),
"When true, trigger-bits on the choreographer will be ignored.");
addField("ignoreConstraintTriggers", TypeBool, myOffset(no_cons_trigs),
"When true, animation triggers from dts-based constraint source objects will be "
"ignored.");
addField("ignorePlayerTriggers", TypeBool, myOffset(no_player_trigs),
"When true, Player-specific triggers from Player-derived constraint source objects "
"will be ignored.");
addField("onTriggerCommand", TypeString, myOffset(on_trig_cmd),
"Like a field substitution statement without the leading '$$' token, this eval "
"statement will be executed when a trigger occurs. Any '%%' and '##' tokens will be "
"substituted.");
// effect lists
// for each of these, dummy_fx_entry is set and then a validator adds it to the appropriate effects list
static ewValidator emptyValidator(0);
addFieldV("addEffect", TYPEID< afxEffectBaseData >(), myOffset(dummy_fx_entry), &emptyValidator,
"A field macro which adds an effect wrapper datablock to a list of effects associated "
"with the phrase-effect's single phrase. Unlike other fields, addEffect follows an "
"unusual syntax. Order is important since the effects will resolve in the order they "
"are added to each list.");
Parent::initPersistFields();
// disallow some field substitutions
disableFieldSubstitutions("addEffect");
}
bool afxPhraseEffectData::onAdd()
{
if (Parent::onAdd() == false)
return false;
return true;
}
void afxPhraseEffectData::pack_fx(BitStream* stream, const afxEffectList& fx, bool packed)
{
stream->writeInt(fx.size(), EFFECTS_PER_PHRASE_BITS);
for (int i = 0; i < fx.size(); i++)
writeDatablockID(stream, fx[i], packed);
}
void afxPhraseEffectData::unpack_fx(BitStream* stream, afxEffectList& fx)
{
fx.clear();
S32 n_fx = stream->readInt(EFFECTS_PER_PHRASE_BITS);
for (int i = 0; i < n_fx; i++)
fx.push_back((afxEffectWrapperData*)(uintptr_t)readDatablockID(stream));
}
void afxPhraseEffectData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->write(duration);
stream->write(n_loops);
stream->write(trigger_mask);
stream->writeInt(match_type, 1);
stream->writeInt(match_state, 2);
stream->writeInt(phrase_type, 1);
stream->writeFlag(no_choreographer_trigs);
stream->writeFlag(no_cons_trigs);
stream->writeFlag(no_player_trigs);
stream->writeString(on_trig_cmd);
pack_fx(stream, fx_list, mPacked);
}
void afxPhraseEffectData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
stream->read(&duration);
stream->read(&n_loops);
stream->read(&trigger_mask);
match_type = stream->readInt(1);
match_state = stream->readInt(2);
phrase_type = stream->readInt(1);
no_choreographer_trigs = stream->readFlag();
no_cons_trigs = stream->readFlag();
no_player_trigs = stream->readFlag();
on_trig_cmd = stream->readSTString();
do_id_convert = true;
unpack_fx(stream, fx_list);
}
bool afxPhraseEffectData::preload(bool server, String &errorStr)
{
if (!Parent::preload(server, errorStr))
return false;
// Resolve objects transmitted from server
if (!server)
{
if (do_id_convert)
{
for (S32 i = 0; i < fx_list.size(); i++)
{
SimObjectId db_id = SimObjectId((uintptr_t)fx_list[i]);
if (db_id != 0)
{
// try to convert id to pointer
if (!Sim::findObject(db_id, fx_list[i]))
{
Con::errorf(ConsoleLogEntry::General,
"afxPhraseEffectData::preload() -- bad datablockId: 0x%x",
db_id);
}
}
}
do_id_convert = false;
}
}
return true;
}
void afxPhraseEffectData::gather_cons_defs(Vector<afxConstraintDef>& defs)
{
afxConstraintDef::gather_cons_defs(defs, fx_list);
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//
DefineEngineMethod( afxPhraseEffectData, pushEffect, void, ( afxEffectBaseData* effectData ),,
"Add a child effect to a phrase effect datablock. Argument can be an afxEffectWrappperData or an afxEffectGroupData.\n" )
{
if (!effectData)
{
Con::errorf("afxPhraseEffectData::pushEffect() -- failed to resolve effect datablock.");
return;
}
object->fx_list.push_back(effectData);
}
//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//