Merge branch 'SubScenes_Gamemode_PR' of https://github.com/Areloch/Torque3D into development

This commit is contained in:
JeffR 2024-10-20 11:14:52 -05:00
commit dde0ffe32f
41 changed files with 3182 additions and 239 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,38 @@ 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 (SimGroup::iterator itr = begin(); itr != end(); itr++)
{
SimGroup* sg = dynamic_cast<SimGroup*>(*itr);
if (sg)
{
ConsoleValue vars[3];
vars[2].setString(fileName);
sg->callOnChildren("onSaving", 3, vars);
}
SceneObject* sO = dynamic_cast<SceneObject*>(*itr);
if (sO)
{
sO->onSaving_callback(fileName);
}
}
/*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 +355,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;
@ -314,11 +386,26 @@ void Scene::getUtilizedAssetsFromSceneObject(SimObject* object, Vector<StringTab
}
//
Vector<SceneObject*> Scene::getObjectsByClass(String className, bool checkSubscenes)
Vector<SceneObject*> Scene::getObjectsByClass(String className)
{
return Vector<SceneObject*>();
}
void Scene::loadAtPosition(const Point3F& position)
{
for (U32 i = 0; i < mSubScenes.size(); i++)
{
Box3F testBox = Box3F(0.5);
testBox.setCenter(position);
if (mSubScenes[i]->testBox(testBox))
{
mSubScenes[i]->setUnloadTimeMS(-1);
mSubScenes[i]->load();
}
}
}
DefineEngineFunction(getScene, Scene*, (U32 sceneId), (0),
"Get the root Scene object that is loaded.\n"
"@return The id of the Root Scene. Will be 0 if no root scene is loaded")
@ -413,9 +500,13 @@ 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));
}
DefineEngineMethod(Scene, loadAtPosition, void, (Point3F position), (Point3F::Zero),
"Loads any subscenes at a given point by force.\n")
{
object->loadAtPosition(position);
}

View file

@ -1,5 +1,5 @@
#pragma once
#ifndef SCENE_H
#include "console/engineAPI.h"
#ifndef _NETOBJECT_H_
@ -9,8 +9,16 @@
#ifndef _ITICKABLE_H_
#include "core/iTickable.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef GAME_MODE_H
#include "gameMode.h"
#endif
#ifndef SUB_SCENE_H
#include "SubScene.h"
#endif
/// Scene
/// This object is effectively a smart container to hold and manage any relevent scene objects and data
@ -19,14 +27,11 @@ class Scene : public NetObject, public virtual ITickable
{
typedef NetObject Parent;
bool mIsSubScene;
Scene* mParentScene;
Vector<Scene*> mSubScenes;
Vector<SubScene*> mSubScenes;
Vector<SceneObject*> mPermanentObjects;
Vector<SceneObject*> mDynamicObjects;
S32 mSceneId;
@ -37,7 +42,8 @@ class Scene : public NetObject, public virtual ITickable
bool mEditPostFX;
StringTableEntry mGameModeName;
StringTableEntry mGameModesNames;
Vector<GameMode*> mGameModesList;
protected:
static Scene * smRootScene;
@ -80,12 +86,14 @@ public:
void unpackUpdate(NetConnection *conn, BitStream *stream) override;
//
Vector<SceneObject*> getObjectsByClass(String className, bool checkSubscenes);
Vector<SceneObject*> getObjectsByClass(String className);
void getUtilizedAssetsFromSceneObject(SimObject* object, Vector<StringTableEntry>* usedAssetsList);
template <class T>
Vector<T*> getObjectsByClass(bool checkSubscenes);
Vector<T*> getObjectsByClass();
void loadAtPosition(const Point3F& position);
static Scene *getRootScene()
{
@ -96,11 +104,13 @@ public:
}
static Vector<Scene*> smSceneList;
DECLARE_CALLBACK(void, onSaving, (const char* fileName));
};
template <class T>
Vector<T*> Scene::getObjectsByClass(bool checkSubscenes)
Vector<T*> Scene::getObjectsByClass()
{
Vector<T*> foundObjects;
@ -121,18 +131,6 @@ Vector<T*> Scene::getObjectsByClass(bool checkSubscenes)
foundObjects.push_back(curObject);
}
if (checkSubscenes)
{
for (U32 i = 0; i < mSubScenes.size(); i++)
{
Vector<T*> appendList = mSubScenes[i]->getObjectsByClass<T>(true);
for (U32 a = 0; a < appendList.size(); a++)
{
foundObjects.push_back(appendList[a]);
}
}
}
return foundObjects;
}
#endif

View file

@ -0,0 +1,352 @@
#include "SceneGroup.h"
#include "gameBase/gameConnection.h"
#include "gfx/gfxDrawUtil.h"
#include "gfx/gfxTransformSaver.h"
#include "gui/editor/inspector/group.h"
#include "gui/worldEditor/editor.h"
#include "math/mathIO.h"
#include "physics/physicsShape.h"
#include "renderInstance/renderPassManager.h"
#include "scene/sceneRenderState.h"
IMPLEMENT_CO_NETOBJECT_V1(SceneGroup);
ConsoleDocClass(SceneGroup,
"@brief A collection of arbitrary objects which can be allocated and manipulated as a group.\n\n"
"%SceneGroup always points to a (.SceneGroup) file which defines its objects. In "
"fact more than one %SceneGroup can reference this file and both will update "
"if the file is modified.\n\n"
"%SceneGroup is a very simple object and only exists on the server. When it is "
"created it allocates children objects by reading the (.SceneGroup) file like "
"a list of instructions. It then sets their transform relative to the %SceneGroup "
"and Torque networking handles the rest by ghosting the new objects to clients. "
"%SceneGroup itself is not ghosted.\n\n"
"@ingroup enviroMisc"
);
SceneGroup::SceneGroup()
{
// Not ghosted unless we're editing
mNetFlags.clear(Ghostable | ScopeAlways);
mTypeMask |= StaticObjectType;
}
SceneGroup::~SceneGroup()
{
}
void SceneGroup::initPersistFields()
{
docsURL;
addGroup("SceneGroup");
endGroup("SceneGroup");
Parent::initPersistFields();
}
bool SceneGroup::onAdd()
{
if (!Parent::onAdd())
return false;
mObjBox.set(Point3F(-0.5f, -0.5f, -0.5f),
Point3F(0.5f, 0.5f, 0.5f));
resetWorldBox();
// Not added to the scene unless we are editing.
if (gEditingMission)
onEditorEnable();
addToScene();
return true;
}
void SceneGroup::onRemove()
{
removeFromScene();
Parent::onRemove();
}
void SceneGroup::onEditorEnable()
{
if (isClientObject())
return;
// Just in case we are already in the scene, lets not cause an assert.
if (mContainer != NULL)
return;
// Enable ghosting so we can see this on the client.
mNetFlags.set(Ghostable);
setScopeAlways();
addToScene();
Parent::onEditorEnable();
}
void SceneGroup::onEditorDisable()
{
if (isClientObject())
return;
// Just in case we are not in the scene, lets not cause an assert.
if (mContainer == NULL)
return;
// Do not need this on the client if we are not editing.
removeFromScene();
mNetFlags.clear(Ghostable);
clearScopeAlways();
Parent::onEditorDisable();
}
void SceneGroup::inspectPostApply()
{
Parent::inspectPostApply();
}
void SceneGroup::onInspect(GuiInspector* inspector)
{
Parent::onInspect(inspector);
//Put the SubScene group before everything that'd be SubScene-effecting, for orginazational purposes
GuiInspectorGroup* sceneGroupGrp = inspector->findExistentGroup(StringTable->insert("SceneGroup"));
if (!sceneGroupGrp)
return;
GuiControl* stack = dynamic_cast<GuiControl*>(sceneGroupGrp->findObjectByInternalName(StringTable->insert("Stack")));
//Regen bounds button
GuiInspectorField* regenFieldGui = sceneGroupGrp->createInspectorField();
regenFieldGui->init(inspector, sceneGroupGrp);
regenFieldGui->setSpecialEditField(true);
regenFieldGui->setTargetObject(this);
StringTableEntry fldnm = StringTable->insert("RegenerateBounds");
regenFieldGui->setSpecialEditVariableName(fldnm);
regenFieldGui->setInspectorField(NULL, fldnm);
regenFieldGui->setDocs("");
stack->addObject(regenFieldGui);
GuiButtonCtrl* regenButton = new GuiButtonCtrl();
regenButton->registerObject();
regenButton->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
regenButton->setText("Regenerate Bounds");
regenButton->resize(Point2I::Zero, regenFieldGui->getExtent());
regenButton->setHorizSizing(GuiControl::horizResizeWidth);
regenButton->setVertSizing(GuiControl::vertResizeHeight);
char rgBuffer[512];
dSprintf(rgBuffer, 512, "%d.recalculateBounds();", this->getId());
regenButton->setConsoleCommand(rgBuffer);
regenFieldGui->addObject(regenButton);
}
void SceneGroup::setTransform(const MatrixF& mat)
{
//transform difference
MatrixF oldTransform = getTransform();
Parent::setTransform(mat);
// Calculate the delta transformation
MatrixF deltaTransform;
oldTransform.inverse();
deltaTransform.mul(oldTransform, getTransform());
if (isServerObject())
{
setMaskBits(TransformMask);
// Update all child transforms
for (SimSetIterator itr(this); *itr; ++itr)
{
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
if (child)
{
MatrixF childTransform = child->getTransform();
MatrixF relativeTransform;
relativeTransform.mul(deltaTransform, childTransform);
child->setTransform(relativeTransform);
child->setScale(childTransform.getScale()); //we don't modify scale
}
}
}
}
void SceneGroup::setRenderTransform(const MatrixF& mat)
{
//transform difference
MatrixF oldTransform = getRenderTransform();
Parent::setRenderTransform(mat);
// Calculate the delta transformation
MatrixF deltaTransform;
oldTransform.inverse();
deltaTransform.mul(oldTransform, getRenderTransform());
// Update all child transforms
for (SimSetIterator itr(this); *itr; ++itr)
{
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
if (child)
{
MatrixF childTransform = child->getRenderTransform();
MatrixF relativeTransform;
relativeTransform.mul(deltaTransform, childTransform);
child->setRenderTransform(relativeTransform);
child->setScale(childTransform.getScale()); //we don't modify scale
}
}
}
void SceneGroup::addObject(SimObject* object)
{
Parent::addObject(object);
// Recalculate the bounding box from scratch (simpler but potentially costly)
recalculateBoundingBox();
}
void SceneGroup::removeObject(SimObject* object)
{
Parent::removeObject(object);
// Recalculate the bounding box from scratch (simpler but potentially costly)
recalculateBoundingBox();
}
void SceneGroup::recalculateBoundingBox()
{
if (empty())
return;
// Reset the bounding box
Box3F bounds;
bounds.minExtents.set(1e10, 1e10, 1e10);
bounds.maxExtents.set(-1e10, -1e10, -1e10);
// Extend the bounding box to include each child's bounding box
for (SimSetIterator itr(this); *itr; ++itr)
{
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
if (child)
{
const Box3F& childBox = child->getWorldBox();
bounds.minExtents.setMin(childBox.minExtents);
bounds.maxExtents.setMax(childBox.maxExtents);
}
}
MatrixF newTrans = mObjToWorld;
newTrans.setPosition(bounds.getCenter());
Parent::setTransform(newTrans);
mObjScale = Point3F(bounds.len_x(), bounds.len_y(), bounds.len_z());
mWorldBox = bounds;
resetObjectBox();
setMaskBits(TransformMask);
}
U32 SceneGroup::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(conn, mask, stream);
mathWrite(*stream, mObjBox);
if (stream->writeFlag(mask & TransformMask))
{
mathWrite(*stream, getTransform());
mathWrite(*stream, getScale());
}
return retMask;
}
void SceneGroup::unpackUpdate(NetConnection* conn, BitStream* stream)
{
Parent::unpackUpdate(conn, stream);
mathRead(*stream, &mObjBox);
resetWorldBox();
// TransformMask
if (stream->readFlag())
{
mathRead(*stream, &mObjToWorld);
mathRead(*stream, &mObjScale);
setTransform(mObjToWorld);
}
}
bool SceneGroup::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere)
{
Vector<SceneObject*> foundObjects;
if (empty())
{
Con::warnf("SceneGroup::buildPolyList() - SceneGroup %s is empty!", getName());
return false;
}
findObjectByType(foundObjects);
for (S32 i = 0; i < foundObjects.size(); i++)
{
foundObjects[i]->buildPolyList(context, polyList, box, sphere);
}
return true;
}
bool SceneGroup::buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F& box, const SphereF& sphere)
{
Vector<SceneObject*> foundObjects;
findObjectByType(foundObjects);
for (S32 i = 0; i < foundObjects.size(); i++)
{
foundObjects[i]->buildExportPolyList(exportData, box, sphere);
}
return true;
}
void SceneGroup::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
{
//if (empty())
return;
Vector<SceneObject*> foundObjects;
findObjectByType(foundObjects);
for (S32 i = 0; i < foundObjects.size(); i++)
{
SceneObject* child = foundObjects[i];
child->getUtilizedAssets(usedAssetsList);
}
}
DefineEngineMethod(SceneGroup, recalculateBounds, void, (), ,
"Recalculates the SceneGroups' bounds and centerpoint.\n")
{
object->recalculateBoundingBox();
}

View file

@ -0,0 +1,55 @@
#pragma once
#ifndef SCENE_GROUP_H
#define SCENE_GROUP_H
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
class SceneGroup : public SceneObject
{
typedef SceneObject Parent;
public:
enum MaskBits
{
TransformMask = Parent::NextFreeMask << 0,
NextFreeMask = Parent::NextFreeMask << 1
};
public:
SceneGroup();
virtual ~SceneGroup();
DECLARE_CONOBJECT(SceneGroup);
DECLARE_CATEGORY("Object \t Collection");
static void initPersistFields();
// SimObject
bool onAdd() override;
void onRemove() override;
void onEditorEnable() override;
void onEditorDisable() override;
void inspectPostApply() override;
void onInspect(GuiInspector* inspector) override;
// NetObject
U32 packUpdate(NetConnection* conn, U32 mask, BitStream* stream) override;
void unpackUpdate(NetConnection* conn, BitStream* stream) override;
// SceneObject
void setTransform(const MatrixF& mat) override;
void setRenderTransform(const MatrixF& mat) override;
// Object management
void addObject(SimObject* object) override;
void removeObject(SimObject* object) override;
void recalculateBoundingBox();
///
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere) override;
bool buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F& box, const SphereF&) override;
void getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList) override;
};
#endif

View file

@ -0,0 +1,446 @@
#include "SubScene.h"
#include "gameMode.h"
#include "console/script.h"
#include "scene/sceneRenderState.h"
#include "renderInstance/renderPassManager.h"
#include "gfx/gfxDrawUtil.h"
#include "gfx/gfxTransformSaver.h"
#include "gui/editor/inspector/group.h"
IMPLEMENT_CO_NETOBJECT_V1(SubScene);
S32 SubScene::mUnloadTimeoutMs = 5000;
SubScene::SubScene() :
mLevelAssetId(StringTable->EmptyString()),
mGameModesNames(StringTable->EmptyString()),
mScopeDistance(-1),
mLoaded(false),
mGlobalLayer(false)
{
mNetFlags.set(Ghostable | ScopeAlways);
mTypeMask |= StaticObjectType;
}
SubScene::~SubScene()
{
}
bool SubScene::onAdd()
{
if (!Parent::onAdd())
return false;
return true;
}
void SubScene::onRemove()
{
if (isClientObject())
removeFromScene();
Parent::onRemove();
}
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");
Parent::initPersistFields();
}
void SubScene::consoleInit()
{
Parent::consoleInit();
Con::addVariable("$SubScene::UnloadTimeoutMS", TypeBool, &SubScene::mUnloadTimeoutMs, "The amount of time in milliseconds it takes for a SubScene to be unloaded if it's inactive.\n"
"@ingroup Editors\n");
}
void SubScene::addObject(SimObject* object)
{
SceneObject::addObject(object);
}
void SubScene::removeObject(SimObject* object)
{
SceneObject::removeObject(object);
}
U32 SubScene::packUpdate(NetConnection* conn, U32 mask, BitStream* stream)
{
U32 retMask = Parent::packUpdate(conn, mask, stream);
stream->writeFlag(mGlobalLayer);
return retMask;
}
void SubScene::unpackUpdate(NetConnection* conn, BitStream* stream)
{
Parent::unpackUpdate(conn, stream);
mGlobalLayer = stream->readFlag();
}
void SubScene::onInspect(GuiInspector* inspector)
{
Parent::onInspect(inspector);
//Put the SubScene group before everything that'd be SubScene-effecting, for orginazational purposes
GuiInspectorGroup* subsceneGrp = inspector->findExistentGroup(StringTable->insert("SubScene"));
if (!subsceneGrp)
return;
GuiControl* stack = dynamic_cast<GuiControl*>(subsceneGrp->findObjectByInternalName(StringTable->insert("Stack")));
//Save button
GuiInspectorField* saveFieldGui = subsceneGrp->createInspectorField();
saveFieldGui->init(inspector, subsceneGrp);
saveFieldGui->setSpecialEditField(true);
saveFieldGui->setTargetObject(this);
StringTableEntry fldnm = StringTable->insert("SaveSubScene");
saveFieldGui->setSpecialEditVariableName(fldnm);
saveFieldGui->setInspectorField(NULL, fldnm);
saveFieldGui->setDocs("");
stack->addObject(saveFieldGui);
GuiButtonCtrl* saveButton = new GuiButtonCtrl();
saveButton->registerObject();
saveButton->setDataField(StringTable->insert("profile"), NULL, "ToolsGuiButtonProfile");
saveButton->setText("Save SubScene");
saveButton->resize(Point2I::Zero, saveFieldGui->getExtent());
saveButton->setHorizSizing(GuiControl::horizResizeWidth);
saveButton->setVertSizing(GuiControl::vertResizeHeight);
char szBuffer[512];
dSprintf(szBuffer, 512, "%d.save();", this->getId());
saveButton->setConsoleCommand(szBuffer);
saveFieldGui->addObject(saveButton);
}
void SubScene::inspectPostApply()
{
Parent::inspectPostApply();
setMaskBits(-1);
}
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());
}
return passes;
}
void SubScene::write(Stream& stream, U32 tabStop, U32 flags)
{
MutexHandle handle;
handle.lock(mMutex);
// export selected only?
if ((flags & SelectedOnly) && !isSelected())
{
for (U32 i = 0; i < size(); i++)
(*this)[i]->write(stream, tabStop, flags);
return;
}
stream.writeTabs(tabStop);
char buffer[2048];
const U32 bufferWriteLen = dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() && !(flags & NoName) ? getName() : "");
stream.write(bufferWriteLen, buffer);
writeFields(stream, tabStop + 1);
//The only meaningful difference between this and simSet for writing is we skip the children, since they're just the levelAsset contents
stream.writeTabs(tabStop);
stream.write(4, "};\r\n");
}
void SubScene::processTick(const Move* move)
{
}
void SubScene::_onFileChanged(const Torque::Path& path)
{
if(mLevelAsset.isNull() || Torque::Path(mLevelAsset->getLevelPath()) != path)
return;
AssertFatal(path == mLevelAsset->getLevelPath(), "Prefab::_onFileChanged - path does not match filename.");
_closeFile(false);
_loadFile(false);
setMaskBits(U32_MAX);
}
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<SimObject*>(*itr);
if (child)
child->safeDeleteObject();
}
if (removeFileNotify && mLevelAsset.notNull() && mLevelAsset->getLevelPath() != StringTable->EmptyString())
{
Torque::FS::RemoveChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
}
mGameModesList.clear();
}
void SubScene::_loadFile(bool addFileNotify)
{
AssertFatal(isServerObject(), "Trying to load a SubScene file on the client is bad!");
if(mLevelAsset.isNull() || mLevelAsset->getLevelPath() == StringTable->EmptyString())
return;
String evalCmd = String::ToString("exec(\"%s\");", mLevelAsset->getLevelPath());
String instantGroup = Con::getVariable("InstantGroup");
Con::setIntVariable("InstantGroup", this->getId());
Con::evaluate((const char*)evalCmd.c_str(), false, mLevelAsset->getLevelPath());
Con::setVariable("InstantGroup", instantGroup.c_str());
if (addFileNotify)
Torque::FS::AddChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged);
}
void SubScene::load()
{
mStartUnloadTimerMS = -1; //reset unload timers
//no need to load multiple times
if (mLoaded)
return;
_loadFile(true);
mLoaded = true;
GameMode::findGameModes(mGameModesNames, &mGameModesList);
for (U32 i = 0; i < mGameModesList.size(); i++)
{
mGameModesList[i]->onSubsceneLoaded_callback();
}
}
void SubScene::unload()
{
if (!mLoaded)
return;
if (isSelected())
{
mStartUnloadTimerMS = Sim::getCurrentTime();
return; //if a child is selected, then we don't want to 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)
{
SimGroup* childGrp = dynamic_cast<SimGroup*>(*itr);
if (childGrp && childGrp->isSelected())
{
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())
{
mStartUnloadTimerMS = Sim::getCurrentTime();
return; //if a child is selected, then we don't want to unload
}
}
}
_closeFile(true);
mLoaded = false;
for (U32 i = 0; i < mGameModesList.size(); i++)
{
mGameModesList[i]->onSubsceneUnloaded_callback();
}
}
bool SubScene::save()
{
//if there's nothing TO save, don't bother
if (size() == 0 || !isLoaded())
return false;
if (mLevelAsset.isNull())
return false;
//If we're flagged for unload, push back the unload timer so we can't accidentally trip be saving partway through an unload
if (mStartUnloadTimerMS != -1)
mStartUnloadTimerMS = Sim::getCurrentTime();
StringTableEntry levelPath = mLevelAsset->getLevelPath();
for (SimGroup::iterator itr = begin(); itr != end(); itr++)
{
//Inform our objects we're saving, so if they do any special stuff
//they can do it before the actual write-out
SimGroup* sg = dynamic_cast<SimGroup*>(*itr);
if (sg)
{
ConsoleValue vars[3];
vars[2].setString(mLevelAssetId);
sg->callOnChildren("onSaving", 3, vars);
}
SceneObject* scO = dynamic_cast<SceneObject*>(*itr);
if (scO)
{
scO->onSaving_callback(mLevelAssetId);
}
SimObject* sO = static_cast<SimObject*>(*itr);
if (!sO->save(levelPath))
{
Con::errorf("SubScene::save() - error, failed to write object %s to file: %s", sO->getIdString(), levelPath);
return false;
}
}
//process our gameModeList and write it out to the levelAsset for metadata stashing
bool saveSuccess = false;
//Get the level asset
if (mLevelAsset.isNull())
return saveSuccess;
//update the gamemode list as well
mLevelAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
//Finally, save
saveSuccess = mLevelAsset->saveAsset();
return saveSuccess;
}
void SubScene::_onSelected()
{
if (!isLoaded() && isServerObject())
load();
}
void SubScene::_onUnselected()
{
}
void SubScene::prepRenderImage(SceneRenderState* state)
{
// only render if selected or render flag is set
if (/*!smRenderTriggers && */!isSelected())
return;
ObjectRenderInst* ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
ri->renderDelegate.bind(this, &SubScene::renderObject);
ri->type = RenderPassManager::RIT_Editor;
ri->translucentSort = true;
ri->defaultKey = 1;
state->getRenderPass()->addInst(ri);
}
void SubScene::renderObject(ObjectRenderInst* ri,
SceneRenderState* state,
BaseMatInstance* overrideMat)
{
if (overrideMat)
return;
GFXStateBlockDesc desc;
desc.setZReadWrite(true, false);
desc.setBlend(true);
// Trigger polyhedrons are set up with outward facing normals and CCW ordering
// so can't enable backface culling.
desc.setCullMode(GFXCullNone);
GFXTransformSaver saver;
MatrixF mat = getRenderTransform();
GFX->multWorld(mat);
GFXDrawUtil* drawer = GFX->getDrawUtil();
//Box3F scale = getScale()
//Box3F bounds = Box3F(-m)
Point3F scale = getScale();
Box3F bounds = Box3F(-scale/2, scale/2);
ColorI boundsColor = ColorI(135, 206, 235, 50);
if (mGlobalLayer)
boundsColor = ColorI(200, 100, 100, 25);
else if (mLoaded)
boundsColor = ColorI(50, 200, 50, 50);
drawer->drawCube(desc, bounds, boundsColor);
// Render wireframe.
desc.setFillModeWireframe();
drawer->drawCube(desc, bounds, ColorI::BLACK);
}
DefineEngineMethod(SubScene, save, bool, (),,
"Save out the subScene.\n")
{
return object->save();
}
DefineEngineMethod(SubScene, load, void, (), ,
"Loads the SubScene's level file.\n")
{
object->load();
}
DefineEngineMethod(SubScene, unload, void, (), ,
"Unloads the SubScene's level file.\n")
{
object->unload();
}

