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));

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;
@ -96,6 +102,8 @@ public:
}
static Vector<Scene*> smSceneList;
DECLARE_CALLBACK(void, onSaving, (const char* fileName));
};
@ -136,3 +144,4 @@ Vector<T*> Scene::getObjectsByClass(bool checkSubscenes)
return foundObjects;
}
#endif

View file

@ -0,0 +1,350 @@
#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)
{
//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,427 @@
#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("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 isOverlapped = getWorldBox().isOverlapped(testBox);
return getWorldBox().isOverlapped(testBox);
}
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++)
{
//Just in case there's a valid callback the scene object would like to invoke for saving
SceneObject* gc = dynamic_cast<SceneObject*>(*itr);
if (gc)
{
gc->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();
}
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,108 @@
#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;
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,436 @@
#include "gameMode.h"
#ifdef TORQUE_TOOLS
#include "gui/containers/guiDynamicCtrlArrayCtrl.h"
#endif
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())
{
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");
}
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();
}
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);
}
DefineEngineFunction(getGameModesList, const char*, (), , "")
{
char* returnBuffer = Con::getReturnBuffer(1024);
String formattedList;
for (SimGroup::iterator itr = Sim::getRootGroup()->begin(); itr != Sim::getRootGroup()->end(); itr++)
{
GameMode* gm = dynamic_cast<GameMode*>(*itr);
if (gm)
{
formattedList += String(gm->getName()) + ";";
}
}
dSprintf(returnBuffer, 1024, "%s", formattedList.c_str());
return returnBuffer;
}
//-----------------------------------------------------------------------------
// 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,110 @@
#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;
public:
GameMode();
~GameMode() = default;
static void initPersistFields();
bool onAdd() override;
void onRemove() override;
bool isActive() { return mIsActive; }
void setActive(const bool& active);
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) {}
};

View file

@ -325,6 +325,8 @@ function replaceInFile(%fileName, %fromWord, %toWord)
//Go through our scriptfile and replace the old namespace with the new
%editedFileContents = "";
%lineArray = new ArrayObject(){};
%file = new FileObject();
if ( %file.openForRead( %fileName ) )
{
@ -334,6 +336,8 @@ function replaceInFile(%fileName, %fromWord, %toWord)
%line = trim( %line );
%editedFileContents = %editedFileContents @ strreplace(%line, %fromWord, %toWord) @ "\n";
%lineArray.add(strreplace(%line, %fromWord, %toWord));
}
%file.close();
@ -341,12 +345,18 @@ function replaceInFile(%fileName, %fromWord, %toWord)
if(%editedFileContents !$= "")
{
%file.openForWrite(%fileName);
%file.writeline(%editedFileContents);
%file.close();
if( %file.openForWrite(%fileName) )
{
for(%i=0; %i < %lineArray.getCount(); %i++)
{
%file.writeline(%lineArray.getKey(%i));
}
%file.close();
}
}
%lineArray.delete();
}
//------------------------------------------------------------------------------

View file

