Merge pull request #1311 from Areloch/SubScenes_Gamemode_PR

Implementation of Subscenes, SceneGroups and Gamemodes
This commit is contained in:
Areloch 2024-12-16 22:02:49 -06:00 committed by GitHub
commit a88aa7a007
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
67 changed files with 4353 additions and 528 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
@ -157,30 +148,88 @@ void Scene::removeObject(SimObject* object)
Parent::removeObject(object);
}
void Scene::addDynamicObject(SceneObject* object)
void Scene::addDynamicObject(SimObject* object)
{
mDynamicObjects.push_back(object);
SimGroup* cleanupGroup;
if(Sim::findObject("MissionCleanup", cleanupGroup))
{
cleanupGroup->addObject(object);
}
//Do it like regular, though we should probably bail if we're trying to add non-scene objects to the scene?
Parent::addObject(object);
//Parent::addObject(object);
}
void Scene::removeDynamicObject(SceneObject* object)
void Scene::removeDynamicObject(SimObject* object)
{
mDynamicObjects.remove(object);
SimGroup* cleanupGroup;
if (Sim::findObject("MissionCleanup", cleanupGroup))
{
cleanupGroup->removeObject(object);
}
//Do it like regular, though we should probably bail if we're trying to add non-scene objects to the scene?
Parent::removeObject(object);
//Parent::removeObject(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)
@ -205,7 +254,7 @@ void Scene::dumpUtilizedAssets()
Con::printf("Dumping utilized assets in scene!");
Vector<StringTableEntry> utilizedAssetsList;
for (U32 i = 0; i < mPermanentObjects.size(); i++)
/*for (U32 i = 0; i < mPermanentObjects.size(); i++)
{
mPermanentObjects[i]->getUtilizedAssets(&utilizedAssetsList);
}
@ -213,7 +262,7 @@ void Scene::dumpUtilizedAssets()
for (U32 i = 0; i < mDynamicObjects.size(); i++)
{
mDynamicObjects[i]->getUtilizedAssets(&utilizedAssetsList);
}
}*/
for (U32 i = 0; i < utilizedAssetsList.size(); i++)
{
@ -247,6 +296,9 @@ StringTableEntry Scene::getLevelAsset()
bool Scene::saveScene(StringTableEntry fileName)
{
if (!isServerObject())
return false;
//So, we ultimately want to not only save out the level, but also collate all the assets utilized
//by the static objects in the scene so we can have those before we parse the level file itself
//Useful for preloading or stat tracking
@ -257,6 +309,21 @@ bool Scene::saveScene(StringTableEntry fileName)
fileName = getOriginatingFile();
}
for (SimGroupIterator itr(this); *itr; ++itr)
{
if((*itr)->isMethod("onSaving"))
{
Con::executef((*itr), "onSaving", 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 +353,12 @@ bool Scene::saveScene(StringTableEntry fileName)
dSprintf(depValue, sizeof(depValue), "%s=%s", ASSET_ID_SIGNATURE, utilizedAssetsList[i]);
levelAssetDef->setDataField(StringTable->insert(depSlotName), NULL, StringTable->insert(depValue));
}
//update the gamemode list as well
levelAssetDef->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
//Finally, save
saveSuccess = levelAssetDef->saveAsset();
return saveSuccess;
@ -314,11 +384,26 @@ void Scene::getUtilizedAssetsFromSceneObject(SimObject* object, Vector<StringTab
}
//
Vector<SceneObject*> Scene::getObjectsByClass(String className, bool checkSubscenes)
Vector<SceneObject*> Scene::getObjectsByClass(String className)
{
return Vector<SceneObject*>();
}
void Scene::loadAtPosition(const Point3F& position)
{
for (U32 i = 0; i < mSubScenes.size(); i++)
{
Box3F testBox = Box3F(0.5);
testBox.setCenter(position);
if (mSubScenes[i]->testBox(testBox))
{
mSubScenes[i]->setUnloadTimeMS(-1);
mSubScenes[i]->load();
}
}
}
DefineEngineFunction(getScene, Scene*, (U32 sceneId), (0),
"Get the root Scene object that is loaded.\n"
"@return The id of the Root Scene. Will be 0 if no root scene is loaded")
@ -413,9 +498,13 @@ DefineEngineMethod(Scene, getLevelAsset, const char*, (), ,
DefineEngineMethod(Scene, save, bool, (const char* fileName), (""),
"Save out the object to the given file.\n"
"@param fileName The name of the file to save to."
"@param selectedOnly If true, only objects marked as selected will be saved out.\n"
"@param preAppendString Text which will be preprended directly to the object serialization.\n"
"@param True on success, false on failure.")
{
return object->saveScene(StringTable->insert(fileName));
}
DefineEngineMethod(Scene, loadAtPosition, void, (Point3F position), (Point3F::Zero),
"Loads any subscenes at a given point by force.\n")
{
object->loadAtPosition(position);
}

View file

@ -1,5 +1,5 @@
#pragma once
#ifndef SCENE_H
#include "console/engineAPI.h"
#ifndef _NETOBJECT_H_
@ -9,8 +9,16 @@
#ifndef _ITICKABLE_H_
#include "core/iTickable.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "scene/sceneObject.h"
#endif
#ifndef GAME_MODE_H
#include "gameMode.h"
#endif
#ifndef SUB_SCENE_H
#include "SubScene.h"
#endif
/// Scene
/// This object is effectively a smart container to hold and manage any relevent scene objects and data
@ -19,15 +27,12 @@ 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;
Vector<SimObject*> mPermanentObjects;
Vector<SimObject*> 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;
@ -63,8 +69,8 @@ public:
void addObject(SimObject* object) override;
void removeObject(SimObject* object) override;
void addDynamicObject(SceneObject* object);
void removeDynamicObject(SceneObject* object);
void addDynamicObject(SimObject* object);
void removeDynamicObject(SimObject* object);
void clearDynamicObjects() { mDynamicObjects.clear(); }
void dumpUtilizedAssets();
@ -80,12 +86,14 @@ public:
void unpackUpdate(NetConnection *conn, BitStream *stream) override;
//
Vector<SceneObject*> getObjectsByClass(String className, bool checkSubscenes);
Vector<SceneObject*> getObjectsByClass(String className);
void getUtilizedAssetsFromSceneObject(SimObject* object, Vector<StringTableEntry>* usedAssetsList);
template <class T>
Vector<T*> getObjectsByClass(bool checkSubscenes);
Vector<T*> getObjectsByClass();
void loadAtPosition(const Point3F& position);
static Scene *getRootScene()
{
@ -96,11 +104,13 @@ public:
}
static Vector<Scene*> smSceneList;
DECLARE_CALLBACK(void, onSaving, (const char* fileName));
};
template <class T>
Vector<T*> Scene::getObjectsByClass(bool checkSubscenes)
Vector<T*> Scene::getObjectsByClass()
{
Vector<T*> foundObjects;
@ -121,18 +131,6 @@ Vector<T*> Scene::getObjectsByClass(bool checkSubscenes)
foundObjects.push_back(curObject);
}
if (checkSubscenes)
{
for (U32 i = 0; i < mSubScenes.size(); i++)
{
Vector<T*> appendList = mSubScenes[i]->getObjectsByClass<T>(true);
for (U32 a = 0; a < appendList.size(); a++)
{
foundObjects.push_back(appendList[a]);
}
}
}
return foundObjects;
}
#endif

View file

@ -0,0 +1,362 @@
#include "SceneGroup.h"
#include "gameBase/gameConnection.h"
#include "gfx/gfxDrawUtil.h"
#include "gfx/gfxTransformSaver.h"
#include "gui/editor/inspector/group.h"
#include "gui/worldEditor/editor.h"
#include "math/mathIO.h"
#include "physics/physicsShape.h"
#include "renderInstance/renderPassManager.h"
#include "scene/sceneRenderState.h"
IMPLEMENT_CO_NETOBJECT_V1(SceneGroup);
ConsoleDocClass(SceneGroup,
"@brief A collection of arbitrary objects which can be allocated and manipulated as a group.\n\n"
"%SceneGroup always points to a (.SceneGroup) file which defines its objects. In "
"fact more than one %SceneGroup can reference this file and both will update "
"if the file is modified.\n\n"
"%SceneGroup is a very simple object and only exists on the server. When it is "
"created it allocates children objects by reading the (.SceneGroup) file like "
"a list of instructions. It then sets their transform relative to the %SceneGroup "
"and Torque networking handles the rest by ghosting the new objects to clients. "
"%SceneGroup itself is not ghosted.\n\n"
"@ingroup enviroMisc"
);
SceneGroup::SceneGroup()
{
// Not ghosted unless we're editing
mNetFlags.clear(Ghostable | ScopeAlways);
mTypeMask |= StaticObjectType;
}
SceneGroup::~SceneGroup()
{
}
void SceneGroup::initPersistFields()
{
docsURL;
addGroup("SceneGroup");
endGroup("SceneGroup");
Parent::initPersistFields();
}
bool SceneGroup::onAdd()
{
if (!Parent::onAdd())
return false;
mObjBox.set(Point3F(-0.5f, -0.5f, -0.5f),
Point3F(0.5f, 0.5f, 0.5f));
resetWorldBox();
// Not added to the scene unless we are editing.
if (gEditingMission)
onEditorEnable();
addToScene();
return true;
}
void SceneGroup::onRemove()
{
removeFromScene();
Parent::onRemove();
}
void SceneGroup::onEditorEnable()
{
if (isClientObject())
return;
// Just in case we are already in the scene, lets not cause an assert.
if (mContainer != NULL)
return;
// Enable ghosting so we can see this on the client.
mNetFlags.set(Ghostable);
setScopeAlways();
addToScene();
Parent::onEditorEnable();
}
void SceneGroup::onEditorDisable()
{
if (isClientObject())
return;
// Just in case we are not in the scene, lets not cause an assert.
if (mContainer == NULL)
return;
// Do not need this on the client if we are not editing.
removeFromScene();
mNetFlags.clear(Ghostable);
clearScopeAlways();
Parent::onEditorDisable();
}
void SceneGroup::inspectPostApply()
{
Parent::inspectPostApply();
}
void SceneGroup::onInspect(GuiInspector* inspector)
{
Parent::onInspect(inspector);
//Put the SubScene group before everything that'd be SubScene-effecting, for orginazational purposes
GuiInspectorGroup* sceneGroupGrp = inspector->findExistentGroup(StringTable->insert("Editing"));
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)
{
if (isServerObject())
{
setMaskBits(TransformMask);
MatrixF newXform = mat;
MatrixF oldXform = getTransform();
oldXform.affineInverse();
MatrixF offset;
offset.mul(newXform, oldXform);
// Update all child transforms
for (SimSetIterator itr(this); *itr; ++itr)
{
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
if (child)
{
MatrixF childMat;
//add the "offset" caused by the parents change, and add it to it's own
// This is needed by objects that update their own render transform thru interpolate tick
// Mostly for stationary objects.
childMat.mul(offset, child->getTransform());
child->setTransform(childMat);
PhysicsShape* childPS = dynamic_cast<PhysicsShape*>(child);
if (childPS)
childPS->storeRestorePos();
}
}
}
Parent::setTransform(mat);
}
void SceneGroup::setRenderTransform(const MatrixF& mat)
{
MatrixF newXform = mat;
MatrixF oldXform = getRenderTransform();
oldXform.affineInverse();
MatrixF offset;
offset.mul(newXform, oldXform);
// Update all child transforms
for (SimSetIterator itr(this); *itr; ++itr)
{
SceneObject* child = dynamic_cast<SceneObject*>(*itr);
if (child)
{
MatrixF childMat;
//add the "offset" caused by the parents change, and add it to it's own
// This is needed by objects that update their own render transform thru interpolate tick
// Mostly for stationary objects.
childMat.mul(offset, child->getRenderTransform());
child->setRenderTransform(childMat);
PhysicsShape* childPS = dynamic_cast<PhysicsShape*>(child);
if (childPS)
childPS->storeRestorePos();
}
}
Parent::setRenderTransform(mat);
}
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,556 @@
#include "SubScene.h"
#include "gameMode.h"
#include "console/persistenceManager.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"
#include "T3D/gameBase/gameBase.h"
bool SubScene::smTransformChildren = false;
IMPLEMENT_CO_NETOBJECT_V1(SubScene);
S32 SubScene::mUnloadTimeoutMs = 5000;
IMPLEMENT_CALLBACK(SubScene, onLoaded, void, (), (),
"@brief Called when a subScene has been loaded and has game mode implications.\n\n");
IMPLEMENT_CALLBACK(SubScene, onUnloaded, void, (), (),
"@brief Called when a subScene has been unloaded and has game mode implications.\n\n");
SubScene::SubScene() :
mLevelAssetId(StringTable->EmptyString()),
mGameModesNames(StringTable->EmptyString()),
mScopeDistance(-1),
mLoaded(false),
mFreezeLoading(false),
mTickPeriodMS(1000),
mCurrTick(0),
mGlobalLayer(false)
{
mNetFlags.set(Ghostable | ScopeAlways);
mTypeMask |= StaticObjectType;
}
SubScene::~SubScene()
{
}
bool SubScene::onAdd()
{
if (!Parent::onAdd())
return false;
setProcessTick(true);
return true;
}
void SubScene::onRemove()
{
if (isClientObject())
removeFromScene();
unload();
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");
addGroup("LoadingManagement");
addField("freezeLoading", TypeBool, Offset(mFreezeLoading, SubScene), "If true, will prevent the zone from being changed from it's current loading state.");
addField("loadIf", TypeCommand, Offset(mLoadIf, SubScene), "evaluation condition (true/false)");
addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)");
addField("onLoadCommand", TypeCommand, Offset(mOnLoadCommand, SubScene), "The command to execute when the subscene is loaded. Maximum 1023 characters.");
addField("onUnloadCommand", TypeCommand, Offset(mOnUnloadCommand, SubScene), "The command to execute when subscene is unloaded. Maximum 1023 characters.");
endGroup("LoadingManagement");
Parent::initPersistFields();
}
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");
Con::addVariable("$SubScene::transformChildren", TypeBool, &SubScene::smTransformChildren,
"@brief If true, then transform manipulations modify child objects. If false, only triggering bounds is manipulated\n\n"
"@ingroup Editors");
}
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);
}
void SubScene::setTransform(const MatrixF& mat)
{
if(SubScene::smTransformChildren)
{
Parent::setTransform(mat);
}
else
{
SceneObject::setTransform(mat);
}
}
void SubScene::setRenderTransform(const MatrixF& mat)
{
if (SubScene::smTransformChildren)
{
Parent::setRenderTransform(mat);
}
else
{
SceneObject::setRenderTransform(mat);
}
}
bool SubScene::evaluateCondition()
{
if (!mLoadIf.isEmpty())
{
//test the mapper plugged in condition line
String resVar = getIdString() + String(".result");
Con::setBoolVariable(resVar.c_str(), false);
String command = resVar + "=" + mLoadIf + ";";
Con::evaluatef(command.c_str());
return Con::getBoolVariable(resVar.c_str());
}
return true;
}
bool SubScene::testBox(const Box3F& testBox)
{
if (mGlobalLayer)
return true;
bool passes = getWorldBox().isOverlapped(testBox);
if (passes)
passes = evaluateCondition();
return passes;
}
void SubScene::write(Stream& stream, U32 tabStop, U32 flags)
{
MutexHandle handle;
handle.lock(mMutex);
// export selected only?
if ((flags & SelectedOnly) && !isSelected())
{
for (U32 i = 0; i < size(); i++)
(*this)[i]->write(stream, tabStop, flags);
return;
}
stream.writeTabs(tabStop);
char buffer[2048];
const U32 bufferWriteLen = dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() && !(flags & NoName) ? getName() : "");
stream.write(bufferWriteLen, buffer);
writeFields(stream, tabStop + 1);
//The only meaningful difference between this and simSet for writing is we skip the children, since they're just the levelAsset contents
stream.writeTabs(tabStop);
stream.write(4, "};\r\n");
}
void SubScene::processTick(const Move* move)
{
mCurrTick += TickMs;
if (mCurrTick > mTickPeriodMS)
{
mCurrTick = 0;
//re-evaluate
if (!evaluateCondition())
unload();
}
}
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::_removeContents(SimGroupIterator set)
{
for (SimGroupIterator itr(set); *itr; ++itr)
{
SimGroup* child = dynamic_cast<SimGroup*>(*itr);
if (child)
{
_removeContents(SimGroupIterator(child));
GameBase* asGameBase = dynamic_cast<GameBase*>(child);
if (asGameBase)
{
asGameBase->scriptOnRemove();
}
Sim::cancelPendingEvents(child);
child->safeDeleteObject();
}
}
}
void SubScene::_closeFile(bool removeFileNotify)
{
AssertFatal(isServerObject(), "Trying to close out a subscene file on the client is bad!");
_removeContents(SimGroupIterator(this));
if (removeFileNotify && mLevelAsset.notNull() && mLevelAsset->getLevelPath() != StringTable->EmptyString())
{
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;
if (mFreezeLoading)
return;
_loadFile(true);
mLoaded = true;
GameMode::findGameModes(mGameModesNames, &mGameModesList);
onLoaded_callback();
for (U32 i = 0; i < mGameModesList.size(); i++)
{
mGameModesList[i]->onSubsceneLoaded_callback(this);
}
if (!mOnLoadCommand.isEmpty())
{
String command = "%this = " + String(getIdString()) + "; " + mLoadIf + ";";
Con::evaluatef(command.c_str());
}
}
void SubScene::unload()
{
if (!mLoaded)
return;
if (mFreezeLoading)
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 (SimGroupIterator itr(this); *itr; ++itr)
{
SimGroup* childGrp = dynamic_cast<SimGroup*>(*itr);
if (childGrp)
{
if (childGrp->isSelected())
{
mStartUnloadTimerMS = Sim::getCurrentTime();
return; //if a child is selected, then we don't want to unload
}
for (SimGroupIterator cldItr(childGrp); *cldItr; ++cldItr)
{
SimObject* chldChld = dynamic_cast<SimObject*>(*cldItr);
if (chldChld && chldChld->isSelected())
{
mStartUnloadTimerMS = Sim::getCurrentTime();
return; //if a child is selected, then we don't want to unload
}
}
}
}
onUnloaded_callback();
for (U32 i = 0; i < mGameModesList.size(); i++)
{
mGameModesList[i]->onSubsceneUnloaded_callback(this);
}
if (!mOnUnloadCommand.isEmpty())
{
String command = "%this = " + String(getIdString()) + "; " + mOnUnloadCommand + ";";
Con::evaluatef(command.c_str());
}
_closeFile(true);
mLoaded = false;
}
bool SubScene::save()
{
if (!isServerObject())
return false;
//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();
PersistenceManager prMger;
StringTableEntry levelPath = mLevelAsset->getLevelPath();
FileStream fs;
fs.open(levelPath, Torque::FS::File::Write);
fs.close();
for (SimGroupIterator itr(this); *itr; ++itr)
{
SimObject* childObj = (*itr);
if (!prMger.isDirty(childObj))
{
if ((*itr)->isMethod("onSaving"))
{
Con::executef((*itr), "onSaving", mLevelAssetId);
}
if (childObj->getGroup() == this)
{
prMger.setDirty((*itr), levelPath);
}
}
}
prMger.saveDirty();
//process our gameModeList and write it out to the levelAsset for metadata stashing
bool saveSuccess = false;
//Get the level asset
if (mLevelAsset.isNull())
return saveSuccess;
//update the gamemode list as well
mLevelAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames));
//Finally, save
saveSuccess = mLevelAsset->saveAsset();
return saveSuccess;
}
void SubScene::_onSelected()
{
if (!isLoaded() && isServerObject())
load();
}
void SubScene::_onUnselected()
{
}
void SubScene::prepRenderImage(SceneRenderState* state)
{
// only render if selected or render flag is set
if (/*!smRenderTriggers && */!isSelected())
return;
ObjectRenderInst* ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
ri->renderDelegate.bind(this, &SubScene::renderObject);
ri->type = RenderPassManager::RIT_Editor;
ri->translucentSort = true;
ri->defaultKey = 1;
state->getRenderPass()->addInst(ri);
}
void SubScene::renderObject(ObjectRenderInst* ri,
SceneRenderState* state,
BaseMatInstance* overrideMat)
{
if (overrideMat)
return;
GFXStateBlockDesc desc;
desc.setZReadWrite(true, false);
desc.setBlend(true);
// Trigger polyhedrons are set up with outward facing normals and CCW ordering
// so can't enable backface culling.
desc.setCullMode(GFXCullNone);
GFXTransformSaver saver;
MatrixF mat = getRenderTransform();
GFX->multWorld(mat);
GFXDrawUtil* drawer = GFX->getDrawUtil();
//Box3F scale = getScale()
//Box3F bounds = Box3F(-m)
Point3F scale = getScale();
Box3F bounds = Box3F(-scale/2, scale/2);
ColorI boundsColor = ColorI(135, 206, 235, 50);
if (mGlobalLayer)
boundsColor = ColorI(200, 100, 100, 25);
else if (mLoaded)
boundsColor = ColorI(50, 200, 50, 50);
drawer->drawCube(desc, bounds, boundsColor);
// Render wireframe.
desc.setFillModeWireframe();
drawer->drawCube(desc, bounds, ColorI::BLACK);
}
DefineEngineMethod(SubScene, save, bool, (),,
"Save out the subScene.\n")
{
return object->save();
}
DefineEngineMethod(SubScene, load, void, (), ,
"Loads the SubScene's level file.\n")
{
object->load();
}
DefineEngineMethod(SubScene, unload, void, (), ,
"Unloads the SubScene's level file.\n")
{
object->unload();
}

