Преглед на файлове

adds loadIf conditional eval, onLoad/UnloadCommands, ability to freeze loading state and per-subscene ticking for conditional checks
Fixes for child iteration of subscenes
Renamed tripCondition field in triggers to tripIf for consistency/naming clarity
Added ability for callbacks for gamemode to have reference on which subscene was loaded/unloaded for respective callback

JeffR преди 11 месеца
родител
ревизия
484ece3d28
променени са 6 файла, в които са добавени 132 реда и са изтрити 43 реда
  1. 106 30
      Engine/source/T3D/SubScene.cpp
  2. 14 4
      Engine/source/T3D/SubScene.h
  3. 2 2
      Engine/source/T3D/gameMode.cpp
  4. 5 2
      Engine/source/T3D/gameMode.h
  5. 4 4
      Engine/source/T3D/trigger.cpp
  6. 1 1
      Engine/source/T3D/trigger.h

+ 106 - 30
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::testBox(const Box3F& testBox)
+bool SubScene::evaluateCondition()
 {
-   if (mGlobalLayer)
-      return true;
-
-   bool passes = getWorldBox().isOverlapped(testBox);
-   if (passes && !mLoadIf.isEmpty())
+   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());
-      passes = Con::getBoolVariable(resVar.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)
+      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::_closeFile(bool removeFileNotify)
+void SubScene::_removeContents(SimGroupIterator set)
 {
-   AssertFatal(isServerObject(), "Trying to close out a subscene file on the client is bad!");
-
-   U32 count = size();
-
-   for (SimSetIterator itr(this); *itr; ++itr)
+   for (SimGroupIterator itr(set); *itr; ++itr)
    {
-      SimObject* child = dynamic_cast<SimObject*>(*itr);
 
+      SimGroup* child = dynamic_cast<SimGroup*>(*itr);
       if (child)
+      {
+         _removeContents(SimGroupIterator(child));
+
+         GameBase* asGameBase = dynamic_cast<GameBase*>(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!");
+
+   _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<SimGroup*>(*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<SimObject*>(*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<SimObject*>(*cldItr);
+            if (chldChld && chldChld->isSelected())
+            {
+               mStartUnloadTimerMS = Sim::getCurrentTime();
+               return; //if a child is selected, then we don't want to unload
+            }
+         }
       }
    }
 
-   _closeFile(true);
-   mLoaded = false;
-
+   onUnloaded_callback();
    for (U32 i = 0; i < mGameModesList.size(); i++)
    {
-      mGameModesList[i]->onSubsceneUnloaded_callback();
+      mGameModesList[i]->onSubsceneUnloaded_callback(this);
    }
+
+   if (!mOnUnloadCommand.isEmpty())
+   {
+      String command = "%this = " + String(getIdString()) + "; " + mOnUnloadCommand + ";";
+      Con::evaluatef(command.c_str());
+   }
+
+   _closeFile(true);
+   mLoaded = false;
+
 }
 
 bool SubScene::save()

+ 14 - 4
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

+ 2 - 2
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");
 
 

+ 5 - 2
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)

+ 4 - 4
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)
    {

+ 1 - 1
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;