View file

@ -0,0 +1,109 @@
#pragma once
#ifndef SUB_SCENE_H
#define SUB_SCENE_H
#ifndef SCENE_GROUP_H
#include "SceneGroup.h"
#endif
#ifndef LEVEL_ASSET_H
#include "assets/LevelAsset.h"
#endif
#ifndef GAME_MODE_H
#include "gameMode.h"
#endif
class SubScene : public SceneGroup
{
typedef SceneGroup Parent;
public:
enum MaskBits
{
NextFreeMask = Parent::NextFreeMask << 0
};
void onLevelChanged() {}
private:
DECLARE_LEVELASSET(SubScene, Level, onLevelChanged);
StringTableEntry mGameModesNames;
Vector<GameMode*> mGameModesList;
F32 mScopeDistance;
/// <summary>
/// How long we wait once every control object has left the SubScene's load boundary for us to unload the levelAsset.
/// </summary>
S32 mStartUnloadTimerMS;
bool mLoaded;
String mLoadIf;
bool mGlobalLayer;
public:
SubScene();
virtual ~SubScene();
DECLARE_CONOBJECT(SubScene);
DECLARE_CATEGORY("Object \t Collection");
static void initPersistFields();
static void consoleInit();
// SimObject
bool onAdd() override;
void onRemove() override;
U32 packUpdate(NetConnection* conn, U32 mask, BitStream* stream) override;
void unpackUpdate(NetConnection* conn, BitStream* stream) override;
void addObject(SimObject* object);
void removeObject(SimObject* object);
//void onEditorEnable() override;
//void onEditorDisable() override;
void inspectPostApply() override;
bool testBox(const Box3F& testBox);
void _onSelected() override;
void _onUnselected() override;
static S32 mUnloadTimeoutMs;
protected:
void write(Stream& stream, U32 tabStop, U32 flags = 0) override;
//
void _onFileChanged(const Torque::Path& path);
void _closeFile(bool removeFileNotify);
void _loadFile(bool addFileNotify);
//
public:
void processTick(const Move* move) override;
//
void onInspect(GuiInspector* inspector) override;
void load();
void unload();
void prepRenderImage(SceneRenderState* state) override;
void renderObject(ObjectRenderInst* ri,
SceneRenderState* state,
BaseMatInstance* overrideMat);
bool isLoaded() { return mLoaded; }
void setUnloadTimeMS(S32 unloadTimeMS) {
mStartUnloadTimerMS = unloadTimeMS;
}
inline S32 getUnloadTimsMS() {
return mStartUnloadTimerMS;
}
bool save();
DECLARE_ASSET_SETGET(SubScene, Level);
};
#endif