View file

@ -0,0 +1,126 @@
#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
class GameMode;
class SubScene : public SceneGroup
{
typedef SceneGroup Parent;
public:
enum MaskBits
{
NextFreeMask = Parent::NextFreeMask << 0
};
void onLevelChanged() {}
protected:
static bool smTransformChildren;
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 mFreezeLoading;
String mLoadIf;
String mOnLoadCommand;
String mOnUnloadCommand;
S32 mTickPeriodMS;
U32 mCurrTick;
bool mGlobalLayer;
public:
SubScene();
virtual ~SubScene();
DECLARE_CONOBJECT(SubScene);
DECLARE_CATEGORY("Object \t Collection");
static void initPersistFields();
static void consoleInit();
StringTableEntry getTypeHint() const override { return (getLevelAsset()) ? getLevelAsset()->getAssetName() : StringTable->EmptyString(); }
// 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;
void setTransform(const MatrixF& mat) override;
void setRenderTransform(const MatrixF& mat) override;
bool testBox(const Box3F& testBox);
bool evaluateCondition();
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 _removeContents(SimGroupIterator);
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_CALLBACK(void, onLoaded, ());
DECLARE_CALLBACK(void, onUnloaded, ());
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

@ -317,10 +317,10 @@ void ConvexShape::initPersistFields()
addGroup( "Internal" );
addProtectedField( "surface", TypeRealString, 0, &protectedSetSurface, &defaultProtectedGetFn,
"Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors );
"Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors | AbstractClassRep::FIELD_SpecialtyArrayField);
addProtectedField( "surfaceTexture", TypeRealString, 0, &protectedSetSurfaceTexture, &defaultProtectedGetFn,
"Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors );
"Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors | AbstractClassRep::FIELD_SpecialtyArrayField);
endGroup( "Internal" );
@ -498,6 +498,57 @@ bool ConvexShape::writeField( StringTableEntry fieldname, const char *value )
return Parent::writeField( fieldname, value );
}
U32 ConvexShape::getSpecialFieldSize(StringTableEntry fieldName)
{
if (fieldName == StringTable->insert("surface") || fieldName == StringTable->insert("surfaceTexture"))
{
return mSurfaces.size();
}
return 0;
}
const char* ConvexShape::getSpecialFieldOut(StringTableEntry fieldName, const U32& index)
{
if (index >= smMaxSurfaces)
return NULL;
if (fieldName == StringTable->insert("surface"))
{
if(index >= mSurfaces.size())
return NULL;
const MatrixF& mat = mSurfaces[index];
QuatF quat(mat);
Point3F pos(mat.getPosition());
char buffer[1024];
dMemset(buffer, 0, 1024);
dSprintf(buffer, 1024, "%g %g %g %g %g %g %g %i %g %g %g %g %g %i %i",
quat.x, quat.y, quat.z, quat.w, pos.x, pos.y, pos.z, mSurfaceUVs[index].matID,
mSurfaceUVs[index].offset.x, mSurfaceUVs[index].offset.y, mSurfaceUVs[index].scale.x,
mSurfaceUVs[index].scale.y, mSurfaceUVs[index].zRot, mSurfaceUVs[index].horzFlip, mSurfaceUVs[index].vertFlip);
return StringTable->insert(buffer);
}
else if (fieldName == StringTable->insert("surfaceTexture"))
{
if (index >= mSurfaceTextures.size())
return NULL;
char buffer[1024];
dMemset(buffer, 0, 1024);
dSprintf(buffer, 1024, "%s", mSurfaceTextures[index].getMaterial());
return StringTable->insert(buffer);
}
return NULL;
}
void ConvexShape::onScaleChanged()
{
if ( isProperlyAdded() )

View file

@ -83,7 +83,7 @@ class ConvexShape : public SceneObject
typedef SceneObject Parent;
friend class GuiConvexEditorCtrl;
friend class GuiConvexEditorUndoAction;
friend class ConvexShapeCollisionConvex;
friend class ConvexShapeCollisionConvex;
public:
@ -113,10 +113,10 @@ public:
U32 p1;
U32 p2;
U32 operator []( U32 index ) const
U32 operator [](U32 index) const
{
AssertFatal( index >= 0 && index <= 2, "index out of range" );
return *( (&p0) + index );
AssertFatal(index >= 0 && index <= 2, "index out of range");
return *((&p0) + index);
}
};
@ -126,23 +126,23 @@ public:
Vector< U32 > points;
Vector< U32 > winding;
Vector< Point2F > texcoords;
Vector< Triangle > triangles;
Vector< Triangle > triangles;
Point3F tangent;
Point3F normal;
Point3F centroid;
F32 area;
S32 id;
};
};
struct surfaceMaterial
{
// The name of the Material we will use for rendering
DECLARE_MATERIALASSET(surfaceMaterial, Material);
DECLARE_ASSET_SETGET(surfaceMaterial, Material);
// The actual Material instance
BaseMatInstance* materialInst;
BaseMatInstance* materialInst;
surfaceMaterial()
{
@ -174,26 +174,26 @@ public:
U32 mPrimCount;
};
struct Geometry
{
void generate(const Vector< PlaneF > &planes, const Vector< Point3F > &tangents, const Vector< surfaceMaterial > surfaceTextures, const Vector< Point2F > texOffset, const Vector< Point2F > texScale, const Vector< bool > horzFlip, const Vector< bool > vertFlip);
struct Geometry
{
void generate(const Vector< PlaneF >& planes, const Vector< Point3F >& tangents, const Vector< surfaceMaterial > surfaceTextures, const Vector< Point2F > texOffset, const Vector< Point2F > texScale, const Vector< bool > horzFlip, const Vector< bool > vertFlip);
Vector< Point3F > points;
Vector< Face > faces;
};
Vector< Point3F > points;
Vector< Face > faces;
};
static bool smRenderEdges;
static bool smRenderEdges;
// To prevent bitpack overflows.
// This is only indirectly enforced by trucation when serializing.
static const S32 smMaxSurfaces = 100;
public:
ConvexShape();
virtual ~ConvexShape();
DECLARE_CONOBJECT( ConvexShape );
DECLARE_CONOBJECT(ConvexShape);
DECLARE_CATEGORY("Object \t Simple");
// ConsoleObject
@ -203,73 +203,76 @@ public:
void inspectPostApply() override;
bool onAdd() override;
void onRemove() override;
void writeFields(Stream &stream, U32 tabStop) override;
bool writeField( StringTableEntry fieldname, const char *value ) override;
void writeFields(Stream& stream, U32 tabStop) override;
bool writeField(StringTableEntry fieldname, const char* value) override;
U32 getSpecialFieldSize(StringTableEntry fieldName) override;
const char* getSpecialFieldOut(StringTableEntry fieldName, const U32& index) override;
// NetObject
U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) override;
void unpackUpdate( NetConnection *conn, BitStream *stream ) override;
U32 packUpdate(NetConnection* conn, U32 mask, BitStream* stream) override;
void unpackUpdate(NetConnection* conn, BitStream* stream) override;
// SceneObject
void onScaleChanged() override;
void setTransform( const MatrixF &mat ) override;
void prepRenderImage( SceneRenderState *state ) override;
void buildConvex( const Box3F &box, Convex *convex ) override;
bool buildPolyList( PolyListContext context, AbstractPolyList *polyList, const Box3F &box, const SphereF &sphere ) override;
bool buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F &box, const SphereF &) override;
bool castRay( const Point3F &start, const Point3F &end, RayInfo *info ) override;
bool collideBox( const Point3F &start, const Point3F &end, RayInfo *info ) override;
void setTransform(const MatrixF& mat) override;
void prepRenderImage(SceneRenderState* state) override;
void buildConvex(const Box3F& box, Convex* convex) override;
bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere) override;
bool buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F& box, const SphereF&) override;
bool castRay(const Point3F& start, const Point3F& end, RayInfo* info) override;
bool collideBox(const Point3F& start, const Point3F& end, RayInfo* info) override;
void updateBounds( bool recenter );
void updateBounds(bool recenter);
void recenter();
/// Geometry access.
/// @{
MatrixF getSurfaceWorldMat( S32 faceid, bool scaled = false ) const;
void cullEmptyPlanes( Vector< U32 > *removedPlanes );
void exportToCollada();
void resizePlanes( const Point3F &size );
void getSurfaceLineList( S32 surfId, Vector< Point3F > &lineList );
Geometry& getGeometry() { return mGeometry; }
Vector<MatrixF>& getSurfaces() { return mSurfaces; }
void getSurfaceTriangles( S32 surfId, Vector< Point3F > *outPoints, Vector< Point2F > *outCoords, bool worldSpace );
MatrixF getSurfaceWorldMat(S32 faceid, bool scaled = false) const;
void cullEmptyPlanes(Vector< U32 >* removedPlanes);
void exportToCollada();
void resizePlanes(const Point3F& size);
void getSurfaceLineList(S32 surfId, Vector< Point3F >& lineList);
Geometry& getGeometry() { return mGeometry; }
Vector<MatrixF>& getSurfaces() { return mSurfaces; }
void getSurfaceTriangles(S32 surfId, Vector< Point3F >* outPoints, Vector< Point2F >* outCoords, bool worldSpace);
/// @}
/// Geometry Visualization.
/// @{
void renderFaceEdges( S32 faceid, const ColorI &color = ColorI::WHITE, F32 lineWidth = 1.0f );
void renderFaceEdges(S32 faceid, const ColorI& color = ColorI::WHITE, F32 lineWidth = 1.0f);
/// @}
String getMaterialName() { return mMaterialName; }
String getMaterialName() { return mMaterialName; }
protected:
void _updateMaterial();
void _updateGeometry( bool updateCollision = false );
void _updateGeometry(bool updateCollision = false);
void _updateCollision();
void _export( OptimizedPolyList *plist, const Box3F &box, const SphereF &sphere );
void _export(OptimizedPolyList* plist, const Box3F& box, const SphereF& sphere);
void _renderDebug( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mat );
void _renderDebug(ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* mat);
static S32 QSORT_CALLBACK _comparePlaneDist( const void *a, const void *b );
static S32 QSORT_CALLBACK _comparePlaneDist(const void* a, const void* b);
static bool protectedSetSurface( void *object, const char *index, const char *data );
static bool protectedSetSurface(void* object, const char* index, const char* data);
static bool protectedSetSurfaceTexture(void* object, const char* index, const char* data);
static bool protectedSetSurfaceUV(void* object, const char* index, const char* data);
static bool protectedSetSurfaceTexture( void *object, const char *index, const char *data );
static bool protectedSetSurfaceUV(void *object, const char *index, const char *data);
protected:
DECLARE_MATERIALASSET(ConvexShape, Material);
DECLARE_ASSET_SETGET(ConvexShape, Material);
// The actual Material instance
BaseMatInstance* mMaterialInst;
BaseMatInstance* mMaterialInst;
// The GFX vertex and primitive buffers
/*GFXVertexBufferHandle< VertexType > mVertexBuffer;
@ -278,7 +281,7 @@ protected:
U32 mVertCount;
U32 mPrimCount;*/
Geometry mGeometry;
Geometry mGeometry;
Vector< PlaneF > mPlanes;
@ -291,14 +294,14 @@ protected:
Vector< surfaceUV > mSurfaceUVs;
Vector< surfaceBuffers > mSurfaceBuffers;
Convex *mConvexList;
Convex* mConvexList;
PhysicsBody *mPhysicsRep;
PhysicsBody* mPhysicsRep;
/// Geometry visualization
/// @{
F32 mNormalLength;
F32 mNormalLength;
/// @}

