diff --git a/Engine/source/T3D/SubScene.cpp b/Engine/source/T3D/SubScene.cpp index 59f68d3dd..03e3fbe69 100644 --- a/Engine/source/T3D/SubScene.cpp +++ b/Engine/source/T3D/SubScene.cpp @@ -7,16 +7,25 @@ #include "gfx/gfxDrawUtil.h" #include "gfx/gfxTransformSaver.h" #include "gui/editor/inspector/group.h" +#include "T3D/gameBase/gameBase.h" IMPLEMENT_CO_NETOBJECT_V1(SubScene); S32 SubScene::mUnloadTimeoutMs = 5000; +IMPLEMENT_CALLBACK(SubScene, onLoaded, void, (), (), + "@brief Called when a subScene has been loaded and has game mode implications.\n\n"); +IMPLEMENT_CALLBACK(SubScene, onUnloaded, void, (), (), + "@brief Called when a subScene has been unloaded and has game mode implications.\n\n"); + SubScene::SubScene() : mLevelAssetId(StringTable->EmptyString()), mGameModesNames(StringTable->EmptyString()), mScopeDistance(-1), mLoaded(false), + mFreezeLoading(false), + mTickPeriodMS(1000), + mCurrTick(0), mGlobalLayer(false) { mNetFlags.set(Ghostable | ScopeAlways); @@ -33,6 +42,8 @@ bool SubScene::onAdd() if (!Parent::onAdd()) return false; + setProcessTick(true); + return true; } @@ -41,6 +52,8 @@ void SubScene::onRemove() if (isClientObject()) removeFromScene(); + unload(); + Parent::onRemove(); } @@ -49,10 +62,20 @@ void SubScene::initPersistFields() addGroup("SubScene"); addField("isGlobalLayer", TypeBool, Offset(mGlobalLayer, SubScene), ""); INITPERSISTFIELD_LEVELASSET(Level, SubScene, "The level asset to load."); - addField("loadIf", TypeCommand, Offset(mLoadIf, SubScene), "evaluation condition (true/false)"); addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with."); endGroup("SubScene"); + addGroup("LoadingManagement"); + addField("freezeLoading", TypeBool, Offset(mFreezeLoading, SubScene), "If true, will prevent the zone from being changed from it's current loading state."); + addField("loadIf", TypeCommand, Offset(mLoadIf, SubScene), "evaluation condition (true/false)"); + + addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)"); + + addField("onLoadCommand", TypeCommand, Offset(mOnLoadCommand, SubScene), "The command to execute when the subscene is loaded. Maximum 1023 characters."); + addField("onUnloadCommand", TypeCommand, Offset(mOnUnloadCommand, SubScene), "The command to execute when subscene is unloaded. Maximum 1023 characters."); + endGroup("LoadingManagement"); + + Parent::initPersistFields(); } @@ -139,22 +162,29 @@ void SubScene::inspectPostApply() setMaskBits(-1); } +bool SubScene::evaluateCondition() +{ + if (!mLoadIf.isEmpty()) + { + //test the mapper plugged in condition line + String resVar = getIdString() + String(".result"); + Con::setBoolVariable(resVar.c_str(), false); + String command = resVar + "=" + mLoadIf + ";"; + + Con::evaluatef(command.c_str()); + return Con::getBoolVariable(resVar.c_str()); + } + return true; +} + bool SubScene::testBox(const Box3F& testBox) { if (mGlobalLayer) return true; bool passes = getWorldBox().isOverlapped(testBox); - if (passes && !mLoadIf.isEmpty()) - { - //test the mapper plugged in condition line - String resVar = getIdString() + String(".result"); - Con::setBoolVariable(resVar.c_str(), false); - String command = resVar + "=" + mLoadIf + ";"; - Con::evaluatef(command.c_str()); - passes = Con::getBoolVariable(resVar.c_str()); - } - + if (passes) + passes = evaluateCondition(); return passes; } @@ -187,6 +217,14 @@ void SubScene::write(Stream& stream, U32 tabStop, U32 flags) void SubScene::processTick(const Move* move) { + mCurrTick += TickMs; + if (mCurrTick > mTickPeriodMS) + { + mCurrTick = 0; + //re-evaluate + if (!evaluateCondition()) + unload(); + } } void SubScene::_onFileChanged(const Torque::Path& path) @@ -201,19 +239,34 @@ void SubScene::_onFileChanged(const Torque::Path& path) setMaskBits(U32_MAX); } +void SubScene::_removeContents(SimGroupIterator set) +{ + for (SimGroupIterator itr(set); *itr; ++itr) + { + + SimGroup* child = dynamic_cast(*itr); + if (child) + { + _removeContents(SimGroupIterator(child)); + + GameBase* asGameBase = dynamic_cast(child); + if (asGameBase) + { + asGameBase->scriptOnRemove(); + } + + Sim::cancelPendingEvents(child); + + child->safeDeleteObject(); + } + } +} + void SubScene::_closeFile(bool removeFileNotify) { AssertFatal(isServerObject(), "Trying to close out a subscene file on the client is bad!"); - U32 count = size(); - - for (SimSetIterator itr(this); *itr; ++itr) - { - SimObject* child = dynamic_cast(*itr); - - if (child) - child->safeDeleteObject(); - } + _removeContents(SimGroupIterator(this)); if (removeFileNotify && mLevelAsset.notNull() && mLevelAsset->getLevelPath() != StringTable->EmptyString()) { @@ -249,14 +302,24 @@ void SubScene::load() if (mLoaded) return; + if (mFreezeLoading) + return; + _loadFile(true); mLoaded = true; GameMode::findGameModes(mGameModesNames, &mGameModesList); + onLoaded_callback(); for (U32 i = 0; i < mGameModesList.size(); i++) { - mGameModesList[i]->onSubsceneLoaded_callback(); + mGameModesList[i]->onSubsceneLoaded_callback(this); + } + + if (!mOnLoadCommand.isEmpty()) + { + String command = "%this = " + String(getIdString()) + "; " + mLoadIf + ";"; + Con::evaluatef(command.c_str()); } } @@ -265,6 +328,9 @@ void SubScene::unload() if (!mLoaded) return; + if (mFreezeLoading) + return; + if (isSelected()) { mStartUnloadTimerMS = Sim::getCurrentTime(); @@ -273,33 +339,43 @@ void SubScene::unload() //scan down through our child objects, see if any are marked as selected, //if so, skip unloading and reset the timer - for (SimSetIterator itr(this); *itr; ++itr) + for (SimGroupIterator itr(this); *itr; ++itr) { SimGroup* childGrp = dynamic_cast(*itr); - if (childGrp && childGrp->isSelected()) + if (childGrp) { - mStartUnloadTimerMS = Sim::getCurrentTime(); - return; //if a child is selected, then we don't want to unload - } - - for (SimSetIterator cldItr(childGrp); *cldItr; ++cldItr) - { - SimObject* chldChld = dynamic_cast(*cldItr); - if (chldChld && chldChld->isSelected()) + if (childGrp->isSelected()) { mStartUnloadTimerMS = Sim::getCurrentTime(); return; //if a child is selected, then we don't want to unload } + for (SimGroupIterator cldItr(childGrp); *cldItr; ++cldItr) + { + SimObject* chldChld = dynamic_cast(*cldItr); + if (chldChld && chldChld->isSelected()) + { + mStartUnloadTimerMS = Sim::getCurrentTime(); + return; //if a child is selected, then we don't want to unload + } + } } } + onUnloaded_callback(); + for (U32 i = 0; i < mGameModesList.size(); i++) + { + mGameModesList[i]->onSubsceneUnloaded_callback(this); + } + + if (!mOnUnloadCommand.isEmpty()) + { + String command = "%this = " + String(getIdString()) + "; " + mOnUnloadCommand + ";"; + Con::evaluatef(command.c_str()); + } + _closeFile(true); mLoaded = false; - for (U32 i = 0; i < mGameModesList.size(); i++) - { - mGameModesList[i]->onSubsceneUnloaded_callback(); - } } bool SubScene::save() diff --git a/Engine/source/T3D/SubScene.h b/Engine/source/T3D/SubScene.h index 53c8b1296..e70ae812b 100644 --- a/Engine/source/T3D/SubScene.h +++ b/Engine/source/T3D/SubScene.h @@ -8,9 +8,8 @@ #ifndef LEVEL_ASSET_H #include "assets/LevelAsset.h" #endif -#ifndef GAME_MODE_H -#include "gameMode.h" -#endif + +class GameMode; class SubScene : public SceneGroup { @@ -38,7 +37,14 @@ private: S32 mStartUnloadTimerMS; bool mLoaded; + bool mFreezeLoading; + String mLoadIf; + String mOnLoadCommand; + String mOnUnloadCommand; + + S32 mTickPeriodMS; + U32 mCurrTick; bool mGlobalLayer; public: @@ -50,6 +56,7 @@ public: static void initPersistFields(); static void consoleInit(); + StringTableEntry getTypeHint() const override { return (getLevelAsset()) ? getLevelAsset()->getAssetName() : StringTable->EmptyString(); } // SimObject bool onAdd() override; @@ -65,7 +72,7 @@ public: void inspectPostApply() override; bool testBox(const Box3F& testBox); - + bool evaluateCondition(); void _onSelected() override; void _onUnselected() override; @@ -76,6 +83,7 @@ protected: // void _onFileChanged(const Torque::Path& path); + void _removeContents(SimGroupIterator); void _closeFile(bool removeFileNotify); void _loadFile(bool addFileNotify); @@ -104,6 +112,8 @@ public: bool save(); + DECLARE_CALLBACK(void, onLoaded, ()); + DECLARE_CALLBACK(void, onUnloaded, ()); DECLARE_ASSET_SETGET(SubScene, Level); }; #endif diff --git a/Engine/source/T3D/gameMode.cpp b/Engine/source/T3D/gameMode.cpp index 0032b12ad..32253cca5 100644 --- a/Engine/source/T3D/gameMode.cpp +++ b/Engine/source/T3D/gameMode.cpp @@ -16,9 +16,9 @@ IMPLEMENT_CALLBACK(GameMode, onSceneLoaded, void, (), (), "@brief Called when a scene has been loaded and has game mode implications.\n\n"); IMPLEMENT_CALLBACK(GameMode, onSceneUnloaded, void, (), (), "@brief Called when a scene has been unloaded and has game mode implications.\n\n"); -IMPLEMENT_CALLBACK(GameMode, onSubsceneLoaded, void, (), (), +IMPLEMENT_CALLBACK(GameMode, onSubsceneLoaded, void, (SubScene*), ("SubScene"), "@brief Called when a subScene has been loaded and has game mode implications.\n\n"); -IMPLEMENT_CALLBACK(GameMode, onSubsceneUnloaded, void, (), (), +IMPLEMENT_CALLBACK(GameMode, onSubsceneUnloaded, void, (SubScene*), ("SubScene"), "@brief Called when a subScene has been unloaded and has game mode implications.\n\n"); diff --git a/Engine/source/T3D/gameMode.h b/Engine/source/T3D/gameMode.h index 9ebde69ed..d28deecc0 100644 --- a/Engine/source/T3D/gameMode.h +++ b/Engine/source/T3D/gameMode.h @@ -8,6 +8,9 @@ #endif #endif +#ifndef SUB_SCENE_H +#include "SubScene.h" +#endif #include "T3D/assets/ImageAsset.h" @@ -48,8 +51,8 @@ public: DECLARE_CALLBACK(void, onDeactivated, ()); DECLARE_CALLBACK(void, onSceneLoaded, ()); DECLARE_CALLBACK(void, onSceneUnloaded, ()); - DECLARE_CALLBACK(void, onSubsceneLoaded, ()); - DECLARE_CALLBACK(void, onSubsceneUnloaded, ()); + DECLARE_CALLBACK(void, onSubsceneLoaded, (SubScene*)); + DECLARE_CALLBACK(void, onSubsceneUnloaded, (SubScene*)); }; DefineConsoleType(TypeGameModeList, String) diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp index 0320d6208..dcccd0778 100644 --- a/Engine/source/T3D/trigger.cpp +++ b/Engine/source/T3D/trigger.cpp @@ -173,7 +173,7 @@ Trigger::Trigger() mPhysicsRep = NULL; mTripOnce = false; mTrippedBy = 0xFFFFFFFF; - mTripCondition = ""; + mTripIf = ""; //Default up a basic square Point3F vecs[3] = { Point3F(1.0, 0.0, 0.0), @@ -379,7 +379,7 @@ void Trigger::initPersistFields() "representing the edges extending from the corner.\n"); addField("TripOnce", TypeBool, Offset(mTripOnce, Trigger),"Do we trigger callacks just the once?"); - addField("TripCondition", TypeRealString, Offset(mTripCondition, Trigger),"evaluation condition to trip callbacks (true/false)"); + addField("tripIf", TypeRealString, Offset(mTripIf, Trigger),"evaluation condition to trip callbacks (true/false)"); addField("TrippedBy", TypeGameTypeMasksType, Offset(mTrippedBy, Trigger), "typemask filter"); addProtectedField("enterCommand", TypeCommand, Offset(mEnterCommand, Trigger), &setEnterCmd, &defaultProtectedGetFn, "The command to execute when an object enters this trigger. Object id stored in %%obj. Maximum 1023 characters." ); @@ -697,13 +697,13 @@ bool Trigger::testTrippable() bool Trigger::testCondition() { - if (mTripCondition.isEmpty()) + if (mTripIf.isEmpty()) return true; //we've got no tests to run so just do it //test the mapper plugged in condition line String resVar = getIdString() + String(".result"); Con::setBoolVariable(resVar.c_str(), false); - String command = resVar + "=" + mTripCondition + ";"; + String command = resVar + "=" + mTripIf + ";"; Con::evaluatef(command.c_str()); if (Con::getBoolVariable(resVar.c_str()) == 1) { diff --git a/Engine/source/T3D/trigger.h b/Engine/source/T3D/trigger.h index 7cd00dd34..9b9190e67 100644 --- a/Engine/source/T3D/trigger.h +++ b/Engine/source/T3D/trigger.h @@ -87,7 +87,7 @@ class Trigger : public GameBase bool mTripped; S32 mTrippedBy; - String mTripCondition; + String mTripIf; String mEnterCommand; String mLeaveCommand; String mTickCommand;