@ -15,19 +15,6 @@ function ExampleModule::initServer(%this)
//This is called when the server is created for an actual game/map to be played
function ExampleModule::onCreateGameServer(%this)
{
//These are common managed data files. For any datablock-based stuff that gets generated by the editors
//(that doesn't have a specific associated file, like data for a player class) will go into these.
//So we'll register them now if they exist.
if(isFile("./scripts/managedData/managedDatablocks." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedDatablocks");
if(isFile("./scripts/managedData/managedForestItemData." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedForestItemData");
if(isFile("./scripts/managedData/managedForestBrushData." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedForestBrushData");
if(isFile("./scripts/managedData/managedParticleData." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedParticleData");
if(isFile("./scripts/managedData/managedParticleEmitterData." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedParticleEmitterData");
}
//This is called when the server is shut down due to the game/map being exited
@ -46,6 +33,8 @@ function ExampleModule::initClient(%this)
%prefPath = getPrefpath();
if(isScriptFile(%prefPath @ "/keybinds"))
exec(%prefPath @ "/keybinds");
%this.queueExec("./scripts/server/ExampleGameMode");
}
//This is called when a client connects to a server

View file

@ -8,4 +8,5 @@
ForestFile="@assetFile=ExampleLevel.forest"
NavmeshFile="@assetFile=ExampleLevel.nav"
staticObjectAssetDependency0="@asset=Prototyping:FloorGray"
gameModesNames="ExampleGameMode;"
VersionId="1"/>

View file

@ -1,11 +1,5 @@
function ExampleGameMode::onCreateGame()
{
// Note: The Game object will be cleaned up by MissionCleanup. Therefore its lifetime is
// limited to that of the mission.
new ScriptObject(ExampleGameMode){};
return ExampleGameMode;
}
if(!isObject(ExampleGameMode))
new GameMode(ExampleGameMode){};
//-----------------------------------------------------------------------------
// The server has started up so do some game start up
@ -159,6 +153,30 @@ function ExampleGameMode::onInitialControlSet(%this)
}
function ExampleGameMode::onSceneLoaded(%this)
{
echo("===================================");
echo("ExampleGameMode - Scene is loaded");
}
function ExampleGameMode::onSceneUnloaded(%this)
{
echo("===================================");
echo("ExampleGameMode - Scene is unloaded");
}
function ExampleGameMode::onSubsceneLoaded(%this)
{
echo("===================================");
echo("ExampleGameMode - Subscene is loaded");
}
function ExampleGameMode::onSubsceneUnloaded(%this)
{
echo("===================================");
echo("ExampleGameMode - Subscene is unloaded");
}
function ExampleGameMode::spawnCamera(%this, %client, %spawnPoint)
{
// Set the control object to the default camera

View file

@ -99,4 +99,9 @@ function UI::onDestroyClientConnection(%this)
function UI::registerGameMenus(%this, %menusArrayObj)
{
%menusArrayObj.add("System", SystemMenu);
}
function listLevelsAndGameModes()
{
}

View file

@ -9,6 +9,7 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
isContainer = "1";
canSaveDynamicFields = "1";
currentMenuIdx = "0";
launchInEditor = "0";
previewButtonSize = "445 120";
@ -39,31 +40,42 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
new GuiStackControl(ChooseLevelMenuTabList) {
stackingType = "Horizontal";
padding = "10";
position = "485 61";
extent = "310 41";
position = "405 61";
extent = "470 41";
horizSizing = "center";
profile = "GuiDefaultProfile";
visible = "0";
tooltipProfile = "GuiToolTipProfile";
hidden = "1";
new GuiButtonCtrl() {
text = "Level";
text = "Game Mode";
groupNum = "1";
extent = "150 41";
profile = "GuiMenuButtonProfile";
command = "ChooseLevelMenu.openMenu(0);";
tooltipProfile = "GuiToolTipProfile";
internalName = "GameModeBtn";
class = "ChooseLevelMenuButton";
};
new GuiButtonCtrl() {
text = "Server Config";
text = "Level";
groupNum = "1";
position = "160 0";
extent = "150 41";
profile = "GuiMenuButtonProfile";
command = "ChooseLevelMenu.openMenu(1);";
tooltipProfile = "GuiToolTipProfile";
internalName = "LevelBtn";
class = "ChooseLevelMenuButton";
};
new GuiButtonCtrl() {
text = "Server Config";
groupNum = "1";
position = "320 0";
extent = "150 41";
profile = "GuiMenuButtonProfile";
command = "ChooseLevelMenu.openMenu(2);";
tooltipProfile = "GuiToolTipProfile";
internalName = "ConfigBtn";
class = "ChooseLevelMenuButton";
};
};
@ -72,14 +84,12 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
extent = "1281 60";
horizSizing = "width";
profile = "GuiNonModalDefaultProfile";
visible = "0";
tooltipProfile = "GuiToolTipProfile";
isContainer = "1";
hidden = "1";
new GuiBitmapCtrl(ChooseLevelMenuPrevNavIcon) {
BitmapAsset = "UI:Keyboard_Black_Q_image";
position = "485 24";
position = "405 24";
extent = "40 40";
vertSizing = "top";
profile = "GuiNonModalDefaultProfile";
@ -87,19 +97,72 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
};
new GuiBitmapCtrl(ChooseLevelMenuNextNavIcon) {
BitmapAsset = "UI:Keyboard_Black_E_image";
position = "595 24";
position = "515 24";
extent = "40 40";
vertSizing = "top";
profile = "GuiNonModalDefaultProfile";
tooltipProfile = "GuiToolTipProfile";
};
};
new GuiContainer(GameModeSelectContainer) {
position = "196 119";
extent = "888 566";
horizSizing = "center";
profile = "GuiNonModalDefaultProfile";
tooltipProfile = "GuiToolTipProfile";
new GuiScrollCtrl(GameModePreviewScroll) {
hScrollBar = "alwaysOff";
vScrollBar = "dynamic";
extent = "445 562";
vertSizing = "height";
profile = "GuiMenuScrollProfile";
tooltipProfile = "GuiToolTipProfile";
new GuiStackControl(GameModePreviewArray) {
padding = "5";
position = "0 1";
extent = "445 120";
horizSizing = "width";
vertSizing = "height";
profile = "GuiMenuDefaultProfile";
tooltipProfile = "GuiToolTipProfile";
};
};
new GuiBitmapCtrl(GameModePreviewBitmap) {
BitmapAsset = "UI:no_preview_image";
position = "448 0";
extent = "440 440";
horizSizing = "left";
profile = "GuiNonModalDefaultProfile";
tooltipProfile = "GuiToolTipProfile";
};
new GuiTextCtrl(GameModeNameText) {
text = "Example Level";
position = "448 445";
extent = "440 20";
horizSizing = "left";
profile = "MenuSubHeaderText";
tooltipProfile = "GuiToolTipProfile";
internalName = "GameModeNameTxt";
};
new GuiMLTextCtrl(GameModeDescriptionText) {
position = "448 473";
extent = "440 19";
horizSizing = "left";
profile = "GuiMLTextProfile";
tooltipProfile = "GuiToolTipProfile";
internalName = "GameModeDescTxt";
};
};
new GuiContainer(LevelSelectContainer) {
position = "196 119";
extent = "888 566";
horizSizing = "center";
profile = "GuiNonModalDefaultProfile";
visible = "0";
tooltipProfile = "GuiToolTipProfile";
hidden = "1";
new GuiScrollCtrl(LevelPreviewScroll) {
hScrollBar = "alwaysOff";
@ -120,7 +183,7 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
};
};
new GuiBitmapCtrl(LevelPreviewBitmap) {
BitmapAsset = "";
BitmapAsset = "UI:no_preview_image";
position = "448 0";
extent = "440 440";
horizSizing = "left";
@ -128,7 +191,7 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
};
new GuiTextCtrl(LevelNameText) {
text = "";
text = "Example Level";
position = "448 445";
extent = "440 20";
horizSizing = "left";
@ -214,7 +277,6 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
};
new GuiTextEditCtrl(serverNameCTRL) {
text = "";
position = "606 4";
extent = "295 22";
horizSizing = "left";
@ -240,7 +302,6 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
};
new GuiTextEditCtrl(serverPassCTRL) {
text = "";
position = "606 4";
extent = "295 22";
horizSizing = "left";
@ -314,7 +375,7 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
new GuiIconButtonCtrl(ChooseLevelStartBtn) {
BitmapAsset = "UI:Keyboard_Black_Return_image";
BitmapAsset = "UI:Keyboard_Black_Space_image";
sizeIconToButton = "1";
makeIconSquare = "1";
textLocation = "Center";

View file

@ -42,6 +42,70 @@ function ChooseLevelMenu::onWake(%this)
%this.fillPrefEntries();
LevelPreviewArray.clear();
refreshGameModesList();
if(!$pref::HostMultiPlayer)
ChooseLevelTitleText.setText("SINGLE PLAYER");
else
ChooseLevelTitleText.setText("CREATE SERVER");
ChooseLevelMenuTabList-->ConfigBtn.visible = $pref::HostMultiPlayer;
ChooseLevelMenuTabList.visible = true;//$pref::HostMultiPlayer;
ChooseLevelMenuNavButtonOverlay.visible = true;//$pref::HostMultiPlayer;
%this.schedule(32, openMenu, 0);
}
function refreshGameModesList()
{
GameModePreviewArray.clear();
$pref::Server::GameMode = "";
ChooseLevelMenuTabList-->LevelBtn.active = false;
//popilate the gamemodes list first
%gamemodeList = getGameModesList();
for(%i=0; %i < getTokenCount(%gamemodeList, ";"); %i++)
{
%gameModeObj = getToken(%gamemodeList, ";", %i);
if(isObject(%gameModeObj))
{
%gameModeName = %gameModeObj.gameModeName;
if(%gameModeName $= "")
%gameModeName = %gameModeObj.getName();
%preview = new GuiButtonCtrl() {
position = "0 0";
extent = ChooseLevelMenu.previewButtonSize;
buttonType = "PushButton";
profile = GuiMenuButtonLeftJustProfile;
horizSizing = "right";
vertSizing = "bottom";
internalName = "button";
class = "GameModePreviewButton";
//command = "ChooseGameModeBegin(" @ %i @ ");";
altCommand = "ChooseGameModeBegin(" @ %i @ ");"; //allow doubleclick to quick action it
text = %gameModeName;
gameModeObj = %gameModeObj;
gameModeDesc = %gameModeObj.description;
previewImage = %gameModeObj.previewImage;
textMargin = 120;
groupNum = 2;
cansave = false;
};
GameModePreviewArray.add(%preview);
}
}
}
function refreshLevelsList()
{
LevelPreviewArray.clear();
//fetch the levelAssets
ChooseLevelAssetQuery.clear();
AssetDatabase.findAssetType(ChooseLevelAssetQuery, "LevelAsset");
@ -57,6 +121,7 @@ function ChooseLevelMenu::onWake(%this)
return;
}
//filter the levelAssets by the gamemode selected
for(%i=0; %i < %count; %i++)
{
%assetId = ChooseLevelAssetQuery.getAsset(%i);
@ -71,6 +136,32 @@ function ChooseLevelMenu::onWake(%this)
if ( !isFile(%file) )
continue;
if( %levelAsset.isSubScene )
continue;
//filter for selected gamemode
%levelGameModes = %levelAsset.gameModesNames;
echo("LevelAsset gamemodes: " @ %levelGameModes);
%foundGameModeMatch = false;
for(%gm = 0; %gm < getTokenCount(%levelGameModes, ";"); %gm++)
{
%gameModeName = getToken(%levelGameModes, ";", %gm);
echo("testing gamemode: " @ %gameModeName);
echo("selected gamemode: " @ $pref::Server::GameMode.getName());
if(%gameModeName $= $pref::Server::GameMode.getName())
{
%foundGameModeMatch = true;
break;
}
}
if(!%foundGameModeMatch)
continue;
%levelPreviewImg = %levelAsset.getPreviewImagePath();
if (!isFile(%levelPreviewImg))
@ -78,7 +169,7 @@ function ChooseLevelMenu::onWake(%this)
%preview = new GuiIconButtonCtrl() {
position = "0 0";
extent = %this.previewButtonSize;
extent = ChooseLevelMenu.previewButtonSize;
buttonType = "PushButton";
profile = GuiMenuButtonLeftJustProfile;
horizSizing = "right";
@ -86,7 +177,7 @@ function ChooseLevelMenu::onWake(%this)
internalName = "button";
class = "LevelPreviewButton";
//command = "$selectedLevelAsset = " @ %assetId @ ";";
altCommand = "ChooseLevelBegin(1);"; //allow doubleclick to quick action it
altCommand = "ChooseLevelBegin(" @ %i @ ");"; //allow doubleclick to quick action it
text = %levelAsset.levelName;
bitmapAsset = %levelPreviewImg;
levelAsset = %levelAsset;
@ -107,20 +198,10 @@ function ChooseLevelMenu::onWake(%this)
// Also add the new level mission as defined in the world editor settings
// if we are choosing a level to launch in the editor.
if ( %this.launchInEditor )
if ( ChooseLevelMenu.launchInEditor )
{
%this.addMissionFile( "tools/levels/DefaultEditorLevel.mis" );
ChooseLevelMenu.addMissionFile( "tools/levels/DefaultEditorLevel.mis" );
}
if(!$pref::HostMultiPlayer)
ChooseLevelTitleText.setText("SINGLE PLAYER");
else
ChooseLevelTitleText.setText("CREATE SERVER");
ChooseLevelMenuTabList.visible = $pref::HostMultiPlayer;
ChooseLevelMenuNavButtonOverlay.visible = $pref::HostMultiPlayer;
%this.schedule(32, openMenu, 0);
}
if(!isObject( ChooseLevelActionMap ) )
@ -133,8 +214,8 @@ if(!isObject( ChooseLevelActionMap ) )
ChooseLevelActionMap.bind( keyboard, e, ChooseLevelMenuNextMenu);
ChooseLevelActionMap.bind( gamepad, btn_r, ChooseLevelMenuNextMenu);
ChooseLevelActionMap.bind( keyboard, Space, ChooseLevelBegin );
ChooseLevelActionMap.bind( gamepad, btn_a, ChooseLevelBegin );
ChooseLevelActionMap.bind( keyboard, Space, ChooseLevelMenuOption );
ChooseLevelActionMap.bind( gamepad, btn_a, ChooseLevelMenuOption );
}
function ChooseLevelMenu::syncGUI(%this)
@ -169,14 +250,26 @@ function LevelPreviewArray::syncGUI(%this)
$selectedLevelAsset = %btn.levelAssetId;
}
function GameModePreviewArray::syncGUI(%this)
{
%btn = %this.getObject(%this.listPosition);
%btn.setHighlighted(true);
$pref::Server::GameMode = %btn.gameModeObject;
}
function ChooseLevelMenuPrevMenu(%val)
{
if(%val && $pref::HostMultiPlayer)
if(%val)
{
%currentIdx = ChooseLevelMenu.currentMenuIdx;
ChooseLevelMenu.currentMenuIdx -= 1;
ChooseLevelMenu.currentMenuIdx = mClamp(ChooseLevelMenu.currentMenuIdx, 0, 1);
%endIndex = 1;
if($pref::HostMultiPlayer)
%endIndex = 2;
ChooseLevelMenu.currentMenuIdx = mClamp(ChooseLevelMenu.currentMenuIdx, 0, %endIndex);
if(%currentIdx == ChooseLevelMenu.currentMenuIdx)
return;
@ -187,12 +280,16 @@ function ChooseLevelMenuPrevMenu(%val)
function ChooseLevelMenuNextMenu(%val)
{
if(%val && $pref::HostMultiPlayer)
if(%val)
{
%currentIdx = ChooseLevelMenu.currentMenuIdx;
ChooseLevelMenu.currentMenuIdx += 1;
ChooseLevelMenu.currentMenuIdx = mClamp(ChooseLevelMenu.currentMenuIdx, 0, 1);
%endIndex = 1;
if($pref::HostMultiPlayer)
%endIndex = 2;
ChooseLevelMenu.currentMenuIdx = mClamp(ChooseLevelMenu.currentMenuIdx, 0, %endIndex);
if(%currentIdx == ChooseLevelMenu.currentMenuIdx)
return;
@ -201,15 +298,17 @@ function ChooseLevelMenuNextMenu(%val)
}
}
function ChooseLevelMenu::openMenu(%this, %menuIdx)
{
LevelSelectContainer.setVisible(%menuIdx == 0);
ServerConfigContainer.setVisible(%menuIdx == 1);
GameModeSelectContainer.setVisible(%menuIdx == 0);
LevelSelectContainer.setVisible(%menuIdx == 1);
ServerConfigContainer.setVisible(%menuIdx == 2);
if(%menuIdx == 0)
$MenuList = LevelPreviewArray;
$MenuList = GameModePreviewArray;
else if(%menuIdx == 1)
$MenuList = LevelPreviewArray;
else if(%menuIdx == 2)
$MenuList = ServerConfigList;
%this.currentMenuIdx = %menuIdx;
@ -220,37 +319,63 @@ function ChooseLevelMenu::openMenu(%this, %menuIdx)
%this.syncGui();
}
function ChooseLevelBegin(%val)
function ChooseLevelMenuOption(%val)
{
if(%val)
{
// So we can't fire the button when loading is in progress.
if ( isObject( ServerGroup ) )
return;
Canvas.popDialog();
%entry = LevelPreviewArray.getObject(LevelPreviewArray.listPosition);
if(!AssetDatabase.isDeclaredAsset(%entry.levelAssetId))
{
MessageBoxOK("Error", "Selected level preview does not have a valid level asset!");
return;
}
$selectedLevelAsset = %entry.levelAssetId;
if(ChooseLevelMenu.currentMenuIdx == 0)
ChooseGameModeBegin(ChooseLevelMenu.listPosition);
else if(ChooseLevelMenu.currentMenuIdx == 1)
ChooseLevelBegin(ChooseLevelMenu.listPosition);
}
}
// Launch the chosen level with the editor open?
if ( ChooseLevelMenu.launchInEditor )
{
activatePackage( "BootEditor" );
ChooseLevelMenu.launchInEditor = false;
StartGame(%entry.levelAssetId, "SinglePlayer");
}
else
{
StartGame(%entry.levelAssetId);
}
function ChooseGameModeBegin(%index)
{
%entry = GameModePreviewArray.getObject(%index);
if(!isObject(%entry) || !isObject(%entry.gameModeObj))
{
MessageBoxOK("Error", "Selected game mode does not have a valid mode");
return;
}
$pref::Server::GameMode = %entry.gameModeObj;
ChooseLevelMenuTabList-->LevelBtn.active = true;
refreshLevelsList();
ChooseLevelMenu.openMenu(1);
}
function ChooseLevelBegin(%index)
{
// So we can't fire the button when loading is in progress.
if ( isObject( ServerGroup ) )
return;
Canvas.popDialog();
%entry = LevelPreviewArray.getObject(%index);
if(!AssetDatabase.isDeclaredAsset(%entry.levelAssetId))
{
MessageBoxOK("Error", "Selected level preview does not have a valid level asset!");
return;
}
$selectedLevelAsset = %entry.levelAssetId;
// Launch the chosen level with the editor open?
if ( ChooseLevelMenu.launchInEditor )
{
activatePackage( "BootEditor" );
ChooseLevelMenu.launchInEditor = false;
StartGame(%entry.levelAssetId, "SinglePlayer");
}
else
{
StartGame(%entry.levelAssetId);
}
}
@ -270,6 +395,23 @@ function ChooseLevelMenu::onSleep( %this )
}
}
function GameModePreviewButton::onHighlighted(%this, %highlighted)
{
if(%highlighted)
{
$MenuList.listPosition = $MenuList.getObjectIndex(%this);
GameModePreviewBitmap.bitmapAsset = %this.previewImage;
GameModePreviewBitmap.visible = (%this.previewImage !$= "");
GameModeNameText.text = %this.text;
GameModeDescriptionText.setText(%this.gameModeDesc);
GameModePreviewScroll.scrollToObject(%this);
}
}
function LevelPreviewButton::onHighlighted(%this, %highlighted)
{
if(%highlighted)

View file

@ -87,6 +87,7 @@ function initializeAssetBrowser()
exec("./scripts/looseFileAudit." @ $TorqueScriptFileExtension);
exec("./scripts/creator." @ $TorqueScriptFileExtension);
exec("./scripts/setAssetTarget." @ $TorqueScriptFileExtension);
exec("./scripts/utils." @ $TorqueScriptFileExtension);
//Processing for the different asset types
exec("./scripts/assetTypes/component." @ $TorqueScriptFileExtension);
@ -110,6 +111,7 @@ function initializeAssetBrowser()
exec("./scripts/assetTypes/looseFiles." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/prefab." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/creatorObj." @ $TorqueScriptFileExtension);
exec("./scripts/assetTypes/gameMode." @ $TorqueScriptFileExtension);
new ScriptObject( AssetBrowserPlugin )
{

View file

@ -79,7 +79,6 @@ function AssetBrowser_addModuleWindow::CreateNewModule(%this)
%line = strreplace( %line, "@@", %newModuleName );
%file.writeline(%line);
echo(%line);
}
%file.close();

View file

@ -0,0 +1,96 @@
function AssetBrowser::createGameMode(%this)
{
%moduleName = AssetBrowser.newAssetSettings.moduleName;
%moduleDef = ModuleDatabase.findModule(%moduleName, 1);
%assetName = AssetBrowser.newAssetSettings.assetName;
%assetPath = NewAssetTargetAddress.getText() @ "/";
%scriptPath = %assetPath @ %assetName @ "." @ $TorqueScriptFileExtension;
%fullScriptPath = makeFullPath(%scriptPath);
%file = new FileObject();
%templateFile = new FileObject();
%postFXTemplateCodeFilePath = %this.templateFilesPath @ "gameMode." @ $TorqueScriptFileExtension @ ".template";
if(%file.openForWrite(%fullScriptPath) && %templateFile.openForRead(%postFXTemplateCodeFilePath))
{
while( !%templateFile.isEOF() )
{
%line = %templateFile.readline();
%line = strreplace( %line, "@@", %assetName );
%file.writeline(%line);
}
%file.close();
%templateFile.close();
}
else
{
%file.close();
%templateFile.close();
warnf("createGameMode - Something went wrong and we couldn't write the gameMode script file!");
}
%localScriptPath = strReplace(%scriptPath, "data/" @ %moduleName @ "/", "./");
%execLine = " %this.queueExec(\"" @ %localScriptPath @ "\");";
%moduleScriptPath = makeFullPath(%moduleDef.ModuleScriptFilePath @ "." @ $TorqueScriptFileExtension);
echo("Attempting exec insert for file: " @ %moduleScriptPath);
%lineIdx = Tools::findInFile(%moduleScriptPath, "*function*" @ %moduleName @ "::initClient*");
if(%lineIdx != -1)
{
echo("INIT CLIENT FUNCTION LINE FOUND ON: " @ %lineIdx);
%insertLineIdx = Tools::findInFunction(%moduleScriptPath, %moduleName, "initClient", "*//--FILE EXEC END--*");
echo("FILE EXEC END LINE FOUND ON: " @ %insertLineIdx);
if(%insertLineIdx == -1)
{
//If there are not 'blocking' comments, then just slap the exec on the end of the function def
//as it doesn't really matter now
Tools::appendLineToFunction(%moduleScriptPath, %moduleName, "initClient", %execLine);
}
else
{
Tools::insertInFile(%moduleScriptPath, %insertLineIdx, %execLine, true);
}
}
%lineIdx = Tools::findInFile(%moduleScriptPath, "*function*" @ %moduleName @ "::initServer*");
if(%lineIdx != -1)
{
echo("INIT SERVER FUNCTION LINE FOUND ON: " @ %lineIdx);
%insertLineIdx = Tools::findInFunction(%moduleScriptPath, %moduleName, "initServer", "*//--FILE EXEC END--*");
echo("FILE EXEC END LINE FOUND ON: " @ %insertLineIdx);
if(%insertLineIdx == -1)
{
//If there are not 'blocking' comments, then just slap the exec on the end of the function def
//as it doesn't really matter now
Tools::appendLineToFunction(%moduleScriptPath, %moduleName, "initServer", %execLine);
}
else
{
Tools::insertInFile(%moduleScriptPath, %insertLineIdx, %execLine, true);
}
}
//and we'll go ahead and force execute the script file so the gamemode is 'available' for use immediately
exec(%scriptPath);
if(isObject(%assetName))
{
//it's possible it got moved to an instant group upon execution, so we'll just
//shove it back to the RootGroup by force to be 100% sure
RootGroup.add(%assetName);
}
return %scriptPath;
}

View file

@ -3,6 +3,8 @@ function AssetBrowser::setupCreateNewLevelAsset(%this)
NewAssetPropertiesInspector.startGroup("Level");
NewAssetPropertiesInspector.addField("LevelName", "Level Name", "String", "Human-readable name of new level", "", "", %this.newAssetSettings);
NewAssetPropertiesInspector.addField("levelPreviewImage", "Level Preview Image", "Image", "Preview Image for the level", "", "", %this.newAssetSettings);
NewAssetPropertiesInspector.addField("isSubScene", "Is SubScene", "bool", "Is this levelAsset intended as a subScene", "", "", %this.newAssetSettings);
NewAssetPropertiesInspector.endGroup();
}
@ -20,14 +22,16 @@ function AssetBrowser::createLevelAsset(%this)
%assetPath = NewAssetTargetAddress.getText() @ "/";
%misExtension = AssetBrowser.newAssetSettings.isSubScene ? ".subMis" : ".mis";
%tamlpath = %assetPath @ %assetName @ ".asset.taml";
%levelPath = %assetPath @ %assetName @ ".mis";
%levelPath = %assetPath @ %assetName @ %misExtension;
%asset = new LevelAsset()
{
AssetName = %assetName;
versionId = 1;
LevelFile = %assetName @ ".mis";
LevelFile = %assetName @ %misExtension;
DecalsFile = %assetName @ ".mis.decals";
PostFXPresetFile = %assetName @ ".postfxpreset." @ $TorqueScriptFileExtension;
ForestFile = %assetName @ ".forest";
@ -35,6 +39,7 @@ function AssetBrowser::createLevelAsset(%this)
LevelName = AssetBrowser.newAssetSettings.levelName;
AssetDescription = AssetBrowser.newAssetSettings.description;
PreviewImage = AssetBrowser.newAssetSettings.levelPreviewImage;
isSubScene = AssetBrowser.newAssetSettings.isSubScene;
};
TamlWrite(%asset, %tamlpath);
@ -58,15 +63,32 @@ function AssetBrowser::createLevelAsset(%this)
%moduleDef = ModuleDatabase.findModule(%moduleName, 1);
%addSuccess = AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath);
if(!%addSuccess)
{
error("AssetBrowser::createLevelAsset() - failed to add declared asset: " @ %tamlpath @ " for module: " @ %moduleDef);
}
AssetBrowser.refresh();
return %tamlpath;
}
//==============================================================================
//SubScene derivative
//==============================================================================
function AssetBrowser::setupCreateNewSubScene(%this)
{
%this.newAssetSettings.isSubScene = true;
%this.newAssetSettings.assetType = "LevelAsset";
}
//==============================================================================
function AssetBrowser::editLevelAsset(%this, %assetDef)
{
schedule( 1, 0, "EditorOpenMission", %assetDef);
if(!%assetDef.isSubScene)
schedule( 1, 0, "EditorOpenMission", %assetDef);
}
//Renames the asset
@ -150,4 +172,100 @@ function AssetBrowser::buildLevelAssetPreview(%this, %assetDef, %previewData)
"Asset Type: Level Asset\n" @
"Asset Definition ID: " @ %assetDef @ "\n" @
"Level File path: " @ %assetDef.getLevelPath();
}
function createAndAssignLevelAsset(%moduleName, %assetName)
{
$createAndAssignField.apply(%moduleName @ ":" @ %assetName);
}
function EWorldEditor::createSelectedAsSubScene( %this, %object )
{
//create new level asset here
AssetBrowser.setupCreateNewAsset("SubScene", AssetBrowser.selectedModule, "finishCreateSelectedAsSubScene");
%this.contextActionObject = %object;
}
function finishCreateSelectedAsSubScene(%assetId)
{
echo("finishCreateSelectedAsSubScene");
%subScene = new SubScene() {
levelAsset = %assetId;
};
%levelAssetDef = AssetDatabase.acquireAsset(%assetId);
%levelFilePath = %levelAssetDef.getLevelPath();
//write out to file
EWorldEditor.contextActionObject.save(%levelFilePath);
AssetDatabase.releaseAsset(%assetId);
getRootScene().add(%subScene);
EWorldEditor.contextActionObject.delete();
%subScene.load();
%subScene.recalculateBounds();
%scalar = $SubScene::createScalar;
if(%scalar $= "")
%scalar = 1.5;
//pad for loading boundary
%subScene.scale = VectorScale(%subScene.scale, %scalar);
}
function GuiInspectorTypeLevelAssetPtr::onControlDropped( %this, %payload, %position )
{
Canvas.popDialog(EditorDragAndDropLayer);
// Make sure this is a color swatch drag operation.
if( !%payload.parentGroup.isInNamespaceHierarchy( "AssetPreviewControlType_AssetDrop" ) )
return;
%assetType = %payload.assetType;
%module = %payload.moduleName;
%assetName = %payload.assetName;
if(%assetType $= "LevelAsset")
{
%cmd = %this @ ".apply(\""@ %module @ ":" @ %assetName @ "\");";
eval(%cmd);
}
EWorldEditor.isDirty = true;
}
function AssetBrowser::onLevelAssetEditorDropped(%this, %assetDef, %position)
{
%assetId = %assetDef.getAssetId();
if(!%assetDef.isSubScene)
{
errorf("Cannot drag-and-drop LevelAsset into WorldEditor");
return;
}
%newSubScene = new SubScene()
{
position = %position;
levelAsset = %assetId;
};
getScene(0).add(%newSubScene);
%newSubScene.load();
%newSubScene.recalculateBounds();
EWorldEditor.clearSelection();
EWorldEditor.selectObject(%newSubScene);
EWorldEditor.dropSelection();
EWorldEditor.isDirty = true;
MECreateUndoAction::submit(%newSubScene );
}

View file

@ -176,6 +176,31 @@ function AssetBrowser::performRenameAsset(%this, %originalAssetName, %newName)
%buildCommand = %this @ ".rename" @ EditAssetPopup.assetType @ "(" @ %assetDef @ "," @ %newName @ ");";
eval(%buildCommand);
}
else
{
//do generic action here
%oldAssetId = %moduleName @ ":" @ %originalAssetName;
%assetDef = AssetDatabase.acquireAsset(%oldAssetId);
%assetFileName = fileName(%assetDef.getFileName());
%assetFilePath = filePath(%assetDef.getFileName());
%pattern = %assetFilePath @ "/" @ %assetFileName @ ".*";
echo("Searching for matches of asset files following pattern: " @ %pattern);
//get list of files that match the name in the same dir
//do a rename on them
for (%file = findFirstFileMultiExpr(%pattern); %file !$= ""; %file = findNextFileMultiExpr(%pattern))
{
if(%file !$= %assetFileName)
renameAssetLooseFile(%file, %newName);
}
//update the assetDef
replaceInFile(%assetDef.getFileName(), %originalAssetName, %newName);
renameAssetFile(%assetDef, %newName);
}
}
else
{

View file

@ -117,7 +117,8 @@ function AssetBrowser::buildPopupMenus(%this)
//item[ 0 ] = "Create Component" TAB AddNewComponentAssetPopup;
item[ 0 ] = "Create Script" TAB "" TAB "AssetBrowser.setupCreateNewAsset(\"ScriptAsset\", AssetBrowser.selectedModule);";
item[ 1 ] = "Create State Machine" TAB "" TAB "AssetBrowser.setupCreateNewAsset(\"StateMachineAsset\", AssetBrowser.selectedModule);";
//item[ 3 ] = "-";
item[ 2 ] = "-";
item[ 3 ] = "Create Game Mode" TAB "" TAB "AssetBrowser.setupCreateNewAsset(\"GameMode\", AssetBrowser.selectedModule);";
//item[ 3 ] = "Create Game Object" TAB "" TAB "AssetBrowser.createNewGameObjectAsset(\"NewGameObject\", AssetBrowser.selectedModule);";
};
//%this.AddNewScriptAssetPopup.insertSubMenu(0, "Create Component", AddNewComponentAssetPopup);

View file

@ -0,0 +1,176 @@
//This file implements game mode logic for an Example gamemode. The primary functions:
//@@::onMissionStart
//@@::onMissionReset
//@@::onMissionEnd
//Are the primary hooks for the server to start, restart and end any active gamemodes
//onMissionStart, for example is called from core/clientServer/scripts/server/levelLoad.cs
//It's called once the server has successfully loaded the level, and has parsed
//through any active scenes to get GameModeNames defined by them. It then iterates
//over them and calls these callbacks to envoke gamemode behaviors. This allows multiple
//gamemodes to be in effect at one time. Modules can implement as many gamemodes as you want.
//
//For levels that can be reused for multiple gammodes, the general setup would be a primary level file
//with the Scene in it having the main geometry, weapons, terrain, etc. You would then have subScenes that
//each contain what's necessary for the given gamemode, such as a subScene that just adds the flags and capture
//triggers for a CTF mode. The subscene would then have it's GameModeName defined to run the CTF gamemode logic
//and the levelLoad code will execute it.
if(!isObject(@@))
{
new GameMode(@@){};
}
//This function is called when the level finishes loading. It sets up the initial configuration, variables and
//spawning and dynamic objects, timers or rules needed for the gamemode to run
function @@::onMissionStart(%this)
{
//set up the game and game variables
%this.initGameVars();
if (%this.isActive())
{
error("@@::onMissionStart: End the game first!");
return;
}
%this.setActive(true);
}
//This function is called when the level ends. It can be envoked due to the gamemode ending
//but is also kicked off when the game server is shut down as a form of cleanup for anything the gamemode
//created or is managing like the above mentioned dynamic objects or timers
function @@::onMissionEnded(%this)
{
if (!%this.isActive())
{
error("@@::onMissionEnded: No game running!");
return;
}
%this.setActive(false);
}
//This function is called in the event the server resets and is used to re-initialize the gamemode
function @@::onMissionReset(%this)
{
// Called by resetMission(), after all the temporary mission objects
// have been deleted.
%this.initGameVars();
}
//This sets up our gamemode's duration time
function @@::initGameVars(%this)
{
// Set the gameplay parameters
%this.duration = 30 * 60;
}
//This is called when the timer runs out, allowing the gamemode to end
function @@::onGameDurationEnd(%this)
{
//we don't end if we're currently editing the level
if (%this.duration && !(EditorIsActive() && GuiEditorIsActive()))
%this.onMissionEnded();
}
//This is called to actually spawn a control object for the player to utilize
//A player character, spectator camera, etc.
function @@::spawnControlObject(%this, %client)
{
//In this example, we just spawn a camera
/*if (!isObject(%client.camera))
{
if(!isObject(Observer))
{
datablock CameraData(Observer)
{
mode = "Observer";
};
}
%client.camera = spawnObject("Camera", Observer);
}
// If we have a camera then set up some properties
if (isObject(%client.camera))
{
MissionCleanup.add( %this.camera );
%client.camera.scopeToClient(%client);
%client.setControlObject(%client.camera);
%client.camera.setTransform("0 0 1 0 0 0 0");
}*/
}
//This is called when the client has finished loading the mission, but aren't "in" it yet
//allowing for some last-second prepwork
function @@::onClientMissionLoaded(%this)
{
$clientLoaded++;
if ($clientLoaded == $clientConneted)
$gameReady = true;
}
//This is called when the client has initially established a connection to the game server
//It's used for setting up anything ahead of time for the client, such as loading in client-passed
//config stuffs, saved data or the like that should be handled BEFORE the client has actually entered
//the game itself
function @@::onClientConnect(%this, %client)
{
$clientConneted++;
getPlayer(%client);
}
//This is called when a client enters the game server. It's used to spawn a player object
//set up any client-specific properties such as saved configs, values, their name, etc
//These callbacks are activated in core/clientServer/scripts/server/levelDownload.cs
function @@::onClientEnterGame(%this, %client)
{
$Game::DefaultPlayerClass = "Player";
$Game::DefaultPlayerDataBlock = "DefaultPlayerData";
$Game::DefaultPlayerSpawnGroups = "spawn_player CameraSpawnPoints PlayerSpawnPoints PlayerDropPoints";
$Game::DefaultCameraClass = "Camera";
$Game::DefaultCameraDataBlock = "Observer";
$Game::DefaultCameraSpawnGroups = "spawn_player CameraSpawnPoints PlayerSpawnPoints PlayerDropPoints";
//Spawn NPCs for the map and stuff here?
// This function currently relies on some helper functions defined in
// core/scripts/spawn.cs. For custom spawn behaviors one can either
// override the properties on the SpawnSphere's or directly override the
// functions themselves.
}
function @@::onSceneLoaded(%this)
{
}
function @@::onSceneUnloaded(%this)
{
}
function @@::onSubsceneLoaded(%this)
{
}
function @@::onSubsceneUnloaded(%this)
{
}
//This is called when the player leaves the game server. It's used to clean up anything that
//was spawned or setup for the client when it connected, in onClientEnterGame
//These callbacks are activated in core/clientServer/scripts/server/levelDownload.cs
function @@::onClientLeaveGame(%this, %client)
{
// Cleanup the camera
if (isObject(%this.camera))
%this.camera.delete();
// Cleanup the player
if (isObject(%this.player))
%this.player.delete();
}

View file

@ -9,6 +9,8 @@ function @@::onDestroy(%this)
//This is called when the server is initially set up by the game application
function @@::initServer(%this)
{
//--FILE EXEC BEGIN--
//--FILE EXEC END--
}
//This is called when the server is created for an actual game/map to be played
@ -17,16 +19,14 @@ function @@::onCreateGameServer(%this)
//These are common managed data files. For any datablock-based stuff that gets generated by the editors
//(that doesn't have a specific associated file, like data for a player class) will go into these.
//So we'll register them now if they exist.
if(isFile("./scripts/managedData/managedDatablocks." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedDatablocks");
if(isFile("./scripts/managedData/managedForestItemData." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedForestItemData");
if(isFile("./scripts/managedData/managedForestBrushData." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedForestBrushData");
if(isFile("./scripts/managedData/managedParticleEmitterData." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedParticleEmitterData");
if(isFile("./scripts/managedData/managedParticleData." @ $TorqueScriptFileExtension))
%this.registerDatablock("./scripts/managedData/managedParticleData");
%this.registerDatablock("./scripts/managedData/managedDatablocks");
%this.registerDatablock("./scripts/managedData/managedForestItemData");
%this.registerDatablock("./scripts/managedData/managedForestBrushData");
%this.registerDatablock("./scripts/managedData/managedParticleEmitterData");
%this.registerDatablock("./scripts/managedData/managedParticleData");
//--DATABLOCK EXEC BEGIN--
//--DATABLOCK EXEC END--
}
//This is called when the server is shut down due to the game/map being exited
@ -37,6 +37,8 @@ function @@::onDestroyGameServer(%this)
//This is called when the client is initially set up by the game application
function @@::initClient(%this)
{
//--FILE EXEC BEGIN--
//--FILE EXEC END--
}
//This is called when a client connects to a server

View file

@ -0,0 +1,253 @@
function testRpl()
{
ToolUtilityScripts::appendLineToFunction("data/prototyping/prototyping.tscript", "Prototyping", "onDestroyGameServer", "//AAAAAAAAAAAAAAAAAAAAA");
}
function Tools::appendLineToFunction(%file, %nameSpace, %functionName, %appendLine)
{
%fileOutput = new ArrayObject(){};
%fileObj = new FileObject(){};
%insideFunction = false;
%scopeDepth = 0;
if ( %fileObj.openForRead( %file ) )
{
while ( !%fileObj.isEOF() )
{
%line = %fileObj.readLine();
if(strIsMatchExpr("*function *(*)*", %line))
{
%fileOutput.add(%line);
%start = strpos(%line, "function ");
%end = strpos(%line, "(", %start);
%scannedFunctionName = "";
if(%start != -1 && %end != -1)
{
%scannedFunctionName = getSubStr(%line, %start + 9, %end-%start-9);
}
%matchesFunctionSig = false;
if(%nameSpace !$= "")
{
if(%scannedFunctionName $= (%nameSpace @ "::" @ %functionName))
%matchesFunctionSig = true;
}
else
{
if(%scannedFunctionName $= %functionName)
%matchesFunctionSig = true;
}
if(!%matchesFunctionSig)
continue;
%insideFunction = true;
if(strIsMatchExpr("*{*", %line))
{
%scopeDepth++;
}
if(strIsMatchExpr("*}*", %line))
{
%scopeDepth--;
}
}
else
{
if(%insideFunction && strIsMatchExpr("*{*", %line))
{
%scopeDepth++;
}
if(%insideFunction && strIsMatchExpr("*}*", %line))
{
%scopeDepth--;
if(%scopeDepth == 0) //we've fully backed out of the function scope, so resolve back to the parent
{
%insideFunction = false;
%fileOutput.add(%appendLine);
}
}
%fileOutput.add(%line);
}
}
%fileObj.close();
}
if ( %fileObj.openForWrite( %file ) )
{
for(%i=0; %i < %fileOutput.count(); %i++)
{
%line = %fileOutput.getKey(%i);
%fileObj.writeLine(%line);
}
%fileObj.close();
}
}
function Tools::findInFile(%file, %findText, %startingLineId)
{
if(%startingLineId $= "")
%startingLineId = 0;
%fileObj = new FileObject(){};
if ( %fileObj.openForRead( %file ) )
{
%lineId = 0;
while ( !%fileObj.isEOF() )
{
if(%lineId >= %startingLineId)
{
%line = %fileObj.readLine();
if(strIsMatchExpr(%findText, %line))
{
return %lineId;
}
}
%lineId++;
}
%fileObj.close();
}
return -1;
}
function Tools::findInFunction(%file, %nameSpace, %functionName, %findText)
{
%fileObj = new FileObject(){};
%insideFunction = false;
%scopeDepth = 0;
if ( %fileObj.openForRead( %file ) )
{
%lineId = -1;
while ( !%fileObj.isEOF() )
{
%line = %fileObj.readLine();
%lineId++;
if(strIsMatchExpr("*function *(*)*", %line))
{
%start = strpos(%line, "function ");
%end = strpos(%line, "(", %start);
%scannedFunctionName = "";
if(%start != -1 && %end != -1)
{
%scannedFunctionName = getSubStr(%line, %start + 9, %end-%start-9);
}
%matchesFunctionSig = false;
if(%nameSpace !$= "")
{
if(%scannedFunctionName $= (%nameSpace @ "::" @ %functionName))
%matchesFunctionSig = true;
}
else
{
if(%scannedFunctionName $= %functionName)
%matchesFunctionSig = true;
}
if(!%matchesFunctionSig)
continue;
%insideFunction = true;
if(strIsMatchExpr("*{*", %line))
{
%scopeDepth++;
}
if(strIsMatchExpr("*}*", %line))
{
%scopeDepth--;
}
}
else
{
if(%insideFunction && strIsMatchExpr("*{*", %line))
{
%scopeDepth++;
}
if(%insideFunction && strIsMatchExpr("*}*", %line))
{
%scopeDepth--;
if(%scopeDepth == 0) //we've fully backed out of the function scope, so resolve back to the parent
{
%insideFunction = false;
break;
}
}
if(%insideFunction && strIsMatchExpr(%findText, %line))
{
break;
}
}
}
%fileObj.close();
}
return %lineId;
}
function Tools::insertInFile(%file, %insertLineId, %insertText, %insertBefore)
{
%fileOutput = new ArrayObject(){};
%fileObj = new FileObject(){};
if ( %fileObj.openForRead( %file ) )
{
%lineId = 0;
while ( !%fileObj.isEOF() )
{
%line = %fileObj.readLine();
if(%insertLineId == %lineId)
{
if(!%insertBefore || %insertBefore $= "")
{
%fileOutput.add(%line);
%fileOutput.add(%insertText);
}
else
{
%fileOutput.add(%insertText);
%fileOutput.add(%line);
}
}
else
{
%fileOutput.add(%line);
}
%lineId++;
}
%fileObj.close();
}
if ( %fileObj.openForWrite( %file ) )
{
for(%i=0; %i < %fileOutput.count(); %i++)
{
%line = %fileOutput.getKey(%i);
%fileObj.writeLine(%line);
}
%fileObj.close();
}
}

View file

@ -301,6 +301,10 @@ function ESettingsWindow::getGeneralSettings(%this)
SettingsInspector.addSettingsField("WorldEditor/AutosaveInterval", "Autosave Interval(in minutes)", "int", "");
SettingsInspector.endGroup();
SettingsInspector.startGroup("SubScenes");
SettingsInspector.addSettingsField("WorldEditor/subSceneCreateScalar", "SubScene Creation Scalar", "float", "Amount to scale SubScene's bounds by when creating from scene objects.", "1.5");
SettingsInspector.endGroup();
SettingsInspector.startGroup("Paths");
//SettingsInspector.addSettingsField("WorldEditor/torsionPath", "Torsion Path", "filename", "");
SettingsInspector.endGroup();

View file

@ -375,6 +375,8 @@
name="orthoShowGrid">1</Setting>
<Setting
name="startupMode">Blank Level</Setting>
<Setting
name="subSceneCreateScalar">2</Setting>
<Setting
name="torsionPath">AssetWork_Debug.exe</Setting>
<Setting

View file

@ -472,7 +472,7 @@ $guiContent = new GuiControl() {
Profile = "ToolsGuiButtonProfile";
HorizSizing = "left";
VertSizing = "bottom";
Position = "290 22";
Position = "269 22";
Extent = "16 16";
MinExtent = "8 2";
canSave = "1";
@ -496,7 +496,7 @@ $guiContent = new GuiControl() {
Profile = "ToolsGuiButtonProfile";
HorizSizing = "left";
VertSizing = "bottom";
Position = "311 22";
Position = "290 22";
Extent = "16 16";
MinExtent = "8 2";
canSave = "1";
@ -512,6 +512,30 @@ $guiContent = new GuiControl() {
useModifiers = "1";
};
new GuiBitmapButtonCtrl(EWAddSceneGroupButton) {
canSaveDynamicFields = "0";
internalName = "AddSceneGroup";
Enabled = "1";
isContainer = "0";
Profile = "ToolsGuiButtonProfile";
HorizSizing = "left";
VertSizing = "bottom";
Position = "311 22";
Extent = "16 16";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
tooltipprofile = "ToolsGuiToolTipProfile";
ToolTip = "Add Scene Group";
hovertime = "1000";
bitmapAsset = "ToolsModule:add_simgroup_btn_n_image";
buttonType = "PushButton";
groupNum = "-1";
text = "";
useMouseEvents = "0";
useModifiers = "1";
};
new GuiBitmapButtonCtrl() {
canSaveDynamicFields = "0";
internalName = "DeleteSelection";

View file

@ -2007,7 +2007,7 @@ function EditorTree::onRightMouseUp( %this, %itemId, %mouse, %obj )
%popup.item[ 0 ] = "Delete" TAB "" TAB "EditorMenuEditDelete();";
%popup.item[ 1 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );";
%popup.item[ 2 ] = "-";
%popup.item[ 3 ] = "Make select a Sub-Level" TAB "" TAB "MakeSelectionASublevel();";
%popup.item[ 3 ] = "Make selected a Sub-Level" TAB "" TAB "MakeSelectionASublevel();";
}
else
{
@ -2103,6 +2103,14 @@ function EditorTree::onRightMouseUp( %this, %itemId, %mouse, %obj )
%popup.enableItem(15, true);
}
}
else if(%obj.getClassName() $= "SimGroup" ||
%obj.getClassName() $= "SceneGroup")
{
//if it's ACTUALLY a SimGroup or SceneGroup, have the ability to
//upconvert to SubScene
%popup.item[ 12 ] = "-";
%popup.item[ 13 ] = "Convert to SubScene" TAB "" TAB "EWorldEditor.createSelectedAsSubScene( " @ %popup.object @ " );";
}
}
}
}
@ -2142,7 +2150,7 @@ function EditorTree::isValidDragTarget( %this, %id, %obj )
if( %obj.name $= "CameraBookmarks" )
return EWorldEditor.areAllSelectedObjectsOfType( "CameraBookmark" );
else
return ( %obj.getClassName() $= "SimGroup" || %obj.isMemberOfClass("Scene"));
return ( %obj.getClassName() $= "SimGroup" || %obj.isMemberOfClass("Scene") || %obj.isMemberOfClass("SceneGroup"));
}
function EditorTree::onBeginReparenting( %this )
@ -2582,6 +2590,77 @@ function EWorldEditor::addSimGroup( %this, %groupCurrentSelection )
%this.syncGui();
}
function EWorldEditor::addSceneGroup( %this, %groupCurrentSelection )
{
%activeSelection = %this.getActiveSelection();
if ( %groupCurrentSelection && %activeSelection.getObjectIndex( getScene(0) ) != -1 )
{
toolsMessageBoxOK( "Error", "Cannot add Scene to a new SceneGroup" );
return;
}
// Find our parent.
%parent = getScene(0);
if( !%groupCurrentSelection && isObject( %activeSelection ) && %activeSelection.getCount() > 0 )
{
%firstSelectedObject = %activeSelection.getObject( 0 );
if( %firstSelectedObject.isMemberOfClass( "SimGroup" ) )
%parent = %firstSelectedObject;
else if( %firstSelectedObject.getId() != getScene(0).getId() )
%parent = %firstSelectedObject.parentGroup;
}
// If we are about to do a group-selected as well,
// starting recording an undo compound.
if( %groupCurrentSelection )
Editor.getUndoManager().pushCompound( "Group Selected" );
// Create the SimGroup.
%object = new SceneGroup()
{
parentGroup = %parent;
};
MECreateUndoAction::submit( %object );
// Put selected objects into the group, if requested.
if( %groupCurrentSelection && isObject( %activeSelection ) )
{
%undo = UndoActionReparentObjects::create( EditorTree );
%numObjects = %activeSelection.getCount();
for( %i = 0; %i < %numObjects; %i ++ )
{
%sel = %activeSelection.getObject( %i );
%undo.add( %sel, %sel.parentGroup, %object );
%object.add( %sel );
}
%undo.addToManager( Editor.getUndoManager() );
}
// Stop recording for group-selected.
if( %groupCurrentSelection )
Editor.getUndoManager().popCompound();
// When not grouping selection, make the newly created SimGroup the
// current selection.
if( !%groupCurrentSelection )
{
EWorldEditor.clearSelection();
EWorldEditor.selectObject( %object );
}
// Refresh the Gui.
%this.syncGui();
}
function EWorldEditor::toggleLockChildren( %this, %simGroup )
{
foreach( %child in %simGroup )
@ -2847,6 +2926,16 @@ function EWAddSimGroupButton::onCtrlClick( %this )
EWorldEditor.addSimGroup( true );
}
function EWAddSceneGroupButton::onDefaultClick( %this )
{
EWorldEditor.addSceneGroup();
}
function EWAddSceneGroupButton::onCtrlClick( %this )
{
EWorldEditor.addSceneGroup( true );
}
//------------------------------------------------------------------------------
function EWToolsToolbar::reset( %this )

View file

@ -34,6 +34,7 @@ EditorSettings.setDefaultValue( "orthoFOV", "50" );
EditorSettings.setDefaultValue( "orthoShowGrid", "1" );
EditorSettings.setDefaultValue( "AutosaveInterval", "5" );
EditorSettings.setDefaultValue( "forceSidebarToSide", "1" );
EditorSettings.setDefaultValue( "subSceneCreateScalar", $SubScene::createScalar );
if( isFile( "C:/Program Files/Torsion/Torsion.exe" ) )
EditorSettings.setDefaultValue( "torsionPath", "C:/Program Files/Torsion/Torsion.exe" );

View file

@ -329,9 +329,6 @@ function EditorSaveMission()
if(EWorldEditor.isDirty || ETerrainEditor.isMissionDirty)
{
//Inform objects a save is happening, in case there is any special pre-save behavior a class needs to do
getScene(0).callOnChildren("onSaving", $Server::MissionFile);
getScene(0).save($Server::MissionFile);
}
@ -604,18 +601,20 @@ function EditorOpenSceneAppend(%levelAsset)
function MakeSelectionASublevel()
{
/*%size = EWorldEditor.getSelectionSize();
%size = EWorldEditor.getSelectionSize();
if ( %size == 0 )
return;
//Make a new Scene object
//Make a group for the objects to go into to be processed as into a
//subscene
%group = new SimGroup();
for(%i=0; %i < %size; %i++)
{
%group.add(EWorldEditor.getSelectedObject(%i));
}
%a = EWorldEditor.getSelectedObject(0);
%b = EWorldEditor.getSelectedObject(1);*/
//Now that we have a group, process it into a subscene
EWorldEditor.createSelectedAsSubScene(%group);
}
function updateEditorRecentLevelsList(%levelAssetId)