View file

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

View file

@ -0,0 +1,117 @@
#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
#ifndef SUB_SCENE_H
#include "SubScene.h"
#endif
#include "T3D/assets/ImageAsset.h"
class GameMode : public SimObject
{
typedef SimObject Parent;
private:
StringTableEntry mGameModeName;
StringTableEntry mGameModeDesc;
DECLARE_IMAGEASSET(GameMode, PreviewImage, previewChange, GFXStaticTextureSRGBProfile);
DECLARE_ASSET_SETGET(GameMode, PreviewImage);
bool mIsActive;
bool mIsAlwaysActive;
public:
GameMode();
~GameMode() = default;
static void initPersistFields();
bool onAdd() override;
void onRemove() override;
bool isActive() { return mIsActive; }
void setActive(const bool& active);
bool isAlwaysActive() { return mIsAlwaysActive; }
void setAlwaysActive(const bool& alwaysActive);
DECLARE_CONOBJECT(GameMode);
static void findGameModes(const char* gameModeList, Vector<GameMode*>* outGameModes);
void previewChange() {}
DECLARE_CALLBACK(void, onActivated, ());
DECLARE_CALLBACK(void, onDeactivated, ());
DECLARE_CALLBACK(void, onSceneLoaded, ());
DECLARE_CALLBACK(void, onSceneUnloaded, ());
DECLARE_CALLBACK(void, onSubsceneLoaded, (SubScene*));
DECLARE_CALLBACK(void, onSubsceneUnloaded, (SubScene*));
};
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

@ -371,6 +371,12 @@ void Prefab::_loadFile( bool addFileNotify )
return;
}
SimObjectPtr<Scene> rootScene = Scene::getRootScene();
if(rootScene.isValid())
{
rootScene->addDynamicObject(group);
}
if ( addFileNotify )
Torque::FS::AddChangeNotification( mFilename, this, &Prefab::_onFileChanged );

View file

