//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~// // 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(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() { 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(), 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(), 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(), 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& 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); } //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//