View file

@ -104,7 +104,7 @@ ConsoleSetType(TypeImageAssetId)
}
// Warn.
Con::warnf("(TypeAssetId) - Cannot set multiple args to a single asset.");
Con::warnf("(TypeImageAssetId) - Cannot set multiple args to a single asset.");
}
//-----------------------------------------------------------------------------
@ -748,7 +748,7 @@ void GuiInspectorTypeImageAssetPtr::setPreviewImage(StringTableEntry assetId)
IMPLEMENT_CONOBJECT(GuiInspectorTypeImageAssetId);
ConsoleDocClass(GuiInspectorTypeImageAssetId,
"@brief Inspector field type for Shapes\n\n"
"@brief Inspector field type for Images\n\n"
"Editor use only.\n\n"
"@internal"
);

View file

@ -42,12 +42,14 @@
// Debug Profiling.
#include "platform/profiler.h"
#include "gfx/gfxDrawUtil.h"
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(LevelAsset);
ConsoleType(LevelAssetPtr, TypeLevelAssetPtr, const char*, ASSET_ID_FIELD_PREFIX)
ConsoleType(LevelAssetPtr, TypeLevelAssetPtr, const char*, "")
//-----------------------------------------------------------------------------
@ -74,6 +76,28 @@ ConsoleSetType(TypeLevelAssetPtr)
Con::warnf("(TypeLevelAssetPtr) - Cannot set multiple args to a single asset.");
}
//-----------------------------------------------------------------------------
ConsoleType(assetIdString, TypeLevelAssetId, const char*, "")
ConsoleGetType(TypeLevelAssetId)
{
// Fetch asset Id.
return *((const char**)(dptr));
}
ConsoleSetType(TypeLevelAssetId)
{
// Was a single argument specified?
if (argc == 1)
{
*((const char**)dptr) = StringTable->insert(argv[0]);
return;
}
// Warn.
Con::warnf("(TypeLevelAssetId) - Cannot set multiple args to a single asset.");
}
//-----------------------------------------------------------------------------
LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
@ -91,7 +115,7 @@ LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false)
mForestPath = StringTable->EmptyString();
mNavmeshPath = StringTable->EmptyString();
mGamemodeName = StringTable->EmptyString();
mGameModesNames = StringTable->EmptyString();
mMainLevelAsset = StringTable->EmptyString();
mEditorFile = StringTable->EmptyString();
@ -134,7 +158,7 @@ void LevelAsset::initPersistFields()
&setBakedSceneFile, &getBakedSceneFile, "Path to the level file with the objects generated as part of the baking process");
addField("isSubScene", TypeBool, Offset(mIsSubLevel, LevelAsset), "Is this a sublevel to another Scene");
addField("gameModeName", TypeString, Offset(mGamemodeName, LevelAsset), "Name of the Game Mode to be used with this level");
addField("gameModesNames", TypeString, Offset(mGameModesNames, LevelAsset), "Name of the Game Mode to be used with this level");
}
//------------------------------------------------------------------------------
@ -357,7 +381,7 @@ void LevelAsset::unloadDependencies()
}
}
DefineEngineMethod(LevelAsset, getLevelPath, const char*, (),,
DefineEngineMethod(LevelAsset, getLevelPath, const char*, (), ,
"Gets the full path of the asset's defined level file.\n"
"@return The string result of the level path")
{
@ -417,3 +441,99 @@ DefineEngineMethod(LevelAsset, unloadDependencies, void, (), ,
{
return object->unloadDependencies();
}
//-----------------------------------------------------------------------------
// GuiInspectorTypeAssetId
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiInspectorTypeLevelAssetPtr);
ConsoleDocClass(GuiInspectorTypeLevelAssetPtr,
"@brief Inspector field type for Shapes\n\n"
"Editor use only.\n\n"
"@internal"
);
void GuiInspectorTypeLevelAssetPtr::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeLevelAssetPtr)->setInspectorFieldType("GuiInspectorTypeLevelAssetPtr");
}
GuiControl* GuiInspectorTypeLevelAssetPtr::constructEditControl()
{
// Create base filename edit controls
GuiControl* retCtrl = Parent::constructEditControl();
if (retCtrl == NULL)
return retCtrl;
// Change filespec
char szBuffer[512];
dSprintf(szBuffer, sizeof(szBuffer), "AssetBrowser.showDialog(\"LevelAsset\", \"AssetBrowser.changeAsset\", %s, \"\");",
getIdString());
mBrowseButton->setField("Command", szBuffer);
setDataField(StringTable->insert("targetObject"), NULL, mInspector->getInspectObject()->getIdString());
// Create "Open in Editor" button
mEditButton = new GuiBitmapButtonCtrl();
dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");",
getIdString());
mEditButton->setField("Command", szBuffer);
char bitmapName[512] = "ToolsModule:iconAdd_image";
mEditButton->setBitmap(StringTable->insert(bitmapName));
mEditButton->setDataField(StringTable->insert("Profile"), NULL, "GuiButtonProfile");
mEditButton->setDataField(StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile");
mEditButton->setDataField(StringTable->insert("hovertime"), NULL, "1000");
mEditButton->setDataField(StringTable->insert("tooltip"), NULL, "Test play this sound");
mEditButton->registerObject();
addObject(mEditButton);
return retCtrl;
}
bool GuiInspectorTypeLevelAssetPtr::updateRects()
{
S32 dividerPos, dividerMargin;
mInspector->getDivider(dividerPos, dividerMargin);
Point2I fieldExtent = getExtent();
Point2I fieldPos = getPosition();
mCaptionRect.set(0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y);
mEditCtrlRect.set(fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 34, fieldExtent.y);
bool resized = mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent);
if (mBrowseButton != NULL)
{
mBrowseRect.set(fieldExtent.x - 32, 2, 14, fieldExtent.y - 4);
resized |= mBrowseButton->resize(mBrowseRect.point, mBrowseRect.extent);
}
if (mEditButton != NULL)
{
RectI shapeEdRect(fieldExtent.x - 16, 2, 14, fieldExtent.y - 4);
resized |= mEditButton->resize(shapeEdRect.point, shapeEdRect.extent);
}
return resized;
}
IMPLEMENT_CONOBJECT(GuiInspectorTypeLevelAssetId);
ConsoleDocClass(GuiInspectorTypeLevelAssetId,
"@brief Inspector field type for Levels\n\n"
"Editor use only.\n\n"
"@internal"
);
void GuiInspectorTypeLevelAssetId::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeLevelAssetId)->setInspectorFieldType("GuiInspectorTypeLevelAssetId");
}