@ -173,7 +173,7 @@ Trigger::Trigger()
mPhysicsRep = NULL;
mTripOnce = false;
mTrippedBy = 0xFFFFFFFF;
mTripCondition = "";
mTripIf = "";
//Default up a basic square
Point3F vecs[3] = { Point3F(1.0, 0.0, 0.0),
@ -379,7 +379,7 @@ void Trigger::initPersistFields()
"representing the edges extending from the corner.\n");
addField("TripOnce", TypeBool, Offset(mTripOnce, Trigger),"Do we trigger callacks just the once?");
addField("TripCondition", TypeRealString, Offset(mTripCondition, Trigger),"evaluation condition to trip callbacks (true/false)");
addField("tripIf", TypeRealString, Offset(mTripIf, Trigger),"evaluation condition to trip callbacks (true/false)");
addField("TrippedBy", TypeGameTypeMasksType, Offset(mTrippedBy, Trigger), "typemask filter");
addProtectedField("enterCommand", TypeCommand, Offset(mEnterCommand, Trigger), &setEnterCmd, &defaultProtectedGetFn,
"The command to execute when an object enters this trigger. Object id stored in %%obj. Maximum 1023 characters." );
@ -697,13 +697,13 @@ bool Trigger::testTrippable()
bool Trigger::testCondition()
{
if (mTripCondition.isEmpty())
if (mTripIf.isEmpty())
return true; //we've got no tests to run so just do it
//test the mapper plugged in condition line
String resVar = getIdString() + String(".result");
Con::setBoolVariable(resVar.c_str(), false);
String command = resVar + "=" + mTripCondition + ";";
String command = resVar + "=" + mTripIf + ";";
Con::evaluatef(command.c_str());
if (Con::getBoolVariable(resVar.c_str()) == 1)
{

View file

@ -87,7 +87,7 @@ class Trigger : public GameBase
bool mTripped;
S32 mTrippedBy;
String mTripCondition;
String mTripIf;
String mEnterCommand;
String mLeaveCommand;
String mTickCommand;

View file

@ -498,6 +498,7 @@ public:
FIELD_ComponentInspectors = BIT(1), ///< Custom fields used by components. They are likely to be non-standard size/configuration, so
///< They are handled specially
FIELD_CustomInspectors = BIT(2), ///< Display as a button in inspectors.
FIELD_SpecialtyArrayField = BIT(3)
};
struct Field

View file

@ -652,6 +652,31 @@ S32 PersistenceManager::getPropertyIndex(ParsedObject* parsedObject, const char*
return propertyIndex;
}
S32 PersistenceManager::getSpecialPropertyAtOffset(ParsedObject* parsedObject, const char* fieldName, U32 offsetPos)
{
S32 propertyIndex = -1;
if (!parsedObject)
return propertyIndex;
U32 hitCount = -1;
for (U32 i = 0; i < parsedObject->properties.size(); i++)
{
if (dStricmp(fieldName, parsedObject->properties[i].name) == 0)
{
hitCount++;
if (hitCount == offsetPos)
{
propertyIndex = i;
break;
}
}
}
return propertyIndex;
}
char* PersistenceManager::getObjectIndent(ParsedObject* object)
{
char* indent = Con::getReturnBuffer(2048);
@ -1361,166 +1386,335 @@ void PersistenceManager::updateObject(SimObject* object, ParsedObject* parentObj
if ( f->type >= AbstractClassRep::ARCFirstCustomField || f->flag.test(AbstractClassRep::FieldFlags::FIELD_ComponentInspectors))
continue;
for(U32 j = 0; S32(j) < f->elementCount; j++)
if (f->flag.test(AbstractClassRep::FIELD_SpecialtyArrayField))
{
const char* value = getFieldValue(object, f->pFieldname, j);
U32 fieldArraySize = object->getSpecialFieldSize(f->pFieldname);
// Make sure we got a value
if (!value)
continue;
// Let's see if this field is already in the file
S32 propertyIndex = getPropertyIndex(parsedObject, f->pFieldname, j);
if (propertyIndex > -1)
for(U32 j = 0; j < fieldArraySize; j++)
{
ParsedProperty& prop = parsedObject->properties[propertyIndex];
const char* value = object->getSpecialFieldOut(f->pFieldname, j);
// If this field is on the remove list then remove it and continue
if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value))
{
removeField( parsedObject->properties[ propertyIndex ] );
dFree( value );
// Make sure we got a value
if (!value)
continue;
}
// Run the parsed value through the console system conditioners so
// that it will better match the data we got back from the object.
const char* evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag);
// Let's see if this field is already in the file
S32 propertyIndex = getSpecialPropertyAtOffset(parsedObject, f->pFieldname, j);
// If our data doesn't match then we get to update it.
//
// As for copy-sources, we just assume here that if a property setting
// is there in the file, the user does not want it inherited from the copy-source
// even in the case the actual values are identical.
if( dStricmp(value, evalue) != 0 )
if (propertyIndex > -1)
{
if( value[ 0 ] == '\0' &&
dStricmp( getFieldValue( defaultObject, f->pFieldname, j ), value ) == 0 &&
( !object->getCopySource() || dStricmp( getFieldValue( object->getCopySource(), f->pFieldname, j ), value ) == 0 ) )
ParsedProperty& prop = parsedObject->properties[propertyIndex];
// If this field is on the remove list then remove it and continue
if (findRemoveField(object, f->pFieldname, j))
{
removeField( prop );
removeField(parsedObject->properties[propertyIndex]);
dFree(value);
continue;
}
// Run the parsed value through the console system conditioners so
// that it will better match the data we got back from the object.
const char* evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag);
// If our data doesn't match then we get to update it.
//
// As for copy-sources, we just assume here that if a property setting
// is there in the file, the user does not want it inherited from the copy-source
// even in the case the actual values are identical.
if (dStricmp(value, evalue) != 0)
{
if (value[0] == '\0' &&
dStricmp(getFieldValue(defaultObject, f->pFieldname, j), value) == 0 &&
(!object->getCopySource() || dStricmp(getFieldValue(object->getCopySource(), f->pFieldname, j), value) == 0))
{
removeField(prop);
}
else
{
// TODO: This should be wrapped in a helper method... probably.
// Detect and collapse relative path information
if (f->type == TypeFilename ||
f->type == TypeStringFilename ||
f->type == TypeImageFilename ||
f->type == TypePrefabFilename ||
f->type == TypeShapeFilename ||
f->type == TypeSoundFilename)
{
char fnBuf[1024];
Con::collapseScriptFilename(fnBuf, 1024, value);
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true);
}
else if (f->type == TypeCommand || f->type == TypeString || f->type == TypeRealString)
{
char cmdBuf[1024];
expandEscape(cmdBuf, value);
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, cmdBuf, true);
}
else
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true);
}
}
}
else
{
// No need to process a removed field that doesn't exist in the file
if (findRemoveField(object, f->pFieldname, j))
{
dFree(value);
continue;
}
bool mustUpdate = false;
// If we didn't find the property in the ParsedObject
// then we need to compare against the default value
// for this property and save it out if it is different
const char* defaultValue = defaultObject->getSpecialFieldOut(f->pFieldname, j);
if (!defaultValue || dStricmp(value, defaultValue) != 0)
{
// Value differs. Check whether it also differs from the
// value in the copy source if there is one.
if (object->getCopySource())
{
const char* copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
if (!copySourceValue || dStricmp(copySourceValue, value) != 0)
mustUpdate = true;
if (copySourceValue)
dFree(copySourceValue);
}
else
mustUpdate = true;
}
else
{
// Value does not differ. If it differs from the copy source's value,
// though, we still want to write it out as otherwise we'll see the
// copy source's value override us.
if (object->getCopySource())
{
const char* copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
if (copySourceValue && dStricmp(copySourceValue, value) != 0)
mustUpdate = true;
if (copySourceValue)
dFree(copySourceValue);
}
}
// The default value for most string type fields is
// NULL so we can't just continue here or we'd never ever
// write them out...
//
//if (!defaultValue)
// continue;
// If the object's value is different from the default
// value then add it to the ParsedObject's newLines
if (mustUpdate)
{
// TODO: This should be wrapped in a helper method... probably.
// Detect and collapse relative path information
if (f->type == TypeFilename ||
f->type == TypeStringFilename ||
f->type == TypeImageFilename ||
f->type == TypePrefabFilename ||
f->type == TypeShapeFilename ||
f->type == TypeSoundFilename )
if (f->type == TypeFilename ||
f->type == TypeStringFilename ||
f->type == TypeImageFilename ||
f->type == TypePrefabFilename ||
f->type == TypeShapeFilename ||
f->type == TypeSoundFilename)
{
char fnBuf[1024];
Con::collapseScriptFilename(fnBuf, 1024, value);
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true);
newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j));
}
else if (f->type == TypeCommand || f->type == TypeString || f->type == TypeRealString)
else if (f->type == TypeCommand)
{
char cmdBuf[1024];
expandEscape(cmdBuf, value);
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, cmdBuf, true);
newLines.push_back(createNewProperty(f->pFieldname, cmdBuf, f->elementCount > 1, j));
}
else
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true);
newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j));
}
if (defaultValue)
dFree(defaultValue);
}
//dFree(value);
}
else
}
else
{
for (U32 j = 0; S32(j) < f->elementCount; j++)
{
// No need to process a removed field that doesn't exist in the file
if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value))
{
dFree( value );
const char* value = getFieldValue(object, f->pFieldname, j);
// Make sure we got a value
if (!value)
continue;
}
bool mustUpdate = false;
// If we didn't find the property in the ParsedObject
// then we need to compare against the default value
// for this property and save it out if it is different
// Let's see if this field is already in the file
S32 propertyIndex = getPropertyIndex(parsedObject, f->pFieldname, j);
const char* defaultValue = getFieldValue(defaultObject, f->pFieldname, j);
if( !defaultValue || dStricmp( value, defaultValue ) != 0 )
if (propertyIndex > -1)
{
// Value differs. Check whether it also differs from the
// value in the copy source if there is one.
if( object->getCopySource() )
ParsedProperty& prop = parsedObject->properties[propertyIndex];
// If this field is on the remove list then remove it and continue
if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value))
{
const char* copySourceValue = getFieldValue( object->getCopySource(), f->pFieldname, j );
if( !copySourceValue || dStricmp( copySourceValue, value ) != 0 )
mustUpdate = true;
if( copySourceValue )
dFree( copySourceValue );
removeField(parsedObject->properties[propertyIndex]);
dFree(value);
continue;
}
// Run the parsed value through the console system conditioners so
// that it will better match the data we got back from the object.
const char* evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag);
// If our data doesn't match then we get to update it.
//
// As for copy-sources, we just assume here that if a property setting
// is there in the file, the user does not want it inherited from the copy-source
// even in the case the actual values are identical.
if (dStricmp(value, evalue) != 0)
{
if (value[0] == '\0' &&
dStricmp(getFieldValue(defaultObject, f->pFieldname, j), value) == 0 &&
(!object->getCopySource() || dStricmp(getFieldValue(object->getCopySource(), f->pFieldname, j), value) == 0))
{
removeField(prop);
}
else
{
// TODO: This should be wrapped in a helper method... probably.
// Detect and collapse relative path information
if (f->type == TypeFilename ||
f->type == TypeStringFilename ||
f->type == TypeImageFilename ||
f->type == TypePrefabFilename ||
f->type == TypeShapeFilename ||
f->type == TypeSoundFilename)
{
char fnBuf[1024];
Con::collapseScriptFilename(fnBuf, 1024, value);
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true);
}
else if (f->type == TypeCommand || f->type == TypeString || f->type == TypeRealString)
{
char cmdBuf[1024];
expandEscape(cmdBuf, value);
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, cmdBuf, true);
}
else
updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true);
}
}
else
mustUpdate = true;
}
else
{
// Value does not differ. If it differs from the copy source's value,
// though, we still want to write it out as otherwise we'll see the
// copy source's value override us.
if( object->getCopySource() )
// No need to process a removed field that doesn't exist in the file
if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value))
{
const char* copySourceValue = getFieldValue( object->getCopySource(), f->pFieldname, j );
if( copySourceValue && dStricmp( copySourceValue, value ) != 0 )
dFree(value);
continue;
}
bool mustUpdate = false;
// If we didn't find the property in the ParsedObject
// then we need to compare against the default value
// for this property and save it out if it is different
const char* defaultValue = getFieldValue(defaultObject, f->pFieldname, j);
if (!defaultValue || dStricmp(value, defaultValue) != 0)
{
// Value differs. Check whether it also differs from the
// value in the copy source if there is one.
if (object->getCopySource())
{
const char* copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
if (!copySourceValue || dStricmp(copySourceValue, value) != 0)
mustUpdate = true;
if (copySourceValue)
dFree(copySourceValue);
}
else
mustUpdate = true;
if( copySourceValue )
dFree( copySourceValue );
}
}
// The default value for most string type fields is
// NULL so we can't just continue here or we'd never ever
// write them out...
//
//if (!defaultValue)
// continue;
// If the object's value is different from the default
// value then add it to the ParsedObject's newLines
if ( mustUpdate )
{
// TODO: This should be wrapped in a helper method... probably.
// Detect and collapse relative path information
if (f->type == TypeFilename ||
f->type == TypeStringFilename ||
f->type == TypeImageFilename ||
f->type == TypePrefabFilename ||
f->type == TypeShapeFilename ||
f->type == TypeSoundFilename )
{
char fnBuf[1024];
Con::collapseScriptFilename(fnBuf, 1024, value);
newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j));
}
else if (f->type == TypeCommand)
{
char cmdBuf[1024];
expandEscape(cmdBuf, value);
newLines.push_back(createNewProperty(f->pFieldname, cmdBuf, f->elementCount > 1, j));
}
else
newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j));
{
// Value does not differ. If it differs from the copy source's value,
// though, we still want to write it out as otherwise we'll see the
// copy source's value override us.
if (object->getCopySource())
{
const char* copySourceValue = getFieldValue(object->getCopySource(), f->pFieldname, j);
if (copySourceValue && dStricmp(copySourceValue, value) != 0)
mustUpdate = true;
if (copySourceValue)
dFree(copySourceValue);
}
}
// The default value for most string type fields is
// NULL so we can't just continue here or we'd never ever
// write them out...
//
//if (!defaultValue)
// continue;
// If the object's value is different from the default
// value then add it to the ParsedObject's newLines
if (mustUpdate)
{
// TODO: This should be wrapped in a helper method... probably.
// Detect and collapse relative path information
if (f->type == TypeFilename ||
f->type == TypeStringFilename ||
f->type == TypeImageFilename ||
f->type == TypePrefabFilename ||
f->type == TypeShapeFilename ||
f->type == TypeSoundFilename)
{
char fnBuf[1024];
Con::collapseScriptFilename(fnBuf, 1024, value);
newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j));
}
else if (f->type == TypeCommand)
{
char cmdBuf[1024];
expandEscape(cmdBuf, value);
newLines.push_back(createNewProperty(f->pFieldname, cmdBuf, f->elementCount > 1, j));
}
else
newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j));
}
if (defaultValue)
dFree(defaultValue);
}
if (defaultValue)
dFree( defaultValue );
dFree(value);
}
dFree( value );
}
}

View file

@ -221,6 +221,12 @@ protected:
// Attempts to look up the property in the ParsedObject
S32 getPropertyIndex(ParsedObject* parsedObject, const char* fieldName, U32 arrayPos = 0);
// Attempts to look up the special array property in the ParsedObject
// This is distinct from getPropertyIndex because while that assumes it's an array'd field
// This figures the property in question is one that is specially tagged for implicit, arbitrarily sized lists
// Like ConvexShape's 'surfaces' or a spline's 'node' properties
S32 getSpecialPropertyAtOffset(ParsedObject* parsedObject, const char* fieldName, U32 offsetPos);
// Gets the amount of indent on the ParsedObject.
char* getObjectIndent(ParsedObject* object);
@ -320,4 +326,4 @@ public:
DECLARE_CONOBJECT(PersistenceManager);
};
#endif
#endif

View file

@ -101,6 +101,8 @@ SimObjectId SimObject::smForcedId = 0;
bool SimObject::preventNameChanging = false;
IMPLEMENT_CALLBACK(SimObject, onInspectPostApply, void, (SimObject* obj), (obj), "Generic callback for when an object is edited");
IMPLEMENT_CALLBACK(SimObject, onSelected, void, (SimObject* obj), (obj), "Generic callback for when an object is selected");
IMPLEMENT_CALLBACK(SimObject, onUnselected, void, (SimObject* obj), (obj), "Generic callback for when an object is un-selected");
namespace Sim
{
@ -527,6 +529,14 @@ bool SimObject::save(const char *pcFileName, bool bOnlySelected, const char *pre
}
bool SimObject::saveAppend(const char* pcFileName, bool bOnlySelected, const char* preappend)
{
return true;
}
//-----------------------------------------------------------------------------
SimPersistID* SimObject::getOrCreatePersistentId()
@ -2207,11 +2217,13 @@ void SimObject::setSelected( bool sel )
{
mFlags.set( Selected );
_onSelected();
onSelected_callback(this);
}
else
{
mFlags.clear( Selected );
_onUnselected();
onUnselected_callback(this);
}
}

View file

@ -530,6 +530,9 @@ class SimObject: public ConsoleObject, public TamlCallbacks
void setDataFieldType(const U32 fieldTypeId, StringTableEntry slotName, const char *array);
void setDataFieldType(const char *typeName, StringTableEntry slotName, const char *array);
virtual U32 getSpecialFieldSize(StringTableEntry fieldName) { return 0; }
virtual const char* getSpecialFieldOut(StringTableEntry fieldName, const U32& index) { return NULL; }
/// Get reference to the dictionary containing dynamic fields.
///
/// See @ref simobject_console "here" for a detailed discussion of what this
@ -579,6 +582,7 @@ class SimObject: public ConsoleObject, public TamlCallbacks
/// Save object as a TorqueScript File.
virtual bool save( const char* pcFilePath, bool bOnlySelected = false, const char *preappend = NULL );
virtual bool saveAppend(const char* pcFilePath, bool bOnlySelected = false, const char* preappend = NULL);
/// Check if a method exists in the objects current namespace.
virtual bool isMethod( const char* methodName );
@ -981,6 +985,8 @@ class SimObject: public ConsoleObject, public TamlCallbacks
DECLARE_CONOBJECT( SimObject );
DECLARE_CALLBACK(void, onInspectPostApply, (SimObject* obj));
DECLARE_CALLBACK(void, onSelected, (SimObject* obj));
DECLARE_CALLBACK(void, onUnselected, (SimObject* obj));
static SimObject* __findObject( const char* id ) { return Sim::findObject( id ); }
static const char* __getObjectId( ConsoleObject* object )

View file

