Implementation of Subscenes, SceneGroups and Gamemodes

Standardizes Gamemodes to be an actual class with data and utility functions that can be parsed
Adds inspector field handling for selecting gamemodes
Updated Scene class to work with Gamemodes for the gamemode field
Updates editor suite elements to be able to create SubScenes, SceneGroups and Gamemodes
Adds ability to convert SimGroup to SubScene
Updates BaseUI's chooselevel menu to have gamemode selection and filters shown levels based on selected gamemode
This commit is contained in:
Areloch 2024-09-01 16:39:00 -05:00
parent 0d07823ecd
commit ae8eca48e1
36 changed files with 2963 additions and 173 deletions

View file

@ -1,20 +1,26 @@
#include "Scene.h"
#include "T3D/assets/LevelAsset.h"
#include "T3D/gameBase/gameConnection.h"
#include "T3D/gameMode.h"
Scene * Scene::smRootScene = nullptr;
Vector<Scene*> Scene::smSceneList;
IMPLEMENT_CALLBACK(Scene, onSaving, void, (const char* fileName), (fileName),
"@brief Called when a scene is saved to allow scenes to special-handle prepwork for saving if required.\n\n"
"@param fileName The level file being saved\n");
IMPLEMENT_CO_NETOBJECT_V1(Scene);
Scene::Scene() :
mIsSubScene(false),
mParentScene(nullptr),
mSceneId(-1),
mIsEditing(false),
mIsDirty(false),
mEditPostFX(0)
{
mGameModeName = StringTable->EmptyString();
mGameModesNames = StringTable->EmptyString();
}
Scene::~Scene()
@ -28,13 +34,12 @@ void Scene::initPersistFields()
Parent::initPersistFields();
addGroup("Internal");
addField("isSubscene", TypeBool, Offset(mIsSubScene, Scene), "", AbstractClassRep::FIELD_HideInInspectors);
addField("isEditing", TypeBool, Offset(mIsEditing, Scene), "", AbstractClassRep::FIELD_HideInInspectors);
addField("isDirty", TypeBool, Offset(mIsDirty, Scene), "", AbstractClassRep::FIELD_HideInInspectors);
endGroup("Internal");
addGroup("Gameplay");
addField("gameModeName", TypeString, Offset(mGameModeName, Scene), "The name of the gamemode that this scene utilizes");
addField("gameModes", TypeGameModeList, Offset(mGameModesNames, Scene), "The game modes that this Scene is associated with.");
endGroup("Gameplay");
addGroup("PostFX");
@ -51,48 +56,33 @@ bool Scene::onAdd()
smSceneList.push_back(this);
mSceneId = smSceneList.size() - 1;
/*if (smRootScene == nullptr)
{
//we're the first scene, so we're the root. woo!
smRootScene = this;
}
else
{
mIsSubScene = true;
smRootScene->mSubScenes.push_back(this);
}*/
GameMode::findGameModes(mGameModesNames, &mGameModesList);
return true;
}
void Scene::onRemove()
{
for (U32 i = 0; i < mGameModesList.size(); i++)
{
mGameModesList[i]->onSceneUnloaded_callback();
}
Parent::onRemove();
smSceneList.remove(this);
mSceneId = -1;
/*if (smRootScene == this)
{
for (U32 i = 0; i < mSubScenes.size(); i++)
{
mSubScenes[i]->deleteObject();
}
}
else if (smRootScene != nullptr)
{
for (U32 i = 0; i < mSubScenes.size(); i++)
{
if(mSubScenes[i]->getId() == getId())
smRootScene->mSubScenes.erase(i);
}
}*/
}
void Scene::onPostAdd()
{
if (isMethod("onPostAdd"))
Con::executef(this, "onPostAdd");
for (U32 i = 0; i < mGameModesList.size(); i++)
{
mGameModesList[i]->onSceneLoaded_callback();
}
}
bool Scene::_editPostEffects(void* object, const char* index, const char* data)
@ -110,11 +100,12 @@ bool Scene::_editPostEffects(void* object, const char* index, const char* data)
void Scene::addObject(SimObject* object)
{
//Child scene
Scene* scene = dynamic_cast<Scene*>(object);
SubScene* scene = dynamic_cast<SubScene*>(object);
if (scene)
{
//We'll keep these principly separate so they don't get saved into each other
mSubScenes.push_back(scene);
Parent::addObject(object);
return;
}
@ -135,7 +126,7 @@ void Scene::addObject(SimObject* object)
void Scene::removeObject(SimObject* object)
{
//Child scene
Scene* scene = dynamic_cast<Scene*>(object);
SubScene* scene = dynamic_cast<SubScene*>(object);
if (scene)
{
//We'll keep these principly separate so they don't get saved into each other
@ -175,12 +166,58 @@ void Scene::removeDynamicObject(SceneObject* object)
void Scene::interpolateTick(F32 delta)
{
}
void Scene::processTick()
{
if (!isServerObject())
return;
//iterate over our subscenes to update their status of loaded or unloaded based on if any control objects intersect their bounds
for (U32 i = 0; i < mSubScenes.size(); i++)
{
bool hasClients = false;
SimGroup* pClientGroup = Sim::getClientGroup();
for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
{
GameConnection* gc = dynamic_cast<GameConnection*>(*itr);
if (gc)
{
GameBase* controlObj = gc->getControlObject();
if (controlObj == nullptr)
{
controlObj = gc->getCameraObject();
}
if (controlObj != nullptr)
{
if (mSubScenes[i]->testBox(controlObj->getWorldBox()))
{
//we have a client controlling object in the bounds, so we ensure the contents are loaded
hasClients = true;
break;
}
}
}
}
if (hasClients)
{
mSubScenes[i]->setUnloadTimeMS(-1);
mSubScenes[i]->load();
}
else
{
if (mSubScenes[i]->isLoaded() && mSubScenes[i]->getUnloadTimsMS() == -1)
{
mSubScenes[i]->setUnloadTimeMS(Sim::getCurrentTime());
}
if (Sim::getCurrentTime() - mSubScenes[i]->getUnloadTimsMS() > 5000)
mSubScenes[i]->unload();
}
}
}
void Scene::advanceTime(F32 timeDelta)
@ -257,6 +294,21 @@ bool Scene::saveScene(StringTableEntry fileName)
fileName = getOriginatingFile();
}
//Inform our objects we're saving, so if they do any special stuff
//they can do it before the actual write-out
for (U32 i = 0; i < mPermanentObjects.size(); i++)
{
SceneObject* obj = mPermanentObjects[i];
obj->onSaving_callback(fileName);
}
//Inform our subscenes we're saving so they can do any
//special work required as well
for (U32 i = 0; i < mSubScenes.size(); i++)
{
mSubScenes[i]->save();
}
bool saveSuccess = save(fileName);
if (!saveSuccess)
@ -286,9 +338,12 @@ bool Scene::saveScene(StringTableEntry fileName)
dSprintf(depValue, sizeof(depValue), "%s=%s", ASSET_ID_SIGNATURE, utilizedAssetsList[i]);
levelAssetDef->setDataField(StringTable->insert(depSlotName), NULL, StringTable->insert(depValue));
}
//update the gamemode list as well
levelAssetDef->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
//Finally, save
saveSuccess = levelAssetDef->saveAsset();
return saveSuccess;
@ -413,8 +468,6 @@ DefineEngineMethod(Scene, getLevelAsset, const char*, (), ,
DefineEngineMethod(Scene, save, bool, (const char* fileName), (""),
"Save out the object to the given file.\n"
"@param fileName The name of the file to save to."
"@param selectedOnly If true, only objects marked as selected will be saved out.\n"
"@param preAppendString Text which will be preprended directly to the object serialization.\n"
"@param True on success, false on failure.")
{
return object->saveScene(StringTable->insert(fileName));