View file

@ -40,6 +40,11 @@
#endif
#include "T3D/assets/ImageAsset.h"
#ifndef _GUI_INSPECTOR_TYPES_H_
#include "gui/editor/guiInspectorTypes.h"
#endif
#include <gui/controls/guiBitmapCtrl.h>
//-----------------------------------------------------------------------------
class LevelAsset : public AssetBase
{
@ -64,7 +69,7 @@ class LevelAsset : public AssetBase
bool mIsSubLevel;
StringTableEntry mMainLevelAsset;
StringTableEntry mGamemodeName;
StringTableEntry mGameModesNames;
Vector<AssetBase*> mAssetDependencies;
@ -114,7 +119,7 @@ public:
U32 load() override { return Ok; };
protected:
static bool setLevelFile(void *obj, const char *index, const char *data) { static_cast<LevelAsset*>(obj)->setLevelFile(data); return false; }
static bool setLevelFile(void* obj, const char* index, const char* data) { static_cast<LevelAsset*>(obj)->setLevelFile(data); return false; }
static const char* getLevelFile(void* obj, const char* data) { return static_cast<LevelAsset*>(obj)->getLevelFile(); }
static bool setEditorFile(void* obj, const char* index, const char* data) { static_cast<LevelAsset*>(obj)->setEditorFile(data); return false; }
@ -135,10 +140,96 @@ protected:
void initializeAsset(void) override;
void onAssetRefresh(void) override;
void loadAsset();
void loadAsset();
typedef Signal<void()> LevelAssetChanged;
LevelAssetChanged mChangeSignal;
public:
LevelAssetChanged& getChangedSignal() { return mChangeSignal; }
};
#ifdef TORQUE_TOOLS
class GuiInspectorTypeLevelAssetPtr : public GuiInspectorTypeFileName
{
typedef GuiInspectorTypeFileName Parent;
public:
GuiBitmapButtonCtrl* mEditButton;
DECLARE_CONOBJECT(GuiInspectorTypeLevelAssetPtr);
static void consoleInit();
GuiControl* constructEditControl() override;
bool updateRects() override;
};
class GuiInspectorTypeLevelAssetId : public GuiInspectorTypeLevelAssetPtr
{
typedef GuiInspectorTypeLevelAssetPtr Parent;
public:
DECLARE_CONOBJECT(GuiInspectorTypeLevelAssetId);
static void consoleInit();
};
#endif
DefineConsoleType(TypeLevelAssetPtr, LevelAsset)
DefineConsoleType(TypeLevelAssetId, String)
#pragma region Singular Asset Macros
//Singular assets
/// <Summary>
/// Declares an level asset
/// This establishes the assetId, asset and legacy filepath fields, along with supplemental getter and setter functions
/// </Summary>
#define DECLARE_LEVELASSET(className, name, changeFunc) public: \
StringTableEntry m##name##AssetId;\
AssetPtr<LevelAsset> m##name##Asset;\
public: \
const AssetPtr<LevelAsset> & get##name##Asset() const { return m##name##Asset; }\
void set##name##Asset(const AssetPtr<LevelAsset> &_in) { m##name##Asset = _in;}\
\
bool _set##name(StringTableEntry _in)\
{\
if(m##name##AssetId != _in)\
{\
if (m##name##Asset.notNull())\
{\
m##name##Asset->getChangedSignal().remove(this, &className::changeFunc);\
}\
if (_in == NULL || _in == StringTable->EmptyString())\
{\
m##name##AssetId = StringTable->EmptyString();\
m##name##Asset = NULL;\
return true;\
}\
if (AssetDatabase.isDeclaredAsset(_in))\
{\
m##name##AssetId = _in;\
m##name##Asset = _in;\
return true;\
}\
}\
\
if(get##name() == StringTable->EmptyString())\
return true;\
\
return false;\
}\
\
const StringTableEntry get##name() const\
{\
return m##name##AssetId;\
}\
bool name##Valid() {return (get##name() != StringTable->EmptyString() && m##name##Asset->getStatus() == AssetBase::Ok); }
#define INITPERSISTFIELD_LEVELASSET(name, consoleClass, docs) \
addProtectedField(assetText(name, Asset), TypeLevelAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.));
#pragma endregion
#endif // _ASSET_BASE_H_

View file

@ -0,0 +1,460 @@
#include "gameMode.h"
#ifdef TORQUE_TOOLS
#include "gui/containers/guiDynamicCtrlArrayCtrl.h"
#endif
#include "console/arrayObject.h"
IMPLEMENT_CONOBJECT(GameMode);
IMPLEMENT_CALLBACK(GameMode, onActivated, void, (), (),
"@brief Called when a gamemode is activated.\n\n");
IMPLEMENT_CALLBACK(GameMode, onDeactivated, void, (), (),
"@brief Called when a gamemode is deactivated.\n\n");
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, (), (),
"@brief Called when a subScene has been loaded and has game mode implications.\n\n");
IMPLEMENT_CALLBACK(GameMode, onSubsceneUnloaded, void, (), (),
"@brief Called when a subScene has been unloaded and has game mode implications.\n\n");
ConsoleType(GameModeList, TypeGameModeList, const char*, "")
ConsoleGetType(TypeGameModeList)
{
// Fetch asset Id.
return *((const char**)(dptr));
}
//-----------------------------------------------------------------------------
ConsoleSetType(TypeGameModeList)
{
// Was a single argument specified?
if (argc == 1)
{
// Yes, so fetch field value.
*((const char**)dptr) = StringTable->insert(argv[0]);
return;
}
// Warn.
//Con::warnf("(TypeGameModeList) - Cannot set multiple args to a single asset.");
}
GameMode::GameMode() :
mGameModeName(StringTable->EmptyString()),
mGameModeDesc(StringTable->EmptyString()),
mIsActive(false),
mIsAlwaysActive(false)
{
INIT_ASSET(PreviewImage);
}
void GameMode::initPersistFields()
{
Parent::initPersistFields();
addField("gameModeName", TypeString, Offset(mGameModeName, GameMode), "Human-readable name of the gamemode");
addField("description", TypeString, Offset(mGameModeDesc, GameMode), "Description of the gamemode");
INITPERSISTFIELD_IMAGEASSET(PreviewImage, GameMode, "Preview Image");
addField("active", TypeBool, Offset(mIsActive, GameMode), "Is the gamemode active");
addField("alwaysActive", TypeBool, Offset(mIsAlwaysActive, GameMode), "Is the gamemode always active");
}
bool GameMode::onAdd()
{
if (!Parent::onAdd())
return false;
return true;
}
void GameMode::onRemove()
{
Parent::onRemove();
}
void GameMode::findGameModes(const char* gameModeList, Vector<GameMode*> *outGameModes)
{
if (outGameModes == nullptr)
return;
Vector<String> gameModeNames;
U32 uCount = StringUnit::getUnitCount(gameModeList, ";");
for (U32 i = 0; i < uCount; i++)
{
String name = StringUnit::getUnit(gameModeList, i, ";");
if (!name.isEmpty())
gameModeNames.push_back(name);
}
for (U32 i = 0; i < gameModeNames.size(); i++)
{
GameMode* gm;
if (Sim::findObject(gameModeNames[i].c_str(), gm))
{
outGameModes->push_back(gm);
}
}
}
void GameMode::setActive(const bool& active)
{
mIsActive = active;
if (mIsActive)
onActivated_callback();
else
onDeactivated_callback();
}
void GameMode::setAlwaysActive(const bool& alwaysActive)
{
mIsAlwaysActive = alwaysActive;
}
DefineEngineMethod(GameMode, isActive, bool, (), ,
"Returns if the GameMode is currently active.\n"
"@return The active status of the GameMode")
{
return object->isActive();
}
DefineEngineMethod(GameMode, setActive, void, (bool active), (true),
"Sets the active state of the GameMode.\n"
"@param active A bool of the state the GameMode should be set to")
{
object->setActive(active);
}
DefineEngineMethod(GameMode, isALwaysActive, bool, (), ,
"Returns if the GameMode is currently active.\n"
"@return The active status of the GameMode")
{
return object->isActive();
}
DefineEngineMethod(GameMode, setAlwaysActive, void, (bool alwaysActive), (true),
"Sets the active state of the GameMode.\n"
"@param active A bool of the state the GameMode should be set to")
{
object->setAlwaysActive(alwaysActive);
}
DefineEngineFunction(getGameModesList, ArrayObject*, (), , "")
{
ArrayObject* dictionary = new ArrayObject();
dictionary->registerObject();
char activeValBuffer[16];
for (SimGroup::iterator itr = Sim::getRootGroup()->begin(); itr != Sim::getRootGroup()->end(); itr++)
{
GameMode* gm = dynamic_cast<GameMode*>(*itr);
if (gm)
{
dSprintf(activeValBuffer, 16, "%d", (gm->mIsActive || gm->mIsAlwaysActive));
dictionary->push_back(gm->getName(), activeValBuffer);
}
}
return dictionary;
}
//-----------------------------------------------------------------------------
// GuiInspectorTypeAssetId
//-----------------------------------------------------------------------------
#ifdef TORQUE_TOOLS
GuiInspectorTypeGameModeList::GuiInspectorTypeGameModeList()
: mHelper(NULL),
mRollout(NULL),
mArrayCtrl(NULL)
{
}
IMPLEMENT_CONOBJECT(GuiInspectorTypeGameModeList);
ConsoleDocClass(GuiInspectorTypeGameModeList,
"@brief Inspector field type for selecting GameModes\n\n"
"Editor use only.\n\n"
"@internal"
);
bool GuiInspectorTypeGameModeList::onAdd()
{
// Skip our parent because we aren't using mEditCtrl
// and according to our parent that would be cause to fail onAdd.
if (!Parent::Parent::onAdd())
return false;
if (!mInspector)
return false;
//build out our list of gamemodes
Vector<GameMode*> gameModesList;
for (SimGroup::iterator itr = Sim::getRootGroup()->begin(); itr != Sim::getRootGroup()->end(); itr++)
{
GameMode* gm = dynamic_cast<GameMode*>(*itr);
if (gm)
gameModesList.push_back(gm);
}
static StringTableEntry sProfile = StringTable->insert("profile");
setDataField(sProfile, NULL, "GuiInspectorFieldProfile");
setBounds(0, 0, 100, 18);
// Allocate our children controls...
mRollout = new GuiRolloutCtrl();
mRollout->setMargin(14, 0, 0, 0);
mRollout->setCanCollapse(false);
mRollout->registerObject();
addObject(mRollout);
mArrayCtrl = new GuiDynamicCtrlArrayControl();
mArrayCtrl->setDataField(sProfile, NULL, "GuiInspectorBitMaskArrayProfile");
mArrayCtrl->setField("autoCellSize", "true");
mArrayCtrl->setField("fillRowFirst", "true");
mArrayCtrl->setField("dynamicSize", "true");
mArrayCtrl->setField("rowSpacing", "4");
mArrayCtrl->setField("colSpacing", "1");
mArrayCtrl->setField("frozen", "true");
mArrayCtrl->registerObject();
mRollout->addObject(mArrayCtrl);
GuiCheckBoxCtrl* pCheckBox = NULL;
for (S32 i = 0; i < gameModesList.size(); i++)
{
pCheckBox = new GuiCheckBoxCtrl();
pCheckBox->setText(gameModesList[i]->getName());
pCheckBox->registerObject();
mArrayCtrl->addObject(pCheckBox);
pCheckBox->autoSize();
// Override the normal script callbacks for GuiInspectorTypeCheckBox
char szBuffer[512];
dSprintf(szBuffer, 512, "%d.applyValue();", getId());
pCheckBox->setField("Command", szBuffer);
}
mArrayCtrl->setField("frozen", "false");
mArrayCtrl->refresh();
mHelper = new GuiInspectorTypeGameModeListHelper();
mHelper->init(mInspector, mParent);
mHelper->mParentRollout = mRollout;
mHelper->mParentField = this;
mHelper->setInspectorField(mField, mCaption, mFieldArrayIndex);
mHelper->registerObject();
mHelper->setExtent(pCheckBox->getExtent());
mHelper->setPosition(0, 0);
mRollout->addObject(mHelper);
mRollout->sizeToContents();
mRollout->instantCollapse();
updateValue();
return true;
}
void GuiInspectorTypeGameModeList::consoleInit()
{
Parent::consoleInit();
ConsoleBaseType::getType(TypeGameModeList)->setInspectorFieldType("GuiInspectorTypeGameModeList");
}
void GuiInspectorTypeGameModeList::childResized(GuiControl* child)
{
setExtent(mRollout->getExtent());
}
bool GuiInspectorTypeGameModeList::resize(const Point2I& newPosition, const Point2I& newExtent)
{
if (!Parent::resize(newPosition, newExtent))
return false;
// Hack... height of 18 is hardcoded
return mHelper->resize(Point2I(0, 0), Point2I(newExtent.x, 18));
}
bool GuiInspectorTypeGameModeList::updateRects()
{
if (!mRollout)
return false;
bool result = mRollout->setExtent(getExtent());
for (U32 i = 0; i < mArrayCtrl->size(); i++)
{
GuiInspectorField* pField = dynamic_cast<GuiInspectorField*>(mArrayCtrl->at(i));
if (pField)
if (pField->updateRects())
result = true;
}
if (mHelper && mHelper->updateRects())
result = true;
return result;
}
StringTableEntry GuiInspectorTypeGameModeList::getValue()
{
if (!mRollout)
return StringTable->insert("");
String results = "";
for (U32 i = 0; i < mArrayCtrl->size(); i++)
{
GuiCheckBoxCtrl* pCheckBox = dynamic_cast<GuiCheckBoxCtrl*>(mArrayCtrl->at(i));
if (pCheckBox->getStateOn())
results += pCheckBox->getText() + String(";");
}
if (!results.isEmpty())
return StringTable->insert(results.c_str());
else
return StringTable->EmptyString();
}
void GuiInspectorTypeGameModeList::setValue(StringTableEntry value)
{
Vector<String> gameModeNames;
U32 uCount = StringUnit::getUnitCount(value, ";");
for (U32 i = 0; i < uCount; i++)
{
String name = StringUnit::getUnit(value, i, ";");
if (!name.isEmpty())
gameModeNames.push_back(name);
}
for (U32 i = 0; i < mArrayCtrl->size(); i++)
{
GuiCheckBoxCtrl* pCheckBox = dynamic_cast<GuiCheckBoxCtrl*>(mArrayCtrl->at(i));
for (U32 m = 0; m < gameModeNames.size(); m++)
{
if (gameModeNames[m].equal(pCheckBox->getText()))
{
pCheckBox->setStateOn(true);
}
}
}
mHelper->setValue(value);
}
void GuiInspectorTypeGameModeList::updateData()
{
StringTableEntry data = getValue();
setData(data);
}
DefineEngineMethod(GuiInspectorTypeGameModeList, applyValue, void, (), , "")
{
object->updateData();
}
GuiInspectorTypeGameModeListHelper::GuiInspectorTypeGameModeListHelper()
: mButton(NULL),
mParentRollout(NULL),
mParentField(NULL)
{
}
IMPLEMENT_CONOBJECT(GuiInspectorTypeGameModeListHelper);
ConsoleDocClass(GuiInspectorTypeGameModeListHelper,
"@brief Inspector field type support for GameModes lists.\n\n"
"Editor use only.\n\n"
"@internal"
);
GuiControl* GuiInspectorTypeGameModeListHelper::constructEditControl()
{
GuiControl* retCtrl = new GuiTextEditCtrl();
retCtrl->setDataField(StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile");
retCtrl->setField("hexDisplay", "true");
_registerEditControl(retCtrl);
char szBuffer[512];
dSprintf(szBuffer, 512, "%d.apply(%d.getText());", mParentField->getId(), retCtrl->getId());
retCtrl->setField("AltCommand", szBuffer);
retCtrl->setField("Validate", szBuffer);
mButton = new GuiBitmapButtonCtrl();
RectI browseRect(Point2I((getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4));
dSprintf(szBuffer, 512, "%d.toggleExpanded(false);", mParentRollout->getId());
mButton->setField("Command", szBuffer);
mButton->setField("buttonType", "ToggleButton");
mButton->setDataField(StringTable->insert("Profile"), NULL, "GuiInspectorButtonProfile");
mButton->setBitmap(StringTable->insert("ToolsModule:arrowBtn_N_image"));
mButton->setStateOn(true);
mButton->setExtent(16, 16);
mButton->registerObject();
addObject(mButton);
mButton->resize(browseRect.point, browseRect.extent);
return retCtrl;
}
bool GuiInspectorTypeGameModeListHelper::resize(const Point2I& newPosition, const Point2I& newExtent)
{
if (!Parent::resize(newPosition, newExtent))
return false;
if (mEdit != NULL)
{
return updateRects();
}
return false;
}
bool GuiInspectorTypeGameModeListHelper::updateRects()
{
S32 dividerPos, dividerMargin;
mInspector->getDivider(dividerPos, dividerMargin);
Point2I fieldExtent = getExtent();
Point2I fieldPos = getPosition();
mCaptionRect.set(0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y);
mEditCtrlRect.set(fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 32, fieldExtent.y);
bool editResize = mEdit->resize(mEditCtrlRect.point, mEditCtrlRect.extent);
bool buttonResize = false;
if (mButton != NULL)
{
mButtonRect.set(fieldExtent.x - 26, 2, 16, 16);
buttonResize = mButton->resize(mButtonRect.point, mButtonRect.extent);
}
return (editResize || buttonResize);
}
void GuiInspectorTypeGameModeListHelper::setValue(StringTableEntry newValue)
{
GuiTextEditCtrl* edit = dynamic_cast<GuiTextEditCtrl*>(mEdit);
edit->setText(newValue);
}
#endif

View file

@ -0,0 +1,114 @@
#pragma once
#ifndef GAME_MODE_H
#define GAME_MODE_H
#ifdef TORQUE_TOOLS
#ifndef _GUI_INSPECTOR_TYPES_H_
#include "gui/editor/guiInspectorTypes.h"
#endif
#endif
#include "T3D/assets/ImageAsset.h"
class GameMode : public SimObject
{
typedef SimObject Parent;
private:
StringTableEntry mGameModeName;
StringTableEntry mGameModeDesc;
DECLARE_IMAGEASSET(GameMode, PreviewImage, previewChange, GFXStaticTextureSRGBProfile);
DECLARE_ASSET_SETGET(GameMode, PreviewImage);
bool mIsActive;
bool mIsAlwaysActive;
public:
GameMode();
~GameMode() = default;
static void initPersistFields();
bool onAdd() override;
void onRemove() override;
bool isActive() { return mIsActive; }
void setActive(const bool& active);
bool isAlwaysActive() { return mIsAlwaysActive; }
void setAlwaysActive(const bool& alwaysActive);
DECLARE_CONOBJECT(GameMode);
static void findGameModes(const char* gameModeList, Vector<GameMode*>* outGameModes);
void previewChange() {}
DECLARE_CALLBACK(void, onActivated, ());
DECLARE_CALLBACK(void, onDeactivated, ());
DECLARE_CALLBACK(void, onSceneLoaded, ());
DECLARE_CALLBACK(void, onSceneUnloaded, ());
DECLARE_CALLBACK(void, onSubsceneLoaded, ());
DECLARE_CALLBACK(void, onSubsceneUnloaded, ());
};
DefineConsoleType(TypeGameModeList, String)
#ifdef TORQUE_TOOLS
class GuiInspectorTypeGameModeListHelper;
class GuiInspectorTypeGameModeList : public GuiInspectorField
{
typedef GuiInspectorField Parent;
public:
GuiInspectorTypeGameModeListHelper* mHelper;
GuiRolloutCtrl* mRollout;
GuiDynamicCtrlArrayControl* mArrayCtrl;
Vector<GuiInspectorField*> mChildren;
GuiBitmapButtonCtrl* mButton;
RectI mButtonRect;
DECLARE_CONOBJECT(GuiInspectorTypeGameModeList);
GuiInspectorTypeGameModeList();
// ConsoleObject
bool onAdd() override;
static void consoleInit();
// GuiInspectorField
bool resize(const Point2I& newPosition, const Point2I& newExtent) override;
void childResized(GuiControl* child) override;
bool updateRects() override;
void updateData() override;
StringTableEntry getValue() override;
void setValue(StringTableEntry value) override;
};
class GuiInspectorTypeGameModeListHelper : public GuiInspectorField
{
typedef GuiInspectorField Parent;
public:
GuiInspectorTypeGameModeListHelper();
DECLARE_CONOBJECT(GuiInspectorTypeGameModeListHelper);
GuiBitmapButtonCtrl* mButton;
GuiRolloutCtrl* mParentRollout;
GuiInspectorTypeGameModeList* mParentField;
RectI mButtonRect;
//-----------------------------------------------------------------------------
// Override able methods for custom edit fields
//-----------------------------------------------------------------------------
GuiControl* constructEditControl() override;
bool resize(const Point2I& newPosition, const Point2I& newExtent) override;
bool updateRects() override;
void setValue(StringTableEntry value) override;
};
#endif
#endif

View file

@ -372,6 +372,13 @@ class GuiControl : public SimGroup
inline const S32 getHorizSizing() const { return mHorizSizing; }
inline const S32 getVertSizing() const { return mVertSizing; }
void setHorizSizing(horizSizingOptions horizSizing) {
mHorizSizing = horizSizing;
}
void setVertSizing(vertSizingOptions vertSizing) {
mVertSizing = vertSizing;
}
/// @}

View file

@ -2013,3 +2013,8 @@ void SceneObject::onNewParent(SceneObject *newParent) { if (isServerObject()) on
void SceneObject::onLostParent(SceneObject *oldParent) { if (isServerObject()) onLostParent_callback(oldParent); }
void SceneObject::onNewChild(SceneObject *newKid) { if (isServerObject()) onNewChild_callback(newKid); }
void SceneObject::onLostChild(SceneObject *lostKid) { if (isServerObject()) onLostChild_callback(lostKid); }
IMPLEMENT_CALLBACK(SceneObject, onSaving, void, (const char* fileName), (fileName),
"@brief Called when a saving is occuring to allow objects to special-handle prepwork for saving if required.\n\n"
"@param fileName The level file being saved\n");

View file

@ -913,6 +913,8 @@ class SceneObject : public NetObject, public ProcessObject
DECLARE_CALLBACK(void, onLostChild, (SceneObject *subObject));
// PATHSHAPE END
DECLARE_CALLBACK(void, onSaving, (const char* fileName));
virtual void getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList) {}
};