@ -322,7 +322,7 @@ void DecalRoad::initPersistFields()
addGroup( "Internal" );
addProtectedField( "node", TypeString, 0, &addNodeFromField, &emptyStringProtectedGetFn,
"Do not modify, for internal use." );
"Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors | AbstractClassRep::FIELD_SpecialtyArrayField);
endGroup( "Internal" );
@ -473,6 +473,36 @@ bool DecalRoad::writeField( StringTableEntry fieldname, const char *value )
return Parent::writeField( fieldname, value );
}
U32 DecalRoad::getSpecialFieldSize(StringTableEntry fieldName)
{
if (fieldName == StringTable->insert("node"))
{
return mNodes.size();
}
return 0;
}
const char* DecalRoad::getSpecialFieldOut(StringTableEntry fieldName, const U32& index)
{
if (fieldName == StringTable->insert("node"))
{
if (index >= mNodes.size())
return NULL;
const RoadNode& node = mNodes[index];
char buffer[1024];
dMemset(buffer, 0, 1024);
dSprintf(buffer, 1024, "%f %f %f %f", node.point.x, node.point.y, node.point.z, node.width);
return StringTable->insert(buffer);
}
return NULL;
}
void DecalRoad::onEditorEnable()
{
}

View file

@ -169,6 +169,9 @@ public:
void onStaticModified(const char* slotName, const char*newValue = NULL) override;
void writeFields(Stream &stream, U32 tabStop) override;
bool writeField( StringTableEntry fieldname, const char *value ) override;
U32 getSpecialFieldSize(StringTableEntry fieldName) override;
const char* getSpecialFieldOut(StringTableEntry fieldName, const U32& index) override;
// NetObject
U32 packUpdate(NetConnection *, U32, BitStream *) override;

View file

@ -956,10 +956,10 @@ void MeshRoad::initPersistFields()
addGroup( "Internal" );
addProtectedField( "Node", TypeString, 0, &addNodeFromField, &emptyStringProtectedGetFn,
"Do not modify, for internal use." );
"Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors | AbstractClassRep::FIELD_SpecialtyArrayField);
addProtectedField( "ProfileNode", TypeString, 0, &addProfileNodeFromField, &emptyStringProtectedGetFn,
"Do not modify, for internal use." );
"Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors | AbstractClassRep::FIELD_SpecialtyArrayField);
endGroup( "Internal" );
@ -1168,6 +1168,60 @@ bool MeshRoad::writeField( StringTableEntry fieldname, const char *value )
return Parent::writeField( fieldname, value );
}
U32 MeshRoad::getSpecialFieldSize(StringTableEntry fieldName)
{
if (fieldName == StringTable->insert("Node"))
{
return mNodes.size();
}
else if (fieldName == StringTable->insert("ProfileNode"))
{
return mSideProfile.mNodes.size();
}
return 0;
}
const char* MeshRoad::getSpecialFieldOut(StringTableEntry fieldName, const U32& index)
{
if (fieldName == StringTable->insert("Node"))
{
if (index >= mNodes.size())
return NULL;
const MeshRoadNode& node = mNodes[index];
char buffer[1024];
dMemset(buffer, 0, 1024);
dSprintf(buffer, 1024, "Node = \"%g %g %g %g %g %g %g %g\";", node.point.x, node.point.y, node.point.z, node.width, node.depth, node.normal.x, node.normal.y, node.normal.z);
return StringTable->insert(buffer);
}
else if (fieldName == StringTable->insert("ProfileNode"))
{
Point3F nodePos = mSideProfile.mNodes[index].getPosition();
U8 smooth, mtrl;
if (index)
mtrl = mSideProfile.mSegMtrls[index - 1];
else
mtrl = 0;
if (mSideProfile.mNodes[index].isSmooth())
smooth = 1;
else
smooth = 0;
char buffer[1024];
dMemset(buffer, 0, 1024);
dSprintf(buffer, 1024, "ProfileNode = \"%.6f %.6f %d %d\";", nodePos.x, nodePos.y, smooth, mtrl);
return StringTable->insert(buffer);
}
return NULL;
}
void MeshRoad::onEditorEnable()
{
}

View file

@ -525,6 +525,9 @@ public:
void writeFields(Stream &stream, U32 tabStop) override;
bool writeField( StringTableEntry fieldname, const char *value ) override;
U32 getSpecialFieldSize(StringTableEntry fieldName) override;
const char* getSpecialFieldOut(StringTableEntry fieldName, const U32& index) override;
// NetObject
U32 packUpdate(NetConnection *, U32, BitStream *) override;
void unpackUpdate(NetConnection *, BitStream *) override;

View file

@ -648,7 +648,8 @@ void River::initPersistFields()
addGroup( "Internal" );
addProtectedField( "Node", TypeString, 0, &addNodeFromField, &emptyStringProtectedGetFn, "For internal use, do not modify." );
addProtectedField( "Node", TypeString, 0, &addNodeFromField, &emptyStringProtectedGetFn, "For internal use, do not modify.",
AbstractClassRep::FIELD_HideInInspectors | AbstractClassRep::FIELD_SpecialtyArrayField);
endGroup( "Internal" );
@ -785,6 +786,38 @@ bool River::writeField( StringTableEntry fieldname, const char *value )
return Parent::writeField( fieldname, value );
}
U32 River::getSpecialFieldSize(StringTableEntry fieldName)
{
if (fieldName == StringTable->insert("node"))
{
return mNodes.size();
}
return 0;
}
const char* River::getSpecialFieldOut(StringTableEntry fieldName, const U32& index)
{
if (fieldName == StringTable->insert("node"))
{
if (index >= mNodes.size())
return NULL;
const RiverNode& node = mNodes[index];
char buffer[1024];
dMemset(buffer, 0, 1024);
dSprintf(buffer, 1024, "Node = \"%f %f %f %f %f %f %f %f\";", node.point.x, node.point.y, node.point.z,
node.width,
node.depth,
node.normal.x, node.normal.y, node.normal.z);
return StringTable->insert(buffer);
}
return NULL;
}
void River::innerRender( SceneRenderState *state )
{
GFXDEBUGEVENT_SCOPE( River_innerRender, ColorI( 255, 0, 0 ) );

View file

@ -394,6 +394,9 @@ public:
void writeFields(Stream &stream, U32 tabStop) override;
bool writeField( StringTableEntry fieldname, const char *value ) override;
U32 getSpecialFieldSize(StringTableEntry fieldName) override;
const char* getSpecialFieldOut(StringTableEntry fieldName, const U32& index) override;
// NetObject
U32 packUpdate(NetConnection *, U32, BitStream *) override;
void unpackUpdate(NetConnection *, BitStream *) override;

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

@ -178,7 +178,7 @@ GuiInspectorField* GuiInspectorGroup::constructField( S32 fieldType )
// return our new datablock field with correct datablock type enumeration info
return dbFieldClass;
}
}
// Nope, not a datablock. So maybe it has a valid inspector field override we can use?
if(!cbt->getInspectorFieldType())
@ -641,40 +641,50 @@ void GuiInspectorGroup::addInspectorField(StringTableEntry name, StringTableEntr
{
S32 fieldType = -1;
if (typeName == StringTable->insert("int"))
fieldType = TypeS32;
else if (typeName == StringTable->insert("float"))
fieldType = TypeF32;
else if (typeName == StringTable->insert("vector"))
fieldType = TypePoint3F;
else if (typeName == StringTable->insert("vector2"))
fieldType = TypePoint2F;
else if (typeName == StringTable->insert("material"))
fieldType = TypeMaterialAssetId;
else if (typeName == StringTable->insert("image"))
fieldType = TypeImageAssetId;
else if (typeName == StringTable->insert("shape"))
fieldType = TypeShapeAssetId;
else if (typeName == StringTable->insert("sound"))
fieldType = TypeSoundAssetId;
else if (typeName == StringTable->insert("bool"))
fieldType = TypeBool;
else if (typeName == StringTable->insert("object"))
fieldType = TypeSimObjectPtr;
else if (typeName == StringTable->insert("string"))
fieldType = TypeString;
else if (typeName == StringTable->insert("colorI"))
fieldType = TypeColorI;
else if (typeName == StringTable->insert("colorF"))
fieldType = TypeColorF;
else if (typeName == StringTable->insert("ease"))
fieldType = TypeEaseF;
else if (typeName == StringTable->insert("command"))
fieldType = TypeCommand;
else if (typeName == StringTable->insert("filename"))
fieldType = TypeStringFilename;
String typeNameTyped = typeName;
if (!typeNameTyped.startsWith("Type"))
typeNameTyped = String("Type") + typeNameTyped;
ConsoleBaseType* typeRef = AbstractClassRep::getTypeByName(typeNameTyped.c_str());
if(typeRef)
{
fieldType = typeRef->getTypeID();
}
else
fieldType = -1;
{
if (typeName == StringTable->insert("int"))
fieldType = TypeS32;
else if (typeName == StringTable->insert("float"))
fieldType = TypeF32;
else if (typeName == StringTable->insert("vector"))
fieldType = TypePoint3F;
else if (typeName == StringTable->insert("vector2"))
fieldType = TypePoint2F;
else if (typeName == StringTable->insert("material"))
fieldType = TypeMaterialAssetId;
else if (typeName == StringTable->insert("image"))
fieldType = TypeImageAssetId;
else if (typeName == StringTable->insert("shape"))
fieldType = TypeShapeAssetId;
else if (typeName == StringTable->insert("sound"))
fieldType = TypeSoundAssetId;
else if (typeName == StringTable->insert("bool"))
fieldType = TypeBool;
else if (typeName == StringTable->insert("object"))
fieldType = TypeSimObjectPtr;
else if (typeName == StringTable->insert("string"))
fieldType = TypeString;
else if (typeName == StringTable->insert("colorI"))
fieldType = TypeColorI;
else if (typeName == StringTable->insert("colorF"))
fieldType = TypeColorF;
else if (typeName == StringTable->insert("ease"))
fieldType = TypeEaseF;
else if (typeName == StringTable->insert("command"))
fieldType = TypeCommand;
else if (typeName == StringTable->insert("filename"))
fieldType = TypeStringFilename;
}
GuiInspectorField* fieldGui;

View file

@ -176,38 +176,53 @@ void GuiVariableInspector::addField(const char* name, const char* label, const c
//find the field type
S32 fieldTypeMask = -1;
if (newField->mFieldTypeName == StringTable->insert("int"))
fieldTypeMask = TypeS32;
else if (newField->mFieldTypeName == StringTable->insert("float"))
fieldTypeMask = TypeF32;
else if (newField->mFieldTypeName == StringTable->insert("vector"))
fieldTypeMask = TypePoint3F;
else if (newField->mFieldTypeName == StringTable->insert("vector2"))
fieldTypeMask = TypePoint2F;
else if (newField->mFieldTypeName == StringTable->insert("material"))
fieldTypeMask = TypeMaterialAssetId;
else if (newField->mFieldTypeName == StringTable->insert("image"))
fieldTypeMask = TypeImageAssetId;
else if (newField->mFieldTypeName == StringTable->insert("shape"))
fieldTypeMask = TypeShapeAssetId;
else if (newField->mFieldTypeName == StringTable->insert("bool"))
fieldTypeMask = TypeBool;
else if (newField->mFieldTypeName == StringTable->insert("object"))
fieldTypeMask = TypeSimObjectPtr;
else if (newField->mFieldTypeName == StringTable->insert("string"))
fieldTypeMask = TypeString;
else if (newField->mFieldTypeName == StringTable->insert("colorI"))
fieldTypeMask = TypeColorI;
else if (newField->mFieldTypeName == StringTable->insert("colorF"))
fieldTypeMask = TypeColorF;
else if (newField->mFieldTypeName == StringTable->insert("ease"))
fieldTypeMask = TypeEaseF;
else if (newField->mFieldTypeName == StringTable->insert("command"))
fieldTypeMask = TypeCommand;
else if (newField->mFieldTypeName == StringTable->insert("filename"))
fieldTypeMask = TypeStringFilename;
String typeNameTyped = typeName;
if (!typeNameTyped.startsWith("Type"))
typeNameTyped = String("Type") + typeNameTyped;
ConsoleBaseType* typeRef = AbstractClassRep::getTypeByName(typeNameTyped.c_str());
if (typeRef)
{
fieldTypeMask = typeRef->getTypeID();
if (!typeRef->getInspectorFieldType())
fieldTypeMask = TypeString;
newField->mFieldTypeName = StringTable->insert(typeRef->getTypeName());
}
else
fieldTypeMask = -1;
{
if (newField->mFieldTypeName == StringTable->insert("int"))
fieldTypeMask = TypeS32;
else if (newField->mFieldTypeName == StringTable->insert("float"))
fieldTypeMask = TypeF32;
else if (newField->mFieldTypeName == StringTable->insert("vector"))
fieldTypeMask = TypePoint3F;
else if (newField->mFieldTypeName == StringTable->insert("vector2"))
fieldTypeMask = TypePoint2F;
else if (newField->mFieldTypeName == StringTable->insert("material"))
fieldTypeMask = TypeMaterialAssetId;
else if (newField->mFieldTypeName == StringTable->insert("image"))
fieldTypeMask = TypeImageAssetId;
else if (newField->mFieldTypeName == StringTable->insert("shape"))
fieldTypeMask = TypeShapeAssetId;
else if (newField->mFieldTypeName == StringTable->insert("bool"))
fieldTypeMask = TypeBool;
else if (newField->mFieldTypeName == StringTable->insert("object"))
fieldTypeMask = TypeSimObjectPtr;
else if (newField->mFieldTypeName == StringTable->insert("string"))
fieldTypeMask = TypeString;
else if (newField->mFieldTypeName == StringTable->insert("colorI"))
fieldTypeMask = TypeColorI;
else if (newField->mFieldTypeName == StringTable->insert("colorF"))
fieldTypeMask = TypeColorF;
else if (newField->mFieldTypeName == StringTable->insert("ease"))
fieldTypeMask = TypeEaseF;
else if (newField->mFieldTypeName == StringTable->insert("command"))
fieldTypeMask = TypeCommand;
else if (newField->mFieldTypeName == StringTable->insert("filename"))
fieldTypeMask = TypeStringFilename;
}
newField->mFieldType = fieldTypeMask;
//

View file

@ -1724,7 +1724,7 @@ void SceneObject::updateRenderChangesByParent(){
MatrixF offset;
offset.mul(renderXform, xform);
MatrixF mat;
MatrixF mat;
//add the "offset" caused by the parents change, and add it to it's own
// This is needed by objects that update their own render transform thru interpolate tick
@ -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

@ -1,52 +1,22 @@
function callGamemodeFunction(%gameModeFuncName, %arg0, %arg1, %arg2, %arg3, %arg4, %arg5, %arg6)
{
%activeSceneCount = getSceneCount();
%hasGameMode = 0;
for(%i=0; %i < %activeSceneCount; %i++)
%validGameModeCall = false;
%gamemodeList = getGameModesList();
%gameModeCount = %gamemodeList.count();
for(%i=0; %i < %gameModeCount; %i++)
{
%gamemodeName = getScene(%i).gameModeName;
if(%gamemodeName !$= "")
%gameModeObj = %gamemodeList.getKey(%i);
%active = %gamemodeList.getValue(%i);
if(!isObject(%gameModeObj) || !%active)
continue;
if(%gameModeObj.isMethod(%gameModeFuncName))
{
//if the scene defines a game mode, go ahead and envoke it here
if(isObject(%gamemodeName) && %gamemodeName.isMethod(%gameModeFuncName))
{
eval(%gamemodeName @ "."@%gameModeFuncName@"(\""@%arg0@"\", \""@%arg1@"\", \""@%arg2@"\", \""@%arg3@"\", \""@%arg4@"\", \""@%arg5@"\", \""@%arg6@"\");" );
%hasGameMode = 1;
}
else
{
//if we don't have an object, attempt the static call
if(isMethod(%gamemodeName, %gameModeFuncName))
{
eval(%gamemodeName @ "::"@%gameModeFuncName@"(\""@%arg0@"\", \""@%arg1@"\", \""@%arg2@"\", \""@%arg3@"\", \""@%arg4@"\", \""@%arg5@"\", \""@%arg6@"\");" );
%hasGameMode = 1;
}
}
eval(%gameModeObj @ "."@%gameModeFuncName@"(\""@%arg0@"\", \""@%arg1@"\", \""@%arg2@"\", \""@%arg3@"\", \""@%arg4@"\", \""@%arg5@"\", \""@%arg6@"\");" );
%validGameModeCall = true;
}
}
//if none of our scenes have gamemodes, we need to kick off a default
if(%hasGameMode == 0)
{
%defaultModeName = ProjectSettings.value("Gameplay/GameModes/defaultModeName");
if(%defaultModeName !$= "")
{
if(isObject(%defaultModeName) && %defaultModeName.isMethod(%gameModeFuncName))
{
eval(%defaultModeName @ "."@%gameModeFuncName@"(\""@%arg0@"\", \""@%arg1@"\", \""@%arg2@"\", \""@%arg3@"\", \""@%arg4@"\", \""@%arg5@"\", \""@%arg6@"\");" );
%hasGameMode = 1;
}
else
{
if(isMethod(%defaultModeName, %gameModeFuncName))
{
eval(%defaultModeName @ "::"@%gameModeFuncName@"(\""@%arg0@"\", \""@%arg1@"\", \""@%arg2@"\", \""@%arg3@"\", \""@%arg4@"\", \""@%arg5@"\", \""@%arg6@"\");" );
%hasGameMode = 1;
}
}
}
}
return %hasGameMode;
return %validGameModeCall;
}

View file

@ -9,25 +9,12 @@ function ExampleModule::onDestroy(%this)
//This is called when the server is initially set up by the game application
function ExampleModule::initServer(%this)
{
%this.queueExec("./scripts/server/ExampleGameMode");
%this.queueExec("./scripts/shared/ExampleGameMode");
}
//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
@ -38,7 +25,7 @@ function ExampleModule::onDestroyGameServer(%this)
//This is called when the client is initially set up by the game application
function ExampleModule::initClient(%this)
{
%this.queueExec("./scripts/client/inputCommands");
%this.queueExec("./scripts/client/inputCommands");
//client scripts
exec("./scripts/client/defaultkeybinds");
@ -46,6 +33,8 @@ function ExampleModule::initClient(%this)
%prefPath = getPrefpath();
if(isScriptFile(%prefPath @ "/keybinds"))
exec(%prefPath @ "/keybinds");
%this.queueExec("./scripts/shared/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,7 +1,7 @@
//--- OBJECT WRITE BEGIN ---
new Scene(ExampleLevel) {
Enabled = "1";
gameModeName="ExampleGameMode";
gameModes="ExampleGameMode";
new LevelInfo(theLevelInfo) {
fogColor = "0.6 0.6 0.7 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

@ -9,8 +9,8 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
isContainer = "1";
canSaveDynamicFields = "1";
currentMenuIdx = "0";
launchInEditor = "0";
previewButtonSize = "445 120";
new GuiInputCtrl(ChooseLevelInputHandler) {
ignoreMouseEvents = "1";
@ -43,39 +43,50 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
extent = "310 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";
visible = "0";
command = "ChooseLevelMenu.openMenu(2);";
tooltipProfile = "GuiToolTipProfile";
internalName = "ConfigBtn";
class = "ChooseLevelMenuButton";
hidden = "1";
};
};
new GuiControl(ChooseLevelMenuNavButtonOverlay) {
position = "0 61";
extent = "1281 60";
horizSizing = "width";
profile = "GuiNonModalDefaultProfile";
visible = "0";
tooltipProfile = "GuiToolTipProfile";
isContainer = "1";
hidden = "1";
new GuiBitmapCtrl(ChooseLevelMenuPrevNavIcon) {
BitmapAsset = "UI:Keyboard_Black_Q_image";
@ -94,13 +105,67 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
};
};
new GuiContainer(LevelSelectContainer) {
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 = "1 1";
extent = "443 60";
horizSizing = "width";
vertSizing = "height";
profile = "GuiMenuDefaultProfile";
tooltipProfile = "GuiToolTipProfile";
};
};
new GuiBitmapCtrl(GameModePreviewBitmap) {
position = "448 0";
extent = "440 440";
horizSizing = "left";
profile = "GuiNonModalDefaultProfile";
visible = "0";
tooltipProfile = "GuiToolTipProfile";
hidden = "1";
};
new GuiTextCtrl(GameModeNameText) {
text = "DeathMatchGame";
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";
vScrollBar = "dynamic";
@ -112,7 +177,7 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
new GuiStackControl(LevelPreviewArray) {
padding = "5";
position = "0 1";
extent = "445 120";
extent = "445 60";
horizSizing = "width";
vertSizing = "height";
profile = "GuiMenuDefaultProfile";
@ -214,7 +279,7 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
};
new GuiTextEditCtrl(serverNameCTRL) {
text = "";
text = "Torque 3D Server";
position = "606 4";
extent = "295 22";
horizSizing = "left";
@ -240,7 +305,6 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
tooltipProfile = "GuiToolTipProfile";
};
new GuiTextEditCtrl(serverPassCTRL) {
text = "";
position = "606 4";
extent = "295 22";
horizSizing = "left";
@ -313,8 +377,8 @@ $guiContent = new GuiControl(ChooseLevelMenu) {
profile = "GuiMenuPanelProfile";
tooltipProfile = "GuiToolTipProfile";
new GuiIconButtonCtrl(ChooseLevelStartBtn) {
BitmapAsset = "UI:Keyboard_Black_Return_image";
new GuiIconButtonCtrl(ChooseLevelNextBtn) {
BitmapAsset = "UI:Keyboard_Black_Blank_image";
sizeIconToButton = "1";
makeIconSquare = "1";
textLocation = "Center";

View file

@ -25,7 +25,7 @@ function ChooseLevelMenu::onAdd( %this )
if(!isObject(ChooseLevelAssetQuery))
new AssetQuery(ChooseLevelAssetQuery);
%this.previewButtonSize = "445 120";
$LevelPreviewButtonSize = "445 60";
}
function ChooseLevelMenu::fillPrefEntries( %this )
@ -40,8 +40,107 @@ function ChooseLevelMenu::fillPrefEntries( %this )
function ChooseLevelMenu::onWake(%this)
{
%this.fillPrefEntries();
refreshGameModesList();
refreshLevelsList();
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;
//If we've only got one gamemode, just force it to use that one, as we can
//assume that is THE game's gamemode
%gamemodeList = getGameModesList();
if(%gamemodeList.count() == 1)
{
%gameModeObj = %gamemodeList.getKey(0);
if(isObject(%gameModeObj))
{
%gameModeObj.active = true;
ChooseLevelMenuTabList-->LevelBtn.active = true;
ChooseLevelMenuTabList-->GameModeBtn.active = false;
ChooseLevelMenu.openMenu(1);
return;
}
}
else
{
//Otherwise open the gamemode list as normal
%this.schedule(32, openMenu, 0);
}
}
function refreshGameModesList()
{
GameModePreviewArray.clear();
$pref::Server::GameMode = "";
ChooseLevelMenuTabList-->LevelBtn.active = false;
//popilate the gamemodes list first
%gamemodeList = getGameModesList();
%gameModeCount = %gamemodeList.count();
for (%i=0; %i<%gameModeCount; %i++)
{
%gameModeObj = %gamemodeList.getKey(%i);
if(isObject(%gameModeObj))
{
%gameModeName = %gameModeObj.gameModeName;
if(%gameModeName $= "")
%gameModeName = %gameModeObj.getName();
%preview = new GuiContainer() {
position = "0 0";
extent = $LevelPreviewButtonSize;
horizSizing = "right";
vertSizing = "bottom";
gameModeObj = %gameModeObj;
gameModeDesc = %gameModeObj.description;
previewImage = %gameModeObj.previewImage;
cansave = false;
new GuiToggleButtonCtrl() {
position = $LevelPreviewButtonSize.y SPC 0;
extent = $LevelPreviewButtonSize.x - $LevelPreviewButtonSize.y - 5 SPC $LevelPreviewButtonSize.y;
profile = GuiMenuButtonProfile;
horizSizing = "right";
vertSizing = "bottom";
internalName = "button";
class = "GameModePreviewButton";
text = %gameModeName;
command = "ToggleGameMode(" @ %i @ ");"; //allow doubleclick to quick action it
textMargin = 120;
};
new GuiBitmapCtrl() {
position = "0 0";
extent = $LevelPreviewButtonSize.y SPC $LevelPreviewButtonSize.y;
internalName = "checkbox";
bitmapAsset = "UI:toggleMarker_image";
};
};
GameModePreviewArray.add(%preview);
}
}
}
function refreshLevelsList()
{
LevelPreviewArray.clear();
//fetch the levelAssets
ChooseLevelAssetQuery.clear();
AssetDatabase.findAssetType(ChooseLevelAssetQuery, "LevelAsset");
@ -57,6 +156,10 @@ function ChooseLevelMenu::onWake(%this)
return;
}
%gameModesList = getGameModesList();
//filter the levelAssets by the gamemode selected
%filteredIndex = 0;
for(%i=0; %i < %count; %i++)
{
%assetId = ChooseLevelAssetQuery.getAsset(%i);
@ -71,6 +174,46 @@ function ChooseLevelMenu::onWake(%this)
if ( !isFile(%file) )
continue;
if( %levelAsset.isSubScene )
continue;
//filter for selected gamemode
%levelGameModes = %levelAsset.gameModesNames;
//If the level has no gamemodes defined, we just assume the default case of it being a wildcard to whatever gamemode
//is available
if(%levelGameModes $= "")
{
%foundGameModeMatch = true;
}
else
{
%foundGameModeMatch = false;
for(%gm = 0; %gm < getTokenCount(%levelGameModes, ";"); %gm++)
{
%gameModeName = getToken(%levelGameModes, ";", %gm);
for(%g=0; %g < %gameModesList.count(); %g++)
{
%gameModeObj = %gameModesList.getKey(%g);
if(!isObject(%gameModeObj) || (!%gameModeObj.active && !%gameModeObj.alwaysActive && %gameModesList.count() > 1))
continue;
if(%gameModeName $= %gameModeObj.getName())
{
%foundGameModeMatch = true;
break;
}
}
if(%foundGameModeMatch)
break;
}
}
if(!%foundGameModeMatch)
continue;
%levelPreviewImg = %levelAsset.getPreviewImagePath();
if (!isFile(%levelPreviewImg))
@ -78,7 +221,7 @@ function ChooseLevelMenu::onWake(%this)
%preview = new GuiIconButtonCtrl() {
position = "0 0";
extent = %this.previewButtonSize;
extent = $LevelPreviewButtonSize;
buttonType = "PushButton";
profile = GuiMenuButtonLeftJustProfile;
horizSizing = "right";
@ -86,7 +229,7 @@ function ChooseLevelMenu::onWake(%this)
internalName = "button";
class = "LevelPreviewButton";
//command = "$selectedLevelAsset = " @ %assetId @ ";";
altCommand = "ChooseLevelBegin(1);"; //allow doubleclick to quick action it
altCommand = "ChooseLevelBegin(" @ %filteredIndex @ ");"; //allow doubleclick to quick action it
text = %levelAsset.levelName;
bitmapAsset = %levelPreviewImg;
levelAsset = %levelAsset;
@ -101,26 +244,19 @@ function ChooseLevelMenu::onWake(%this)
};
LevelPreviewArray.add(%preview);
%filteredIndex++;
}
GameModePreviewArray.listPosition = 0;
LevelPreviewArray.listPosition = 0;
// 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 +269,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)
@ -155,10 +291,31 @@ function ChooseLevelMenu::syncGUI(%this)
ChooseLevelBackBtn.setBitmap(BaseUIActionMap.getCommandButtonBitmap(%device, "BaseUIBackOut"));
ChooseLevelStartBtn.setBitmap(ChooseLevelActionMap.getCommandButtonBitmap(%device, "ChooseLevelBegin"));
if(ChooseLevelMenu.currentMenuIdx == 0)
ChooseLevelNextBtn.text = "Toggle Mode";
else
ChooseLevelNextBtn.text = "Start Game";
ChooseLevelNextBtn.setBitmap(ChooseLevelActionMap.getCommandButtonBitmap(%device, "ChooseLevelMenuOption"));
ChooseLevelMenuPrevNavIcon.setBitmap(ChooseLevelActionMap.getCommandButtonBitmap(%device, "ChooseLevelMenuPrevMenu"));
ChooseLevelMenuNextNavIcon.setBitmap(ChooseLevelActionMap.getCommandButtonBitmap(%device, "ChooseLevelMenuNextMenu"));
%hasActiveGameMode = false;
for(%i=0; %i < GameModePreviewArray.getCount(); %i++)
{
%btn = GameModePreviewArray.getObject(%i);
if(!isObject(%btn.gameModeObj))
continue;
if((%btn.gameModeObj.isActive() || %btn.gameModeObj.isAlwaysActive()) == true)
{
%hasActiveGameMode = true;
break;
}
}
ChooseLevelMenuTabList-->LevelBtn.active = %hasActiveGameMode;
}
function LevelPreviewArray::syncGUI(%this)
@ -169,14 +326,40 @@ function LevelPreviewArray::syncGUI(%this)
$selectedLevelAsset = %btn.levelAssetId;
}
function GameModePreviewArray::syncGUI(%this)
{
for(%i=0; %i < %this.getCount(); %i++)
{
%btn = %this.getObject(%i);
%btn-->button.setHighlighted(false);
%bitmapAst = (%btn.gameModeObj.active || %btn.gameModeObj.alwaysActive) ? "UI:toggleMarker_h_image" : "UI:toggleMarker_image";
%btn-->checkbox.setBitmap(%bitmapAst);
}
%btn = %this.getObject(%this.listPosition);
%btn-->button.setHighlighted(true);
}
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);
//bail if we're trying to step into a hidden or disabled menu
if(!ChooseLevelMenu.isMenuAvailable(ChooseLevelMenu.currentMenuIdx))
{
ChooseLevelMenu.currentMenuIdx = %currentIdx;
return;
}
if(%currentIdx == ChooseLevelMenu.currentMenuIdx)
return;
@ -187,12 +370,23 @@ 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);
//bail if we're trying to step into a hidden or disabled menu
if(!ChooseLevelMenu.isMenuAvailable(ChooseLevelMenu.currentMenuIdx))
{
ChooseLevelMenu.currentMenuIdx = %currentIdx;
return;
}
if(%currentIdx == ChooseLevelMenu.currentMenuIdx)
return;
@ -201,15 +395,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 +416,78 @@ function ChooseLevelMenu::openMenu(%this, %menuIdx)
%this.syncGui();
}
function ChooseLevelBegin(%val)
function ChooseLevelMenu::isMenuAvailable(%this, %menuIdx)
{
if(%menuIdx == 0)
%btn = %this-->GameModeBtn;
else if(%menuIdx == 1)
%btn = %this-->LevelBtn;
else if(%menuIdx == 2)
%btn = %this-->ConfigBtn;
return %btn.isActive() && %btn.isVisible();
}
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)
ToggleGameMode(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 ToggleGameMode(%index)
{
if(%index $= "")
%index = $MenuList.listPosition;
%entry = GameModePreviewArray.getObject(%index);
if(!isObject(%entry) || !isObject(%entry.gameModeObj))
{
MessageBoxOK("Error", "Selected game mode does not have a valid mode");
return;
}
%entry.gameModeObj.active = !%entry.gameModeObj.active;
refreshLevelsList();
$MenuList.syncGui();
ChooseLevelMenu.syncGui();
}
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 +507,23 @@ function ChooseLevelMenu::onSleep( %this )
}
}
function GameModePreviewButton::onHighlighted(%this, %highlighted)
{
if(%highlighted)
{
$MenuList.listPosition = $MenuList.getObjectIndex(%this.getParent());
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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -0,0 +1,3 @@
<ImageAsset
AssetName="toggleMarker_h_image"
imageFile="@assetFile=toggleMarker_h.png"/>

View file

@ -0,0 +1,3 @@
<ImageAsset
AssetName="toggleMarker_image"
imageFile="@assetFile=toggleMarker.png"/>

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,35 @@
AssetBrowser::registerObjectType("GameModeType", "Gamemode Objects", "Gamemode");
function GameModeType::setupCreateNew()
{
}
function GameModeType::onCreateNew()
{
}
function GameModeType::buildBrowserElement(%this, %className, %previewData)
{
//echo("DatablockObjectType::buildBrowserElement() - " @ %datablock @ ", " @ %name @ ", " @ %previewData);
%previewData.assetName = %this.getName();
%previewData.assetPath = %this.getFileName();
%previewData.previewImage = "ToolsModule:genericAssetIcon_image";
//Lets see if we have a icon for specifically for this datablock type
%dataClass = %this.getClassName();
%dataClass = strreplace(%dataClass, "Data", "");
%previewImage = "tools/classIcons/" @ %dataClass @ ".png";
if(isFile(%previewImage))
{
%previewData.previewImage = %previewImage;
}
//%previewData.assetFriendlyName = %assetDef.assetName;
%previewData.assetDesc = %this;
%previewData.tooltip = "\nGameMode Name: " @ %previewData.assetName @
"\nPath: " @ %previewData.assetPath;
}

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,24 +9,23 @@ 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
function @@::onCreateGameServer(%this)
{
//--DATABLOCK EXEC BEGIN--
//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 END--
}
//This is called when the server is shut down due to the game/map being exited
@ -37,6 +36,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

@ -1,20 +1,24 @@
function GuiVariableInspector::onInspectorFieldModified(%this, %targetObj, %fieldName, %index, %oldValue, %newValue)
{
echo("FIELD CHANGED: " @ %fieldName @ " from " @ %oldValue @ " to " @ %newValue);
//echo("FIELD CHANGED: " @ %fieldName @ " from " @ %oldValue @ " to " @ %newValue);
}
function GuiInspectorVariableGroup::onConstructField(%this, %fieldName, %fieldLabel, %fieldTypeName, %fieldDesc, %fieldDefaultVal, %fieldDataVals, %callbackName, %ownerObj)
{
%inspector = %this.getParent();
%makeCommand = %this @ ".build" @ %fieldTypeName @ "Field(\""@ %fieldName @ "\",\"" @ %fieldLabel @ "\",\"" @ %fieldDesc @ "\",\"" @
%fieldDefaultVal @ "\",\"" @ %fieldDataVals @ "\",\"" @ %inspector @ "." @ %callbackName @ "\",\"" @ %ownerObj @"\");";
eval(%makeCommand);
return GuiInspectorGroup::onConstructField(%this, %fieldName, %fieldLabel, %fieldTypeName, %fieldDesc, %fieldDefaultVal, %fieldDataVals, %callbackName, %ownerObj);
}
function GuiInspectorGroup::onConstructField(%this, %fieldName, %fieldLabel, %fieldTypeName, %fieldDesc, %fieldDefaultVal, %fieldDataVals, %callbackName, %ownerObj)
{
%inspector = %this.getParent();
%makeCommand = %this @ ".build" @ %fieldTypeName @ "Field(\""@ %fieldName @ "\",\"" @ %fieldLabel @ "\",\"" @ %fieldDesc @ "\",\"" @
%fieldDefaultVal @ "\",\"" @ %fieldDataVals @ "\",\"" @ %inspector @ "." @ %callbackName @ "\",\"" @ %ownerObj @"\");";
eval(%makeCommand);
if(%this.isMethod("build" @ %fieldTypeName @ "Field"))
{
%inspector = %this.getParent();
%makeCommand = %this @ ".build" @ %fieldTypeName @ "Field(\""@ %fieldName @ "\",\"" @ %fieldLabel @ "\",\"" @ %fieldDesc @ "\",\"" @
%fieldDefaultVal @ "\",\"" @ %fieldDataVals @ "\",\"" @ %inspector @ "." @ %callbackName @ "\",\"" @ %ownerObj @"\");";
%ret = eval(%makeCommand);
if(%ret == true || %ret $= "")
return true;
}
return false;
}

View file

@ -2,6 +2,7 @@
AssetName="DefaultEditorLevel"
LevelFile="@assetFile=DefaultEditorLevel.mis"
LevelName="DefaultEditorLevel"
PostFXPresetFile="@assetFile=tools/levels/DefaultEditorLevel.postfxpreset.tscript"
description="An empty room"
previewImageAsset0="@asset=ToolsModule:DefaultEditorLevel_preview_image"
previewImageAsset1="@asset=ToolsModule:DefaultEditorLevel_preview_image"

View file

@ -1,33 +1,20 @@
//--- OBJECT WRITE BEGIN ---
new Scene(EditorTemplateLevel) {
canSave = "1";
canSaveDynamicFields = "1";
Enabled = "1";
isEditing = "1";
enabled = "1";
new LevelInfo(theLevelInfo) {
nearClip = "0.1";
visibleDistance = "1000";
visibleGhostDistance = "0";
decalBias = "0.0015";
fogColor = "0.6 0.6 0.7 1";
fogDensity = "0";
FogColor = "0.6 0.6 0.7 1";
fogDensityOffset = "700";
fogAtmosphereHeight = "0";
canvasClearColor = "0 0 0 255";
ambientLightBlendPhase = "1";
ambientLightBlendCurve = "0 0 -1 -1";
soundAmbience = "AudioAmbienceDefault";
soundDistanceModel = "Linear";
canSave = "1";
canSaveDynamicFields = "1";
Enabled = "1";
enabled = "1";
};
new ScatterSky(DynamicSky) {
sunScale = "0.991102 0.921582 0.83077 1";
zOffset = "-3000";
azimuth = "25";
brightness = "5";
flareType = "LightFlareExample1";
MoonMatAsset = "Core_Rendering:moon_wglow";
useNightCubemap = "1";
nightCubemap = "nightCubemap";
@ -44,12 +31,11 @@ new Scene(EditorTemplateLevel) {
persistentId = "289ad401-3140-11ed-aae8-c0cb519281fc";
reflectionPath = "tools/levels/DefaultEditorLevel/probes/";
};
new GroundPlane() {
scaleU = "32";
scaleV = "32";
MaterialAsset = "Prototyping:FloorGray";
Enabled = "1";
enabled = "1";
position = "0 0 0";
rotation = "1 0 0 0";
scale = "1 1 1";

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

@ -42,7 +42,7 @@ $guiContent = new GuiControl() {
minSize = "50 50";
EdgeSnap = false;
text = "";
class = "EWToolsPaletteWindowClass";
class = "ButtonPalette";
new GuiDynamicCtrlArrayControl(ToolsPaletteArray) {
canSaveDynamicFields = "0";

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

@ -69,6 +69,7 @@ function initializeWorldEditor()
exec("./scripts/probeBake.ed." @ $TorqueScriptFileExtension);
exec("./scripts/visibility/visibilityLayer.ed." @ $TorqueScriptFileExtension);
exec("./scripts/visibility/probeViz." @ $TorqueScriptFileExtension);
exec("./scripts/buttonPalette." @ $TorqueScriptFileExtension);
exec("tools/gui/postFxEditor." @ $TorqueScriptFileExtension );
exec("tools/gui/renderTargetVisualizer.ed." @ $TorqueScriptFileExtension);
@ -77,6 +78,8 @@ function initializeWorldEditor()
loadDirectory(expandFilename("./scripts/editors"));
loadDirectory(expandFilename("./scripts/interfaces"));
exec("./scripts/interfaces/subSceneEditing.tscript");
// Create the default editor plugins before calling buildMenus.
new ScriptObject( WorldEditorPlugin )

View file

@ -1069,11 +1069,13 @@ function WorldEditorInspectorPlugin::onWorldEditorStartup( %this )
//connect editor windows
GuiWindowCtrl::attach( EWInspectorWindow, EWTreeWindow);
%map = new ActionMap();
%map = new ActionMap();
/*
%map.bindCmd( keyboard, "1", "EWorldEditorNoneModeBtn.performClick();", "" ); // Select
%map.bindCmd( keyboard, "2", "EWorldEditorMoveModeBtn.performClick();", "" ); // Move
%map.bindCmd( keyboard, "3", "EWorldEditorRotateModeBtn.performClick();", "" ); // Rotate
%map.bindCmd( keyboard, "4", "EWorldEditorScaleModeBtn.performClick();", "" ); // Scale
%map.bindCmd( keyboard, "4", "EWorldEditorScaleModeBtn.performClick();", "" ); // Scale*/
%map.bindCmd( keyboard, "f", "FitToSelectionBtn.performClick();", "" );// Fit Camera to Selection
%map.bindCmd( keyboard, "z", "EditorGuiStatusBar.setCamera(\"Standard Camera\");", "" );// Free camera
%map.bindCmd( keyboard, "n", "ToggleNodeBar->renderHandleBtn.performClick();", "" );// Render Node
@ -1093,21 +1095,38 @@ function WorldEditorInspectorPlugin::onWorldEditorStartup( %this )
function WorldEditorInspectorPlugin::onActivated( %this )
{
Parent::onActivated( %this );
//Clears the button pallete stack
EWToolsPaletteWindow.setStackCtrl(ToolsPaletteArray); //legacy ctrl adhereance
EWToolsPaletteWindow.clearButtons();
EWToolsPaletteWindow.setActionMap(WorldEditorInspectorPlugin.map);
//Adds a button to the pallete stack
//Name Icon Click Command Tooltip text Keybind
EWToolsPaletteWindow.addButton("Select", "ToolsModule:arrow_n_image", "EWorldEditorNoneModeBtn::onClick();", "", "Select Arrow", "1");
EWToolsPaletteWindow.addButton("Move", "ToolsModule:translate_n_image", "EWorldEditorMoveModeBtn::onClick();", "", "Move Selection", "2");
EWToolsPaletteWindow.addButton("Rotate", "ToolsModule:rotate_n_image", "EWorldEditorRotateModeBtn::onClick();", "", "Rotate Selection", "3");
EWToolsPaletteWindow.addButton("Scale", "ToolsModule:Scale_n_image", "EWorldEditorScaleModeBtn::onClick();", "", "Scale Selection", "4");
EWToolsPaletteWindow.refresh();
EditorGui-->InspectorWindow.setVisible( true );
EditorGui-->TreeWindow.setVisible( true );
EditorGui-->WorldEditorToolbar.setVisible( true );
%this.map.push();
//%this.map.push();
}
function WorldEditorInspectorPlugin::onDeactivated( %this )
{
Parent::onDeactivated( %this );
EWToolsPaletteWindow.popActionMap();
EditorGui-->InspectorWindow.setVisible( false );
EditorGui-->TreeWindow.setVisible( false );
EditorGui-->WorldEditorToolbar.setVisible( false );
%this.map.pop();
//%this.map.pop();
}
function WorldEditorInspectorPlugin::onEditMenuSelect( %this, %editMenu )
@ -2044,7 +2063,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
{
@ -2140,6 +2159,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 @ " );";
}
}
}
}
@ -2179,7 +2206,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 )
@ -2620,6 +2647,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 )
@ -2887,6 +2985,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

@ -0,0 +1,160 @@
function ButtonPalette::setStackCtrl(%this, %ctrl)
{
%this.stackCtrl = %ctrl;
}
function ButtonPalette::getStackCtrl(%this)
{
if(isObject(%this.stackCtrl))
return %this.stackCtrl;
else
return %this;
}
function ButtonPalette::setActionMap(%this, %actionMap)
{
%this.actionMap = %actionMap;
%this.actionMap.push();
}
function ButtonPalette::getActionMap(%this)
{
return %this.actionMap;
}
function ButtonPalette::popActionMap(%this, %actionMap)
{
%this.actionMap.pop();
}
function ButtonPalette::clearButtons(%this)
{
%stackCtrl = %this.getStackCtrl();
%stackCtrl.clear();
for(%i=0; %i < %this.numButtons; %i++)
{
if(isObject(%this.actionMap))
{
%this.actionMap.unbind("keyboard", getField(%this.buttons[%i], 5));
}
%this.buttons[%i] = "";
}
%this.numButtons = 0;
}
//Name, Icon, Click Command, Variable, Tooltip text, Keybind
function ButtonPalette::addButton(%this, %name, %iconAsset, %command, %syncVariable, %toolTip, %keybind)
{
if(%this.numButtons $= "")
%this.numButtons = 0;
for(%i=0; %i < %this.numButtons; %i++)
{
%buttonInfo = %this.buttons[%i];
//echo("Testing button info: " @ getField(%buttonInfo, 0) @ " against new button name: " @ %name);
if(getField(%buttonInfo, 0) $= %name) //no duplicates
return;
}
%this.buttons[%this.numButtons] = %name TAB %iconAsset TAB %command TAB %syncVariable TAB %toolTip TAB %keybind;
%this.numButtons++;
}
function ButtonPalette::removeButton(%this, %name)
{
%found = false;
for(%i=0; %i < %this.numButtons; %i++)
{
if(getField(%this.buttons[%i], 0) $= %name)
{
%found = true;
if(isObject(%this.actionMap))
{
%this.actionMap.unbind("keyboard", getField(%this.buttons[%i], 5));
}
}
if(%found)
{
%this.buttons[%i] = %this.buttons[%i+1];
}
}
if(%found)
%this.numButtons--;
return %found;
}
function ButtonPalette::findButton(%this, %name)
{
%stackCtrl = %this.getStackCtrl();
for(%i=0; %i < %stackCtrl.getCount(); %i++)
{
%btnObj = %stackCtrl.getObject(%i);
if(%btnObj.buttonName $= %name)
return %btnObj;
}
return 0;
}
function ButtonPalette::refresh(%this)
{
%stackCtrl = %this.getStackCtrl();
%stackCtrl.clear();
%this.visible = true;
%extents = "25 25";
if(%this.numButtons == 0)
{
%this.visible = false;
return;
}
for(%i=0; %i < %this.numButtons; %i++)
{
%buttonInfo = %this.buttons[%i];
%paletteButton = new GuiBitmapButtonCtrl() {
canSaveDynamicFields = "0";
internalName = getField(%buttonInfo, 0) @ "_paletteButton";
Enabled = "1";
isContainer = "0";
Profile = "ToolsGuiButtonProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = "0 0";
Extent = "25 19";
MinExtent = "8 2";
canSave = "1";
Visible = "1";
tooltipprofile = "ToolsGuiToolTipProfile";
ToolTip = getField(%buttonInfo, 4) SPC "(" @ getField(%buttonInfo, 5) @ ")";
hovertime = "1000";
bitmapAsset = getField(%buttonInfo, 1);
buttonType = "RadioButton";
useMouseEvents = "0";
buttonName = getField(%buttonInfo, 0);
command = getField(%buttonInfo, 2);
variable = getField(%buttonInfo, 3);
};
%extents.y += 23;
if(isObject(%this.actionMap))
%this.actionMap.bindCmd( keyboard, getField(%buttonInfo, 5), %paletteButton @ ".performClick();", "" );
%stackCtrl.add(%paletteButton);
}
%this.extent.y = %extents.y;
}

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

@ -554,3 +554,142 @@ function simGroup::onInspectPostApply(%this)
%this.callOnChildren("setHidden",%this.hidden);
%this.callOnChildren("setLocked",%this.locked);
}
function simGroup::SelectFiteredObjects(%this, %min, %max)
{
EWorldEditor.clearSelection();
%this.callOnChildren("filteredSelect", %min, %max );
}
function SceneObject::filteredSelect(%this, %min, %max)
{
echo("SceneObject::filteredSelect() - min: " @ %min @ " max: " @ %max);
%box = %this.getWorldBox();
%xlength = mAbs(getWord(%box,0) - getWord(%box,3));
%ylength = mAbs(getWord(%box,1) - getWord(%box,4));
%Zlength = mAbs(getWord(%box,2) - getWord(%box,5));
%diagSq = mPow(%xlength,2)+mPow(%ylength,2)+mPow(%Zlength,2);
if (%diagSq > mPow(%min,2) && %diagSq < mPow(%max,2))
{
EWorldEditor.selectObject(%this);
}
}
function simGroup::onInspect(%obj, %inspector)
{
//Find the 'Editing' group in the inspector
%group = %inspector.findExistentGroup("Editing");
if(isObject(%group))
{
//We add a field of the type 'SimGroupSelectionButton'. This isn't a 'real' type, so when the inspector group tries to add it
//it will route down through GuiInspectorGroup(the namespace of %group) and call onConstructField in an attemp to see if there's any
//script defined functions that can build a field of that type.
//We happen to define the required 'build @ <fieldTypeName> @ Field()' function below, allowing us to build out the custom field type
%group.addField("Select Objects", "SimGroupSelectionButton", "Select filtered objects");
}
}
function GuiInspectorGroup::buildSimGroupSelectionButtonField(%this, %fieldName, %fieldLabel, %fieldDesc,
%fieldDefaultVal, %fieldDataVals, %callback, %ownerObj)
{
//Set defaults if needbe
if(%ownerObj.minSize $= "")
%ownerObj.minSize = 0.1;
if(%ownerObj.maxSize $= "")
%ownerObj.maxSize = 1;
%container = new GuiControl() {
canSaveDynamicFields = "0";
Profile = "EditorContainerProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = "0 0";
Extent = "300 80";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = "";// %tooltip;
tooltipProfile = "EditorToolTipProfile";
new GuiTextCtrl() {
profile = GuiInspectorFieldProfile;
text = %fieldLabel;
Position = "16 2";
Extent = "300 18";
HorizSizing = "right";
VertSizing = "bottom";
};
new GuiControl() {
Position = "40 20";
Extent = "270 18";
HorizSizing = "right";
VertSizing = "bottom";
new GuiTextCtrl() {
profile = GuiInspectorFieldProfile;
text = "Minimum Size:";
Position = "0 2";
Extent = "150 18";
HorizSizing = "right";
VertSizing = "bottom";
};
new GuiTextEditCtrl() {
profile = GuiInspectorTextEditProfile;
variable = %ownerObj @ ".minSize";
Position = "150 2";
Extent = "150 18";
HorizSizing = "left";
VertSizing = "bottom";
};
};
new GuiControl() {
Position = "40 40";
Extent = "270 18";
HorizSizing = "right";
VertSizing = "bottom";
new GuiTextCtrl() {
profile = GuiInspectorFieldProfile;
text = "Maximum Size:";
Position = "0 2";
Extent = "150 18";
HorizSizing = "right";
VertSizing = "bottom";
};
new GuiTextEditCtrl() {
profile = GuiInspectorTextEditProfile;
variable = %ownerObj @ ".maxSize";
Position = "150 2";
Extent = "150 18";
HorizSizing = "left";
VertSizing = "bottom";
};
};
new GuiButtonCtrl() {
canSaveDynamicFields = "0";
Profile = "ToolsGuiButtonProfile";
HorizSizing = "right";
VertSizing = "bottom";
Position = "40 60";
Extent = "265 18";
MinExtent = "8 2";
canSave = "0";
Visible = "1";
hovertime = "100";
tooltip = ""; //%tooltip;
tooltipProfile = "EditorToolTipProfile";
text = "Select";
maxLength = "1024";
command = %ownerObj @ ".SelectFiteredObjects("@ %ownerObj.minSize @","@ %ownerObj.maxSize @");";
};
};
%this-->stack.add(%container);
}

View file

@ -0,0 +1,54 @@
function SubScene::onSelected(%this)
{
EWToolsPaletteWindow.clearButtons();
//Adds a button to the pallete stack
EWToolsPaletteWindow.addButton("Select", "ToolsModule:arrow_n_image", "EWorldEditorNoneModeBtn::onClick();", "", "Select Arrow", "1");
EWToolsPaletteWindow.addButton("Move", "ToolsModule:translate_n_image", "SubSceneMoveModeBtn::onClick();", "", "Move Selection", "2");
EWToolsPaletteWindow.addButton("Rotate", "ToolsModule:rotate_n_image", "SubSceneRotateModeBtn::onClick();", "", "Rotate Selection", "3");
EWToolsPaletteWindow.addButton("Scale", "ToolsModule:Scale_n_image", "EWorldEditorScaleModeBtn::onClick();", "", "Scale Selection", "4");
EWToolsPaletteWindow.addButton("SubSceneMove", "ToolsModule:translate_n_image", "SubSceneChildMoveModeBtn::onClick();", "", "Move SubScene + Children", "5");
EWToolsPaletteWindow.addButton("SubSceneRotate", "ToolsModule:rotate_n_image", "SubSceneChildRotateModeBtn::onClick();", "", "Rotate SubScene + Children", "6");
EWToolsPaletteWindow.refresh();
}
function SubScene::onUnselected(%this)
{
EWToolsPaletteWindow.clearButtons();
//Adds a button to the pallete stack
EWToolsPaletteWindow.addButton("Select", "ToolsModule:arrow_n_image", "EWorldEditorNoneModeBtn::onClick();", "", "Select Arrow", "1");
EWToolsPaletteWindow.addButton("Move", "ToolsModule:translate_n_image", "EWorldEditorMoveModeBtn::onClick();", "", "Move Selection", "2");
EWToolsPaletteWindow.addButton("Rotate", "ToolsModule:rotate_n_image", "EWorldEditorRotateModeBtn::onClick();", "", "Rotate Selection", "3");
EWToolsPaletteWindow.addButton("Scale", "ToolsModule:Scale_n_image", "EWorldEditorScaleModeBtn::onClick();", "", "Scale Selection", "4");
$SubScene::transformChildren = false;
EWToolsPaletteWindow.refresh();
}
function SubSceneMoveModeBtn::onClick(%this)
{
EWorldEditorMoveModeBtn::onClick();
$SubScene::transformChildren = false;
}
function SubSceneRotateModeBtn::onClick(%this)
{
EWorldEditorRotateModeBtn::onClick();
$SubScene::transformChildren = false;
}
function SubSceneChildMoveModeBtn::onClick()
{
EWorldEditorMoveModeBtn::onClick();
$SubScene::transformChildren = true;
}
function SubSceneChildRotateModeBtn::onClick()
{
EWorldEditorRotateModeBtn::onClick();
$SubScene::transformChildren = true;
}

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)