From 3442aceb0f9b5540f9fb4fdf0b52dfd8f913ed73 Mon Sep 17 00:00:00 2001 From: JeffR Date: Sat, 28 Sep 2024 16:09:56 -0500 Subject: [PATCH 01/74] Updates most of the handling of asset types to follow a more standardized type-registration system. This streamlines much of the code and makes it easier to find and follow how different asset, object and file types are handled by the asset browser Also clears out various bits of cruft and old commented blocks of code --- Engine/source/T3D/assets/CppAsset.cpp | 14 + Engine/source/T3D/assets/CppAsset.h | 6 +- Engine/source/assets/assetManager.cpp | 47 + Engine/source/assets/assetManager.h | 3 + .../assets/assetManager_ScriptBinding.h | 14 + .../tools/assetBrowser/guis/editAsset.gui | 2 +- .../game/tools/assetBrowser/main.tscript | 32 +- .../assetBrowser/scripts/assetBrowser.tscript | 1200 ++++++++--------- .../assetTypes/assetTypeExample.tscript | 86 -- .../scripts/assetTypes/component.tscript | 143 -- .../scripts/assetTypes/cpp.tscript | 208 +-- .../scripts/assetTypes/cubemap.tscript | 56 +- .../assetTypes/datablockObjects.tscript | 202 +-- .../scripts/assetTypes/decal.tscript | 0 .../scripts/assetTypes/explosion.tscript | 0 .../scripts/assetTypes/folder.tscript | 118 +- .../scripts/assetTypes/forest.tscript | 0 .../scripts/assetTypes/gameObject.tscript | 285 ---- .../scripts/assetTypes/genericAsset.tscript | 351 +++++ .../scripts/assetTypes/gui.tscript | 104 +- .../scripts/assetTypes/image.tscript | 480 +------ .../scripts/assetTypes/level.tscript | 112 +- .../scripts/assetTypes/looseFiles.tscript | 17 +- .../scripts/assetTypes/material.tscript | 447 +----- .../scripts/assetTypes/particle.tscript | 0 .../scripts/assetTypes/postFX.tscript | 148 +- .../scripts/assetTypes/prefab.tscript | 48 +- .../scripts/assetTypes/script.tscript | 188 +-- .../scripts/assetTypes/shape.tscript | 342 +---- .../scripts/assetTypes/shapeAnimation.tscript | 63 +- .../scripts/assetTypes/sound.tscript | 65 +- .../scripts/assetTypes/stateMachine.tscript | 161 --- .../scripts/assetTypes/terrain.tscript | 114 +- .../assetTypes/terrainMaterial.tscript | 106 +- .../scripts/directoryHandling.tscript | 33 + .../assetBrowser/scripts/editAsset.tscript | 197 +-- .../assetBrowser/scripts/newAsset.tscript | 8 +- .../assetBrowser/scripts/popupMenus.tscript | 53 +- .../tools/gui/fieldTypes/listField.tscript | 4 +- 39 files changed, 1920 insertions(+), 3537 deletions(-) delete mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/assetTypeExample.tscript delete mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/component.tscript delete mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/decal.tscript delete mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/explosion.tscript delete mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/forest.tscript delete mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/gameObject.tscript create mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/genericAsset.tscript delete mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/particle.tscript delete mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/stateMachine.tscript diff --git a/Engine/source/T3D/assets/CppAsset.cpp b/Engine/source/T3D/assets/CppAsset.cpp index b51ae1025..6d900e1ef 100644 --- a/Engine/source/T3D/assets/CppAsset.cpp +++ b/Engine/source/T3D/assets/CppAsset.cpp @@ -178,3 +178,17 @@ void CppAsset::onAssetRefresh(void) mHeaderPath = getOwned() ? expandAssetFilePath(mHeaderFile) : mHeaderPath; } + +DefineEngineMethod(CppAsset, getCodePath, const char*, (), , + "Gets the code file filepath of this asset.\n" + "@return File path of the code file.") +{ + return object->getCppFilePath(); +} + +DefineEngineMethod(CppAsset, getHeaderPath, const char*, (), , + "Gets the header file filepath of this asset.\n" + "@return File path of the header file.") +{ + return object->getHeaderFilePath(); +} diff --git a/Engine/source/T3D/assets/CppAsset.h b/Engine/source/T3D/assets/CppAsset.h index 40ad8ebfc..c7d99e81e 100644 --- a/Engine/source/T3D/assets/CppAsset.h +++ b/Engine/source/T3D/assets/CppAsset.h @@ -66,6 +66,9 @@ public: void setHeaderFile(const char* pHeaderFile); inline StringTableEntry getHeaderFile(void) const { return mHeaderFile; }; + inline StringTableEntry getCppFilePath(void) const { return mCodePath; }; + inline StringTableEntry getHeaderFilePath(void) const { return mHeaderPath; }; + protected: void initializeAsset(void) override; void onAssetRefresh(void) override; @@ -73,9 +76,6 @@ protected: static bool setCppFile(void *obj, const char *index, const char *data) { static_cast(obj)->setCppFile(data); return false; } static const char* getCppFile(void* obj, const char* data) { return static_cast(obj)->getCppFile(); } - inline StringTableEntry getCppFilePath(void) const { return mCodePath; }; - inline StringTableEntry getHeaderFilePath(void) const { return mHeaderPath; }; - static bool setHeaderFile(void *obj, const char *index, const char *data) { static_cast(obj)->setHeaderFile(data); return false; } static const char* getHeaderFile(void* obj, const char* data) { return static_cast(obj)->getHeaderFile(); } }; diff --git a/Engine/source/assets/assetManager.cpp b/Engine/source/assets/assetManager.cpp index 30efc47db..e2c6d1133 100644 --- a/Engine/source/assets/assetManager.cpp +++ b/Engine/source/assets/assetManager.cpp @@ -1563,6 +1563,53 @@ bool AssetManager::restoreAssetTags( void ) //----------------------------------------------------------------------------- +const char* AssetManager::getAssetLooseFiles(const char* pAssetId) +{ + // Debug Profiling. + PROFILE_SCOPE(AssetManager_getAssetLooseFIles); + + // Sanity! + AssertFatal(pAssetId != NULL, "Cannot look up NULL asset Id."); + + // Find asset. + AssetDefinition* pAssetDefinition = findAsset(pAssetId); + + // Did we find the asset? + if (pAssetDefinition == NULL) + { + // No, so warn. + Con::warnf("Asset Manager: Failed to find asset Id '%s' as it does not exist.", pAssetId); + return String::EmptyString; + } + + // Info. + if (mEchoInfo) + { + Con::printSeparator(); + Con::printf("Asset Manager: Started getting loose files of Asset Id '%s'...", pAssetId); + } + + String looseFileList = ""; + Vector& assetLooseFiles = pAssetDefinition->mAssetLooseFiles; + for (Vector::iterator looseFileItr = assetLooseFiles.begin(); looseFileItr != assetLooseFiles.end(); ++looseFileItr) + { + // Fetch loose file. + StringTableEntry looseFile = *looseFileItr; + + looseFileList += looseFile; + + if (looseFileItr != assetLooseFiles.end()) + looseFileList += "\t"; + } + + char* ret = Con::getReturnBuffer(1024); + dSprintf(ret, 1024, "%s", looseFileList.c_str()); + + return ret; +} + +//----------------------------------------------------------------------------- + S32 QSORT_CALLBACK descendingAssetDefinitionLoadCount(const void* a, const void* b) { // Debug Profiling. diff --git a/Engine/source/assets/assetManager.h b/Engine/source/assets/assetManager.h index 43a5a6c35..9f243fc17 100644 --- a/Engine/source/assets/assetManager.h +++ b/Engine/source/assets/assetManager.h @@ -341,6 +341,9 @@ public: bool restoreAssetTags( void ); inline AssetTagsManifest* getAssetTags( void ) const { return mAssetTagsManifest; } + /// Loose File management + const char* getAssetLooseFiles(const char* pAssetId); + /// Info. inline U32 getDeclaredAssetCount( void ) const { return (U32)mDeclaredAssets.size(); } inline U32 getReferencedAssetCount( void ) const { return (U32)mReferencedAssets.size(); } diff --git a/Engine/source/assets/assetManager_ScriptBinding.h b/Engine/source/assets/assetManager_ScriptBinding.h index ddfb3f3f2..9e675ed52 100644 --- a/Engine/source/assets/assetManager_ScriptBinding.h +++ b/Engine/source/assets/assetManager_ScriptBinding.h @@ -432,6 +432,20 @@ DefineEngineMethod(AssetManager, getAssetTags, S32, (), , //----------------------------------------------------------------------------- +DefineEngineMethod(AssetManager, getAssetLooseFiles, const char*, (const char* assetId), (""), + "Finds the specified asset Id and gets a list of its loose files.\n" + "@param assetId The selected asset Id.\n" + "@return A tab-delinated list of loose files associated to the assetId.\n") +{ + // Fetch asset Id. + const char* pAssetId = assetId; + + // Delete asset. + return object->getAssetLooseFiles(pAssetId); +} + +//----------------------------------------------------------------------------- + DefineEngineMethod(AssetManager, findAllAssets, S32, (const char* assetQuery, bool ignoreInternal, bool ignorePrivate), ("", true, true), "Performs an asset query searching for all assets optionally ignoring internal assets.\n" "@param assetQuery The asset query object that will be populated with the results.\n" diff --git a/Templates/BaseGame/game/tools/assetBrowser/guis/editAsset.gui b/Templates/BaseGame/game/tools/assetBrowser/guis/editAsset.gui index 9ac2e2ef2..6d30fea61 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/guis/editAsset.gui +++ b/Templates/BaseGame/game/tools/assetBrowser/guis/editAsset.gui @@ -75,7 +75,7 @@ $guiContent = new GuiControl(AssetBrowser_editAsset) { canSave = "1"; canSaveDynamicFields = "0"; - new GuiInspector(AssetEditInspector) { + new GuiVariableInspector(AssetEditInspector) { dividerMargin = "5"; showCustomFields = "1"; stackingType = "Vertical"; diff --git a/Templates/BaseGame/game/tools/assetBrowser/main.tscript b/Templates/BaseGame/game/tools/assetBrowser/main.tscript index d2c935100..554f8274c 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/main.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/main.tscript @@ -27,30 +27,9 @@ function initializeAssetBrowser() $AssetBrowser::collectionSetsFile = "tools/assetBrowser/searchCollectionSets.xml"; $AssetBrowser::currentImportConfig = ""; - if(!isObject(AssetFilterTypeList)) - { - new ArrayObject(AssetFilterTypeList); + if(!isObject(ABAssetTypesList)) + new ArrayObject(ABAssetTypesList){}; - AssetFilterTypeList.add("All"); - AssetFilterTypeList.add("ComponentAsset"); - AssetFilterTypeList.add("CppAsset"); - AssetFilterTypeList.add("CubemapAsset"); - AssetFilterTypeList.add("GameObjectAsset"); - AssetFilterTypeList.add("GUIAsset"); - AssetFilterTypeList.add("ImageAsset"); - AssetFilterTypeList.add("LevelAsset"); - AssetFilterTypeList.add("MaterialAsset"); - AssetFilterTypeList.add("ParticleAsset"); - AssetFilterTypeList.add("PostFXAsset"); - AssetFilterTypeList.add("ScriptAsset"); - AssetFilterTypeList.add("ShapeAsset"); - AssetFilterTypeList.add("ShapeAnimationAsset"); - AssetFilterTypeList.add("SoundAsset"); - AssetFilterTypeList.add("StateMachineAsset"); - AssetFilterTypeList.add("TerrainAsset"); - AssetFilterTypeList.add("TerrainMaterialAsset"); - } - exec("./scripts/profiles." @ $TorqueScriptFileExtension); exec("./guis/assetBrowser.gui"); @@ -89,9 +68,9 @@ function initializeAssetBrowser() exec("./scripts/setAssetTarget." @ $TorqueScriptFileExtension); //Processing for the different asset types - exec("./scripts/assetTypes/component." @ $TorqueScriptFileExtension); + exec("./scripts/assetTypes/genericAsset." @ $TorqueScriptFileExtension); + exec("./scripts/assetTypes/cpp." @ $TorqueScriptFileExtension); - exec("./scripts/assetTypes/gameObject." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/gui." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/image." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/level." @ $TorqueScriptFileExtension); @@ -100,8 +79,7 @@ function initializeAssetBrowser() exec("./scripts/assetTypes/script." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/shape." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/shapeAnimation." @ $TorqueScriptFileExtension); - exec("./scripts/assetTypes/sound." @ $TorqueScriptFileExtension); - exec("./scripts/assetTypes/stateMachine." @ $TorqueScriptFileExtension); + exec("./scripts/assetTypes/sound." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/cubemap." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/folder." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/terrain." @ $TorqueScriptFileExtension); diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript index 2575fb772..6cc4acea6 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetBrowser.tscript @@ -29,6 +29,224 @@ function AssetBrowser::addToolbarButton(%this) } // +// +function AssetBrowser::registerAssetType(%assetTypeName, %humanReadableName, %assetClassNamesList, %showInFilters) +{ + if(%showInFilters $= "") + %showInFilters = true; + + ABAssetTypesList.add("Asset", %assetTypeName TAB %humanReadableName TAB %assetClassNamesList TAB %showInFilters); +} + +function AssetBrowser::registerFileType(%fileTypeName, %humanReadableName, %fileExtensionsList, %showInFilters) +{ + if(%showInFilters $= "") + %showInFilters = true; + + ABAssetTypesList.add("File", %fileTypeName TAB %humanReadableName TAB %fileExtensionsList TAB %showInFilters); +} + +function AssetBrowser::registerObjectType(%objectTypeName, %humanReadableName, %objectClassNames, %showInFilters) +{ + if(%showInFilters $= "") + %showInFilters = true; + + ABAssetTypesList.add("Object", %objectTypeName TAB %humanReadableName TAB %objectClassNames TAB %showInFilters); +} + +function AssetBrowser::callAssetTypeFunc(%this, %type, %function, %param0, %param1, %param2, %param3, %param4) +{ + //echo("AssetBrowser::callAssetTypeFunc() - deets: " @ %type @ ", " @ %function @ ", " @ %param0 @ ", " @ %param1 @ ", " @ %param2 @ ", " @ %param3 @ ", " @ %param4); + $paramCache0 = %param0; + $paramCache1 = %param1; + $paramCache2 = %param2; + $paramCache3 = %param3; + $paramCache4 = %param4; + + $CurrentAssetBrowser = %this; + + //look up the type group + %typeGroup = ""; + %typeData = ""; + for(%i=0; %i < ABAssetTypesList.count(); %i++) + { + %group = ABAssetTypesList.getKey(%i); + %typeData = ABAssetTypesList.getValue(%i); + + if(getField(%typeData, 0) $= %type) + { + %typeGroup = %group; + %typeData = %typeData; + break; + } + } + + %paramOffset = 0; + %isStaticCall = false; + %command = ""; + if(%typeGroup $= "Asset") + { + //check to see if the first param is an asset definition + if(isObject(%param0) && %param0.isInNamespaceHierarchy("AssetBase")) + { + if(%param0.isMethod(%function)) + { + %command = %param0 @ "." @ %function @ "("; + %paramOffset = 1; + } + else + { + %command = "GenericAsset::" @ %function @ "(" @ %param0; + %paramOffset = 1; + %isStaticCall = true; + } + } + else + { + if(AssetDatabase.isDeclaredAsset(%param0)) + { + %assetDef = AssetDatabase.acquireAsset(%param0); + if(%assetDef.isMethod(%function)) + { + %command = %assetDef @ "." @ %function @ "("; + %paramOffset = 1; + } + else + { + %command = "GenericAsset::" @ %function @ "(" @ %assetDef; + %paramOffset = 1; + %isStaticCall = true; + } + } + else + { + //we may have to build the assetID + %assetId = %param0 @ ":" @ %param1; + if(AssetDatabase.isDeclaredAsset(%assetId)) + { + %assetDef = AssetDatabase.acquireAsset(%assetId); + + if(%assetDef.isMethod(%function)) + { + %command = %assetDef @ "." @ %function @ "("; + %paramOffset = 2; + } + else + { + %command = "GenericAsset::" @ %function @ "(" @ %assetDef; + %paramOffset = 2; + %isStaticCall = true; + } + } + } + } + } + else if(%typeGroup $= "File") + { + %compoundPath = %param0 @ "/" @ %param1; + if(isFile(%compoundPath) || isDirectory(%compoundPath)) + { + if(isMethod(%type, %function)) + { + %command = %type @ "::" @ %function @ "(\"" @ %compoundPath @ "\""; + %paramOffset = 2; + %isStaticCall = true; + } + else + { + %command = "GenericAsset::" @ %function @ "(\"" @ %compoundPath @ "\""; + %paramOffset = 2; + %isStaticCall = true; + } + } + else if(isFile(%param0) || isDirectory(%param0)) + { + if(isMethod(%type, %function)) + { + %command = %type @ "::" @ %function @ "(\"" @ %param0 @ "\""; + %paramOffset = 1; + %isStaticCall = true; + } + else + { + %command = "GenericAsset::" @ %function @ "(\"" @ %param0 @ "\""; + %paramOffset = 1; + %isStaticCall = true; + } + } + } + else if(%typeGroup $= "Object") + { + if(isObject(%param0) && %param0.isMethod(%function)) + { + %command = %param0 @ "." @ %function @ "("; + %paramOffset = 1; + } + else + { + if(isMethod(%type, %function)) + { + %command = %type @ "::" @ %function @ "("; + %isStaticCall = true; + } + } + } + + if(%command $= "") + { + //check in case it's a purely static type call with no usual incoming vars. + //Usually only relevent for create functions + if($paramCache0 $= "" && $paramCache1 $= "" && isMethod(%type, %function)) + { + %command = %type @ "::" @ %function @ "("; + %isStaticCall = true; + } + else if(isMethod("GenericAsset", %function)) + { + %command = "GenericAsset::" @ %function @ "("; + %isStaticCall = true; + } + else + { + error("AssetBrowser::callAssetTypeFunc() - unable to find a valid type: " @ %type @ " with function: " @ %function); + return; + } + } + + for(%i=%paramOffset; %i < 5; %i++) + { + %paramVar = getVariable("$paramCache" @ %i); + if(%paramVar !$= "") + { + if(%paramOffset != 0 && %i==%paramOffset && %isStaticCall) + %command = %command @ ","; + + if(getWordCount(%paramVar) > 1 || strPos(%paramVar, "/") != 0 || stePos(%paramVar, "\\") != 0) + %command = %command @ "\"" @ %paramVar @ "\""; + else + %command = %command @ %paramVar; + } + %nextParamVar = getVariable("$paramCache" @ %i+1); + if(%nextParamVar !$= "") + { + %command = %command @ ","; + } + } + + %command = %command @ ");"; + + //echo("AssetBrowser::callAssetTypeFunc() - executing command: " @ %command); + + %return = ""; + if(%command !$= "") + %return = eval(%command); + + $CurrentAssetBrowser = ""; + + return %return; +} + +// function AssetBrowser::initialize(%this) { // manage preview array @@ -40,11 +258,11 @@ function AssetBrowser::initialize(%this) if(!isObject(%this.dirHandler)) { - %this.dirHandler = makedirectoryHandler(AssetBrowser-->filterTree, "cache,shaderCache", ""); + %this.dirHandler = makedirectoryHandler(%this-->filterTree, "cache,shaderCache", ""); %this.dirHandler.currentAddress = "data/"; } - AssetBrowser-->filterTree.buildIconTable( ":tools/classIcons/Prefab:tools/classIcons/Prefab" @ + %this-->filterTree.buildIconTable( ":tools/classIcons/Prefab:tools/classIcons/Prefab" @ ":tools/classIcons/SimSet:tools/classIcons/SimSet"); %this.isReImportingAsset = false; @@ -61,9 +279,11 @@ function AssetBrowser::initialize(%this) //First, build our our list of active modules %modulesList = ModuleDatabase.findModules(true); - AssetBrowser-->previewSlider.setValue(EditorSettings.value("Assets/Browser/previewTileSize", "1.0")); + %this-->previewSlider.setValue(EditorSettings.value("Assets/Browser/previewTileSize", "1.0")); - AssetBrowser-->filterAssetsButton.setActive(true); + %this-->filterAssetsButton.setActive(true); + + %this.toggleAssetTypeFilter(0); } function AssetBrowser::onAdd(%this) @@ -109,11 +329,11 @@ function AssetBrowser::setTab(%this, %tab, %text, %command) function AssetBrowser::putToFront(%this) { // Close the object - AssetBrowser.hideDialog(); + %this.hideDialog(); // Create the object again so it will render on top - AssetBrowser.ShowDialog(); + %this.ShowDialog(); // Put the focus on this window - AssetBrowser.restoreLastPosExt(); + %this.restoreLastPosExt(); AssetBrowserWindow.selectWindow(); } @@ -173,7 +393,7 @@ function AssetBrowser::viewToolsModulesFilter(%this) EditorSettings.setValue("Assets/Browser/showToolsModule", %newVal); - AssetBrowser.loadDirectories(); + %this.loadDirectories(); } function AssetBrowser::viewPopulatedModulesFilter(%this) @@ -185,7 +405,7 @@ function AssetBrowser::viewPopulatedModulesFilter(%this) EditorSettings.setValue("Assets/Browser/showOnlyPopulatedModule", %newVal); - AssetBrowser.loadDirectories(); + %this.loadDirectories(); } function AssetBrowser::toggleShowingFolders(%this) @@ -197,7 +417,7 @@ function AssetBrowser::toggleShowingFolders(%this) EditorSettings.setValue("Assets/Browser/showFolders", %newVal); - AssetBrowser.loadDirectories(); + %this.loadDirectories(); } function AssetBrowser::toggleShowingEmptyFolders(%this) @@ -209,7 +429,7 @@ function AssetBrowser::toggleShowingEmptyFolders(%this) EditorSettings.setValue("Assets/Browser/showEmptyFolders", %newVal); - AssetBrowser.refresh(); + %this.refresh(); } function AssetBrowser::toggleAssetTypeFilter(%this, %assetTypeIdx) @@ -219,24 +439,24 @@ function AssetBrowser::toggleAssetTypeFilter(%this, %assetTypeIdx) //Clear existing filters if(%assetTypeIdx == 0) { - for(%i=0; %i < AssetFilterTypeList.Count() + 1; %i++) + for(%i=0; %i < ABAssetTypesList.Count(); %i++) { - AssetTypeListPopup.checkItem(%i, false); + AssetTypeListPopup.checkItem(%i+2, false); } AssetTypeListPopup.checkItem(0, true); } - else + else if(%assetTypeIdx > 1) //slot 1 is the divider so ignore it { if(%isChecked) { %anyOtherFilters = false; - for(%i=1; %i < AssetFilterTypeList.Count() + 1; %i++) + for(%i=0; %i < ABAssetTypesList.Count(); %i++) { - if(%assetTypeIdx == %i) + if(%assetTypeIdx == %i+2) continue; - if(AssetTypeListPopup.isItemChecked(%i)) + if(AssetTypeListPopup.isItemChecked(%i+1)) { %anyOtherFilters = true; break; @@ -246,9 +466,9 @@ function AssetBrowser::toggleAssetTypeFilter(%this, %assetTypeIdx) if(%isChecked && !%anyOtherFilters) { - for(%i=0; %i < AssetFilterTypeList.Count() + 1; %i++) + for(%i=0; %i < ABAssetTypesList.Count(); %i++) { - AssetTypeListPopup.checkItem(%i, false); + AssetTypeListPopup.checkItem(%i+2, false); } AssetTypeListPopup.checkItem(0, true); @@ -272,21 +492,26 @@ function AssetBrowser::toggleAssetTypeFilter(%this, %assetTypeIdx) %i--; } } - - //Update our search terms - %newSearchPhrase = ""; - for(%i=0; %i < AssetFilterTypeList.Count() + 1; %i++) + + for(%i=2; %i < AssetTypeListPopup.getItemCount(); %i++) { %isChecked = AssetTypeListPopup.isItemChecked(%i); - if(!%isChecked) continue; - - %itemText = AssetTypeListPopup.getItemText(%i); - if(%itemText $= "All") - continue; - AssetSearchTerms.add("type", %itemText); + %typeListText = AssetTypeListPopup.getItemText(%i); + + for(%t=0; %t < ABAssetTypesList.Count(); %t++) + { + %typeData = ABAssetTypesList.getValue(%t); + %typeName = getField(%typeData, 1); + + if(%typeListText $= %typeName) + { + AssetSearchTerms.add("type", getField(%typeData, 0)); + break; + } + } } %this.updateSearchTextFromFilter(); @@ -297,19 +522,19 @@ function AssetBrowser::toggleAssetTypeFilter(%this, %assetTypeIdx) // function AssetBrowser::selectAsset( %this, %asset ) { - if(AssetBrowser.selectCallback !$= "") + if(%this.selectCallback !$= "") { // The callback function should be ready to intake the returned material //eval("materialEd_previewMaterial." @ %propertyField @ " = " @ %value @ ";"); - if( AssetBrowser.returnType $= "name" ) + if( %this.returnType $= "name" ) { // TODO! %name = ""; - eval( "" @ AssetBrowser.selectCallback @ "(" @ %name @ ");"); + eval( "" @ %this.selectCallback @ "(" @ %name @ ");"); } else { - %command = "" @ AssetBrowser.selectCallback @ "(\"" @ %asset @ "\");"; + %command = "" @ %this.selectCallback @ "(\"" @ %asset @ "\");"; eval(%command); } } @@ -322,22 +547,22 @@ function AssetBrowser::selectAsset( %this, %asset ) if(isObject(Inspector)) Inspector.refresh(); - AssetBrowser.hideDialog(); + %this.hideDialog(); } function AssetBrowser::showDialog( %this, %AssetTypeFilter, %selectCallback, %targetObj, %fieldName, %returnType) { // Set the select callback - AssetBrowser.selectCallback = %selectCallback; - AssetBrowser.returnType = %returnType; - AssetBrowser.assetTypeFilter = %AssetTypeFilter; - AssetBrowser.fieldTargetObject = %targetObj; - AssetBrowser.fieldTargetName = %fieldName; + %this.selectCallback = %selectCallback; + %this.returnType = %returnType; + %this.assetTypeFilter = %AssetTypeFilter; + %this.fieldTargetObject = %targetObj; + %this.fieldTargetName = %fieldName; - Canvas.popDialog(AssetBrowser); - Canvas.pushDialog(AssetBrowser); + Canvas.popDialog(%this); + Canvas.pushDialog(%this); - AssetBrowser.setVisible(1); + %this.setVisible(1); AssetBrowserWindow.setVisible(1); AssetBrowserWindow.selectWindow(); @@ -350,11 +575,11 @@ function AssetBrowser::showDialog( %this, %AssetTypeFilter, %selectCallback, %ta //visibility filter if(%AssetTypeFilter !$= "") { - AssetBrowser-->filterAssetsButton.setActive(false); + %this-->filterAssetsButton.setActive(false); } else { - AssetBrowser-->filterAssetsButton.setActive(true); + %this-->filterAssetsButton.setActive(true); } if(%selectCallback $= "") @@ -369,30 +594,30 @@ function AssetBrowser::showDialog( %this, %AssetTypeFilter, %selectCallback, %ta %this.selectMode = 1; } - AssetBrowser.loadDirectories(); + %this.loadDirectories(); - AssetBrowser.restoreLastPosExt(); + %this.restoreLastPosExt(); } function AssetBrowser::hideDialog( %this ) { - AssetBrowser.setVisible(1); + %this.setVisible(1); AssetBrowserWindow.setVisible(1); Canvas.popDialog(AssetBrowser_addModule); Canvas.popDialog(ImportAssetWindow); - Canvas.popDialog(AssetBrowser); + Canvas.popDialog(%this); } function AssetBrowser::toggleDialog( %this ) { - if(AssetBrowser.isAwake()) + if(%this.isAwake()) { - AssetBrowser.hideDialog(); + %this.hideDialog(); } else { - AssetBrowser.showDialog(); + %this.showDialog(); } } @@ -425,114 +650,14 @@ function AssetBrowser::buildAssetPreview( %this, %asset, %moduleName ) { %assetType = getField(%asset,0); %assetName = getField(%asset, 1); - %sdfasdgah = true; - if(%assetType $= "Folder") - { - %fullPath = %moduleName !$= "" ? %moduleName @ "/" @ %assetName : %assetName; - %fullPath = strreplace(%fullPath, "/", "_"); - - if(isObject(%fullPath)) - %assetDesc = %fullPath; - else - %assetDesc = new ScriptObject(%fullPath); - - %assetDesc.dirPath = %moduleName; - %assetDesc.assetName = %assetName; - %assetDesc.description = %moduleName @ "/" @ %assetName; - %assetDesc.assetType = %assetType; - } - else if(%assetType $= "Datablock") - { - %assetDesc = %assetName; - %assetDesc.assetType = %assetType; - } - else if(%assetType $= "LooseFile") - { - %fullPath = %moduleName !$= "" ? %moduleName @ "/" @ %assetName : %assetName; - %fullPath = strreplace(%fullPath, "/", "_"); - %fullPath = strreplace(%fullPath, ".", "-"); - - if(isObject(%fullPath)) - %assetDesc = %fullPath; - else - %assetDesc = new ScriptObject(%fullPath); - - %assetDesc.dirPath = %moduleName; - %assetDesc.assetName = %assetName; - %assetDesc.description = %moduleName @ "/" @ %assetName; - %assetDesc.assetType = %assetType; - } - else if(%assetType $= "Prefab") - { - %fullPath = %moduleName !$= "" ? %moduleName @ "/" @ %assetName : %assetName; - %fullPath = strreplace(%fullPath, "/", "_"); - %fullPath = strreplace(%fullPath, ".", "-"); - - if(isObject(%fullPath)) - %assetDesc = %fullPath; - else - %assetDesc = new ScriptObject(%fullPath); - - %assetDesc.dirPath = %moduleName; - %assetDesc.assetName = %assetName; - %assetDesc.description = %moduleName @ "/" @ %assetName; - %assetDesc.assetType = %assetType; - } - else if(%assetType $= "Cpp") - { - %fullPath = %moduleName !$= "" ? %moduleName @ "/" @ %assetName : %assetName; - %fullPath = strreplace(%fullPath, "/", "_"); - %fullPath = strreplace(%fullPath, ".", "-"); - - if(isObject(%fullPath)) - %assetDesc = %fullPath; - else - %assetDesc = new ScriptObject(%fullPath); - - %assetDesc.dirPath = %moduleName; - %assetDesc.assetName = %assetName; - %assetDesc.description = %moduleName @ "/" @ %assetName; - %assetDesc.assetType = %assetType; - } - else if(%assetType $= "tscript") - { - %fullPath = %moduleName !$= "" ? %moduleName @ "/" @ %assetName : %assetName; - %fullPath = strreplace(%fullPath, "/", "_"); - %fullPath = strreplace(%fullPath, ".", "-"); - - if(isObject(%fullPath)) - %assetDesc = %fullPath; - else - %assetDesc = new ScriptObject(%fullPath); - - %assetDesc.dirPath = %moduleName; - %assetDesc.assetName = %assetName; - %assetDesc.description = %moduleName @ "/" @ %assetName; - %assetDesc.assetType = %assetType; - } - else if(%assetType $= "Creator") + if(%assetType $= "Creator") { %assetDesc = %assetName; %assetDesc.assetType = %assetType; %moduleName = %assetDesc; } } - /*%fullPath = %moduleName !$= "" ? %moduleName @ "/" @ %assetName : %assetName; - %fullPath = strreplace(%fullPath, "/", "_"); - - if(isObject(%fullPath)) - %assetDesc = %fullPath; - else - %assetDesc = new ScriptObject(%fullPath); - - %assetDesc.dirPath = %moduleName; - %assetDesc.assetName = %assetName; - %assetDesc.description = %moduleName @ "/" @ %assetName; - %assetDesc.assetType = %assetType;*/ - - //%assetName = %asset; - //%assetType = "Folder"; } %previewSize = 100 SPC 100; @@ -554,17 +679,18 @@ function AssetBrowser::buildAssetPreview( %this, %asset, %moduleName ) buttonType = "RadioButton"; buttonMargin = "0 -10"; profile = ToolsGuiDefaultIconBtnProfile; + assetBrowser = %this; }; - %previewScaleSize = AssetBrowser-->previewSlider.getValue(); + %previewScaleSize = %this-->previewSlider.getValue(); if(%previewScaleSize $= "") { %previewScaleSize = 1; - AssetBrowser-->previewSlider.setValue(1); + %this-->previewSlider.setValue(1); } - if(%previewScaleSize == 0 || startsWith(AssetBrowser.dirHandler.currentAddress, "Creator")) + if(%previewScaleSize == 0 || startsWith(%this.dirHandler.currentAddress, "Creator")) { %previewButton.iconLocation = "Left"; %previewButton.textLocation = "Right"; @@ -572,14 +698,14 @@ function AssetBrowser::buildAssetPreview( %this, %asset, %moduleName ) %previewButton.buttonMargin = "8 8"; %previewButton.textMargin = "6"; - AssetBrowser.previewListMode = true; + %this.previewListMode = true; } else { %size = %previewSize.x * %previewScaleSize; %previewButton.setextent(%size,%size + %textBottomPad); - AssetBrowser.previewListMode = false; + %this.previewListMode = false; } //%previewButton.extent = %previewSize.x + %previewBounds SPC %previewSize.y + %previewBounds + 24; @@ -589,18 +715,17 @@ function AssetBrowser::buildAssetPreview( %this, %asset, %moduleName ) if(%this.selectMode) { - %doubleClickCommand = "AssetBrowser.selectAsset( AssetBrowser.selectedAsset );"; + %doubleClickCommand = %this @ ".selectAsset( "@ %this @ ".selectedAsset );"; } else { - %doubleClickCommand = "AssetBrowser.editAsset( "@%assetDesc@" );"; + %doubleClickCommand = %this @ ".callAssetTypeFunc(" @ %assetType @ ", \"onEdit\", \"" @ %moduleName @ "\", \"" @ %assetName @ "\" );"; } %this.previewData.previewLoaded = true; - //Build out the preview - %buildCommand = %this @ ".build" @ %assetType @ "Preview(\"" @ %assetDesc @ "\"," @ %this.previewData @ ");"; - eval(%buildCommand); + //echo("AssetBrowser::buildAssetPreview() - building preview of asset type: " @ %assetType); + %this.callAssetTypeFunc(%assetType, "buildBrowserElement", %moduleName, %assetName, %this.previewData); //debug dump %tooltip = %this.previewData.tooltip; @@ -613,19 +738,20 @@ function AssetBrowser::buildAssetPreview( %this, %asset, %moduleName ) %previewButton.assetName = %assetName; %previewButton.moduleName = %moduleName; %previewButton.assetType = %assetType; + %previewButton.assetBrowser = %this; %previewButton.bitmapAsset = %this.previewData.previewImage; %previewButton.profile = "AssetBrowserPreview" @ %previewButton.assetType; %previewButton.tooltip = %this.previewData.tooltip; - %previewButton.Command = "AssetBrowser.updateSelection( $ThisControl.assetName, $ThisControl.moduleName );"; + %previewButton.Command = %this @ ".updateSelection( $ThisControl.assetName, $ThisControl.moduleName );"; %previewButton.altCommand = %doubleClickCommand; %previewButton.text = %this.previewData.assetName; %previewButton.text.originalAssetName = %this.previewData.assetName; // add to the gui control array - AssetBrowser-->assetList.add(%previewButton); + %this-->assetList.add(%previewButton); // add to the array object for reference later if(%this.previewData.previewLoaded == false) @@ -649,12 +775,12 @@ function AssetBrowser::doRefresh(%this) %this.navigateTo(%this.dirHandler.currentAddress); //Forces a clean collapse of the tree for any not-really-exposed items - %dataItem = AssetBrowser-->filterTree.findItemByName("data"); + %dataItem = %this-->filterTree.findItemByName("data"); if(%dataItem != 0) { - AssetBrowser-->filterTree.expandItem(%dataItem, false); - AssetBrowser-->filterTree.expandItem(%dataItem); + %this-->filterTree.expandItem(%dataItem, false); + %this-->filterTree.expandItem(%dataItem); } %this.dirty = false; @@ -669,15 +795,16 @@ function AssetBrowser::populatePreviewImages(%this) for(%i=0; %i < AssetPreviewArray.count(); %i++) { %previewButton = AssetPreviewArray.getKey(%i); - %type = %previewButton.assetType; + %assetType = %previewButton.assetType; echo(" - Generating preview for asset: " @ %previewButton.moduleName @ ":" @ %previewButton.assetName); - AssetBrowser.call("generate" @ %previewButton.assetType @ "PreviewImage", %previewButton); + %this.callAssetTypeFunc(%assetType, "generatePreviewImage", %previewButton.moduleName, %previewButton.assetName, %previewButton); + AssetPreviewArray.erase(%i); echo(" - done, scheduling another pass"); - AssetBrowser.schedule(32, "populatePreviewImages"); + %this.schedule(32, "populatePreviewImages"); return; } } @@ -703,33 +830,33 @@ function assetBrowserPreviewSlider::onMouseDragged(%this) function AssetBrowser::loadDirectories( %this ) { - AssetBrowser-->filterTree.clear(); + %this-->filterTree.clear(); %dataItem = AssetBrowser-->filterTree.insertItem(0, "Content"); - AssetBrowser-->filterTree.collectionsIdx = AssetBrowser-->filterTree.insertItem(1, "Collections"); + %this-->filterTree.collectionsIdx = %this-->filterTree.insertItem(1, "Collections"); - AssetBrowser-->filterTree.modulesIdx = AssetBrowser-->filterTree.insertItem(1, "Modules"); + %this-->filterTree.modulesIdx = %this-->filterTree.insertItem(1, "Modules"); %dataItem = AssetBrowser-->filterTree.insertItem(AssetBrowser-->filterTree.modulesIdx, "data"); - AssetBrowser-->filterTree.tagsIdx = AssetBrowser-->filterTree.insertItem(1, "Tags"); + %this-->filterTree.tagsIdx = %this-->filterTree.insertItem(1, "Tags"); - AssetBrowser-->filterTree.creatorIdx = AssetBrowser-->filterTree.insertItem(1, "Creator"); + %this-->filterTree.creatorIdx = %this-->filterTree.insertItem(1, "Creator"); - AssetBrowser-->filterTree.clearSelection(); + %this-->filterTree.clearSelection(); if(%this.selectMode) { //Due to a fluke in how this tracks, it overrides the current addres, so we'll //store it real fast - %curAdd = AssetBrowser.dirHandler.currentAddress; + %curAdd = %this.dirHandler.currentAddress; //Disable these for this go - AssetBrowser-->filterTree.addSelection(AssetBrowser-->filterTree.collectionsIdx); - AssetBrowser-->filterTree.addSelection(AssetBrowser-->filterTree.creatorIdx); - AssetBrowser-->filterTree.hideSelection(); - AssetBrowser-->filterTree.clearSelection(); + %this-->filterTree.addSelection(%this-->filterTree.collectionsIdx); + %this-->filterTree.addSelection(%this-->filterTree.creatorIdx); + %this-->filterTree.hideSelection(); + %this-->filterTree.clearSelection(); - AssetBrowser.dirHandler.currentAddress = %curAdd; + %this.dirHandler.currentAddress = %curAdd; } %this.dirHandler.loadFolders("data", %dataItem); @@ -744,18 +871,18 @@ function AssetBrowser::loadDirectories( %this ) //If set to, show core if(EditorSettings.value("Assets/Browser/showCoreModule", false) == 1) { - %coreItem = AssetBrowser-->filterTree.insertItem(AssetBrowser-->filterTree.modulesIdx, "core"); + %coreItem = %this-->filterTree.insertItem(%this-->filterTree.modulesIdx, "core"); %this.dirHandler.loadFolders("core", %coreItem); } //If set to, show tools if(EditorSettings.value("Assets/Browser/showToolsModule", false) == 1) { - %toolsItem = AssetBrowser-->filterTree.insertItem(AssetBrowser-->filterTree.modulesIdx, "tools"); + %toolsItem = %this-->filterTree.insertItem(%this-->filterTree.modulesIdx, "tools"); %this.dirHandler.loadFolders("tools", %toolsItem); } - AssetBrowser-->filterTree.buildVisibleTree(true); + %this-->filterTree.buildVisibleTree(true); //Remove any modules that have no assets if we have that filter on if(%this.onlyShowModulesWithAssets) @@ -765,30 +892,30 @@ function AssetBrowser::loadDirectories( %this ) { %moduleName = getWord(%modulesList, %i).ModuleId; - %moduleItemId = AssetBrowser-->filterTree.findItemByName(%moduleName); + %moduleItemId = %this-->filterTree.findItemByName(%moduleName); - if(AssetBrowser-->filterTree.isParentItem(%moduleItemId) == false) - AssetBrowser-->filterTree.removeItem(%moduleItemId); + if(%this-->filterTree.isParentItem(%moduleItemId) == false) + %this-->filterTree.removeItem(%moduleItemId); } } //special handling for selections - if(AssetBrowser.newModuleId !$= "") + if(%this.newModuleId !$= "") { - AssetBrowser-->filterTree.clearSelection(); - %newModuleItem = AssetBrowser-->filterTree.findItemByName(AssetBrowser.newModuleId); - AssetBrowser-->filterTree.selectItem(%newModuleItem); - AssetBrowser.newModuleId = ""; + %this-->filterTree.clearSelection(); + %newModuleItem = %this-->filterTree.findItemByName(%this.newModuleId); + %this-->filterTree.selectItem(%newModuleItem); + %this.newModuleId = ""; } - AssetBrowser.dirHandler.expandTreeToAddress(AssetBrowser.dirHandler.currentAddress); + %this.dirHandler.expandTreeToAddress(%this.dirHandler.currentAddress); - %selectedItem = AssetBrowser.dirHandler.getFolderTreeItemFromAddress(AssetBrowser.dirHandler.currentAddress); - AssetBrowser-->filterTree.scrollVisibleByObjectId(%selectedItem); + %selectedItem = %this.dirHandler.getFolderTreeItemFromAddress(%this.dirHandler.currentAddress); + %this-->filterTree.scrollVisibleByObjectId(%selectedItem); - AssetBrowser-->filterTree.buildVisibleTree(true); + %this-->filterTree.buildVisibleTree(true); - AssetBrowser.refresh(); + %this.refresh(); } function AssetBrowser::updateSelection( %this, %asset, %moduleName ) @@ -1145,63 +1272,8 @@ function AssetBrowser::reImportAsset(%this) // RMB context popups function AssetBrowserPreviewButton::onRightClick(%this) { - AssetBrowser.selectedAssetPreview = %this; - EditAssetPopup.assetId = %this.moduleName @ ":" @ %this.assetName; - EditAssetPopup.assetType = %this.assetType; - %assetType = %this.assetType; - - //Do some enabling/disabling of options depending on asset type - EditAssetPopup.enableItem(0, true); - EditAssetPopup.enableItem(9, true); - - //Is it an editable type? - if(%assetType $= "ImageAsset" /*|| %assetType $= "GameObjectAsset"*/ || %assetType $= "CppAsset") - { - EditAssetPopup.enableItem(0, false); - } - - //Is it an importable type? - if(%assetType $= "GameObjectAsset" || %assetType $= "ComponentAsset" || %assetType $= "GUIAsset" || %assetType $= "LevelAsset" - || %assetType $= "MaterialAsset" || %assetType $= "ParticleAsset" || %assetType $= "PostEffectAsset" || %assetType $= "ScriptAsset" - || %assetType $= "StateMachineAsset") - { - EditAssetPopup.enableItem(9, false); - } - - /*if(AssetDatabase.acquireAsset(EditAssetPopup.assetId)) - { - %assetDef = AssetDatabase.acquireAsset(EditAssetPopup.assetId); - if(%assetDef.originalFilePath $= "" || !isFile(%assetDef.originalFilePath)) - { - //if we have no noted original import file path or it's invalid - //we can't reimport either - EditAssetPopup.enableItem(9, false); - } - AssetDatabase.releaseAsset(EditAssetPopup.assetId); - }*/ - - if(%assetType $= "LevelAsset") - { - EditLevelAssetPopup.showPopup(Canvas); - } - else if(%assetType $= "Folder") - { - EditFolderPopup.dirPath = %this.moduleName @ "/" @ %this.assetName; - EditFolderPopup.showPopup(Canvas); - } - else if(%assetType $= "TerrainAsset") - { - EditTerrainAssetPopup.showPopup(Canvas); - } - else - { - EditAssetPopup.showPopup(Canvas); - } - - if(%assetType $= "Folder") - { - EditAssetPopup.assetId = %this.moduleName @ "/" @ %this.assetName; - } + %this.assetBrowser.selectedAssetPreview = %this; + %this.assetBrowser.callAssetTypeFunc(%this.assetType, "onShowActionMenu", %this.moduleName, %this.assetName); } //function AssetListPanel::onRightMouseDown(%this) @@ -1431,6 +1503,9 @@ function AssetBrowserFilterTree::onSelect(%this, %itemId) //can't select root return; + %scrollCtrl = %this.getParent(); + %scrollPos = %scrollCtrl.getScrollPosition(); + //process special cases %parentItem = %this.getParentItem(%itemId); if(%parentItem == %this.tagsIdx) @@ -1468,6 +1543,9 @@ function AssetBrowserFilterTree::onSelect(%this, %itemId) AssetBrowser.navigateTo(%breadcrumbPath); } + + //restore the scroll position + %scrollCtrl.schedule(1, "setScrollPosition", %scrollPos.x, %scrollPos.y); } function AssetBrowserFilterTree::hasAsParent(%this, %itemId, %text) @@ -1519,172 +1597,115 @@ function AssetBrowser::doRebuildAssetArray(%this) %finalAssetCount = 0; - //Add folders - if(EditorSettings.value("Assets/Browser/showFolders", true) == true) + if(!startsWith(%breadcrumbPath, "Creator")) { - %folders = getDirectoryList(%breadcrumbPath); - for(%f=0; %f < getFieldCount(%folders); %f++) + //Add folders + if(EditorSettings.value("Assets/Browser/showFolders", true) == true) { - %folderName = getField(%folders, %f); - - %searchActive = AssetSearchTerms.count() != 0; - if(%searchActive) + %folders = getDirectoryList(%breadcrumbPath); + for(%f=0; %f < getFieldCount(%folders); %f++) { - if(matchesSearch(%folderName, "Folder", "")) + %folderName = getField(%folders, %f); + + %searchActive = AssetSearchTerms.count() != 0; + if(%searchActive) { - $AssetBrowser::AssetArray.add( %breadcrumbPath, "Folder" TAB %folderName ); - continue; - } - } - else - { - //got it. - if(%folderName $= "shaderCache" || %folderName $= "cache" || %folderName $= ".git") - continue; - - if(!%this.coreModulesFilter && %folderName $= "core" && %breadcrumbPath $= "") - continue; - - if(!%this.toolsModulesFilter && %folderName $= "tools" && %breadcrumbPath $= "") - continue; - - $AssetBrowser::AssetArray.add( %breadcrumbPath, "Folder" TAB %folderName ); - } - } - } - - //now, we'll iterate through, and find the assets that are in this module, and this category - for( %i=0; %i < %numAssetsFound; %i++) - { - %assetId = %assetQuery.getAsset(%i); - - %assetPath = makeRelativePath(AssetDatabase.getAssetFilePath(%assetId)); - %assetBasePath = filePath(%assetPath); - - //clean up the path - %assetBasePath = strreplace(%assetBasePath, "//", "/"); - - %searchActive = AssetSearchTerms.count() != 0; - if(%assetBasePath $= %breadcrumbPath || (%searchActive && startsWith(%assetBasePath,%breadcrumbPath))) - { - //first, get the asset's module, as our major categories - %module = AssetDatabase.getAssetModule(%assetId); - %moduleName = %module.moduleId; - - //it's good, so test that the category is right! - %assetType = AssetDatabase.getAssetCategory(%assetId); - if(%assetType $= "") - { - %assetType = AssetDatabase.getAssetType(%assetId); - } - - //stop adding after previewsPerPage is hit - %assetName = AssetDatabase.getAssetName(%assetId); - - if(%searchActive) - { - if(matchesSearch(%assetName, %assetType)) - { - $AssetBrowser::AssetArray.add( %moduleName, %assetId); - - if(%assetType !$= "Folder") - %finalAssetCount++; - - continue; - } - } - else - { - if(AssetBrowser.assetTypeFilter !$= "") - { - %filtersCount = getWordCount(AssetBrowser.assetTypeFilter); - for(%fltrIdx = 0; %fltrIdx < %filtersCount; %fltrIdx++) + if(matchesSearch(%folderName, "Folder", "")) { - %fltr = getWord(AssetBrowser.assetTypeFilter, %fltrIdx); - if(%fltr $= %assetType) - { - $AssetBrowser::AssetArray.add( %moduleName, %assetId ); - - if(%assetType !$= "Folder") - %finalAssetCount++; + $AssetBrowser::AssetArray.add( %breadcrumbPath, "FolderObjectType" TAB %folderName ); + continue; } } - } else { //got it. - $AssetBrowser::AssetArray.add( %moduleName, %assetId ); - - if(%assetType !$= "Folder") - %finalAssetCount++; + if(%folderName $= "shaderCache" || %folderName $= "cache" || %folderName $= ".git") + continue; + + if(!%this.coreModulesFilter && %folderName $= "core" && %breadcrumbPath $= "") + continue; + + if(!%this.toolsModulesFilter && %folderName $= "tools" && %breadcrumbPath $= "") + continue; + + $AssetBrowser::AssetArray.add( %breadcrumbPath, "FolderObjectType" TAB %folderName ); } } } - } + + //now, we'll iterate through, and find the assets that are in this module, and this category + for( %i=0; %i < %numAssetsFound; %i++) + { + %assetId = %assetQuery.getAsset(%i); + + %assetPath = makeRelativePath(AssetDatabase.getAssetFilePath(%assetId)); + %assetBasePath = filePath(%assetPath); + + //clean up the path + %assetBasePath = strreplace(%assetBasePath, "//", "/"); + + %searchActive = AssetSearchTerms.count() != 0; + if(%assetBasePath $= %breadcrumbPath || (%searchActive && startsWith(%assetBasePath,%breadcrumbPath))) + { + //first, get the asset's module, as our major categories + %module = AssetDatabase.getAssetModule(%assetId); + %moduleName = %module.moduleId; + + //it's good, so test that the category is right! + %assetType = AssetDatabase.getAssetCategory(%assetId); + if(%assetType $= "") + { + %assetType = AssetDatabase.getAssetType(%assetId); + } + + //stop adding after previewsPerPage is hit + %assetName = AssetDatabase.getAssetName(%assetId); - //Add Non-Asset Scripted Objects. Datablock, etc based - %hasDBFilter = true; - if(AssetBrowser.assetTypeFilter !$= "") - { - %hasDBFilter = false; - %filterCount = getWordCount(AssetBrowser.assetTypeFilter); - for(%fltrIdx = 0; %fltrIdx < %filterCount; %fltrIdx++) - { - %fltr = getWord(AssetBrowser.assetTypeFilter, %fltrIdx); - if(%fltr $= "Datablock" || %fltr $= "Datablocks") - { - %hasDBFilter = true; - break; - } - } - } - if(%hasDBFilter && %breadcrumbPath !$= "" && isDirectory(%breadcrumbPath)) - { - %category = getWord( %breadcrumbPath, 1 ); - %dataGroup = "DataBlockGroup"; - - for ( %i = 0; %i < %dataGroup.getCount(); %i++ ) - { - %obj = %dataGroup.getObject(%i); - // echo ("Obj: " @ %obj.getName() @ " - " @ %obj.category ); - - //if ( %obj.category $= "" && %obj.category == 0 ) - // continue; - - %dbFilename = %obj.getFileName(); - %dbFilePath = filePath(%dbFilename); - - %searchActive = AssetSearchTerms.count() != 0; - if(%searchActive) - { - if(startsWith(%dbFilePath, %breadcrumbPath)) + if(%searchActive) { - %dbName = %obj.getName(); - if(matchesSearch(%dbName, "Datablock")) + if(matchesSearch(%assetName, %assetType)) { - $AssetBrowser::AssetArray.add( %dbFilename, "Datablock" TAB %dbName ); - } + $AssetBrowser::AssetArray.add( %moduleName, %assetId); + + if(%assetType !$= "Folder") + %finalAssetCount++; + + continue; + } + } + else + { + if(AssetBrowser.assetTypeFilter !$= "") + { + %filtersCount = getWordCount(AssetBrowser.assetTypeFilter); + for(%fltrIdx = 0; %fltrIdx < %filtersCount; %fltrIdx++) + { + %fltr = getWord(AssetBrowser.assetTypeFilter, %fltrIdx); + if(%fltr $= %assetType) + { + $AssetBrowser::AssetArray.add( %moduleName, %assetId ); + + if(%assetType !$= "FolderObjectType") + %finalAssetCount++; + } + } + } + else + { + //got it. + $AssetBrowser::AssetArray.add( %moduleName, %assetId ); + + if(%assetType !$= "FolderObjectType") + %finalAssetCount++; + } } } - else if(%dbFilePath $= %breadcrumbPath) - { - %dbName = %obj.getName(); - $AssetBrowser::AssetArray.add( %dbFilename, "Datablock" TAB %dbName ); - - /*%catItem = AssetBrowser-->filterTree.findItemByName(%obj.category); - - if(%catItem == 0) - AssetBrowser-->filterTree.insertItem(%scriptedItem, %obj.category, "scripted");*/ - /*%ctrl = %this.findIconCtrl( %obj.category ); - if ( %ctrl == -1 ) - { - %this.addFolderIcon( %obj.category ); - }*/ - } } - - %this.getLooseFilesInDir(); + if(!isObject(ABLooseFileArray)) + new ArrayObject(ABLooseFileArray){}; + + %nonAssetFileCount = getNonAssetFilesInDir(%this.dirHandler.currentAddress, ABLooseFileArray); %looseFiles = ABLooseFileArray.count(); for( %i=0; %i < %looseFiles; %i++) @@ -1694,136 +1715,92 @@ function AssetBrowser::doRebuildAssetArray(%this) %looseFileName = fileName(%looseFileFullPath); %looseFileExt = fileExt(%looseFileFullPath); - $AssetBrowser::AssetArray.add( %looseFilePath, "LooseFile" TAB %looseFileName ); - } - - //Prefabs - %expr = "*.prefab"; - %fullPrefabPath = findFirstFile( %breadcrumbPath @ "/" @ %expr ); - - while ( %fullPrefabPath !$= "" ) - { - %prefabPath = filePath(%fullPrefabPath); - %prefabName = fileName(%fullPrefabPath); - - %searchActive = AssetSearchTerms.count() != 0; - if(%searchActive) + //iterate over the registered types to filter out types we special handle + for(%t=0; %t < ABAssetTypesList.count(); %t++) { - if(startsWith(%prefabPath, %breadcrumbPath)) - { - if(matchesSearch(%prefabName, "Prefab")) - { - $AssetBrowser::AssetArray.add( %prefabPath, "Prefab" TAB %prefabName ); - } - } - } - else if(%prefabPath $= %breadcrumbPath) - { - $AssetBrowser::AssetArray.add( %prefabPath, "Prefab" TAB %prefabName ); - } + %typeGroup = ABAssetTypesList.getKey(%t); - %fullPrefabPath = findNextFile( %breadcrumbPath @ "/" @ %expr ); - } - - //C++ files - %cppPattern = %breadcrumbPath @ "/" @ "*.cpp"; - for (%fullCppPath = findFirstFile(%cppPattern); %fullCppPath !$= ""; %fullCppPath = findNextFile(%cppPattern)) - { - %cppPath = filePath(%fullCppPath); - %cppName = fileName(%fullCppPath); - - %searchActive = AssetSearchTerms.count() != 0; - if(%searchActive) - { - if(startsWith(%cppPath, %breadcrumbPath)) - { - if(matchesSearch(%cppName, "Cpp")) - { - $AssetBrowser::AssetArray.add( %cppPath, "Cpp" TAB %cppName ); - } - } - } - else if(%cppPath $= %breadcrumbPath) - { - $AssetBrowser::AssetArray.add( %cppPath, "Cpp" TAB %cppName ); - } - } - - //C++ Header files - %cppPattern = %breadcrumbPath @ "/" @ "*.h"; - for (%fullCppPath = findFirstFile(%cppPattern); %fullCppPath !$= ""; %fullCppPath = findNextFile(%cppPattern)) - { - %cppPath = filePath(%fullCppPath); - %cppName = fileName(%fullCppPath); - - %searchActive = AssetSearchTerms.count() != 0; - if(%searchActive) - { - if(startsWith(%cppPath, %breadcrumbPath)) - { - if(matchesSearch(%cppName, "Cpp")) - { - $AssetBrowser::AssetArray.add( %cppPath, "Cpp" TAB %cppName ); - } - } - } - else if(%cppPath $= %breadcrumbPath) - { - $AssetBrowser::AssetArray.add( %cppPath, "Cpp" TAB %cppName ); - } - } - - //script files - %tscriptPattern = %breadcrumbPath @ "/" @ "*.tscript"; - for (%fullScriptPath = findFirstFile(%tscriptPattern); %fullScriptPath !$= ""; %fullScriptPath = findNextFile(%tscriptPattern)) - { - //If it's associated to an asset, we'll want to do extra checks - %assetQuery = new AssetQuery(); - %foundAssets = AssetDatabase.findAssetLooseFile(%assetQuery, %fullScriptPath); - - if(%foundAssets != 0) - { - %doSkip = false; - %count = %assetQuery.getCount(); - for(%i=0; %i < %count; %i++) - { - %assetId = %assetQuery.getAsset(%i); - %foundAssetType = AssetDatabase.getAssetType(%assetId); - - if(%foundAssetType !$= "ScriptAsset" && %foundAssetType !$= "GUIAsset") - { - %doSkip = true; - break; - } - } + %typeEntryName = getField(ABAssetTypesList.getValue(%t), 0); + %filterDataList = getField(ABAssetTypesList.getValue(%t), 2); - if(%doSkip) - continue; - } - - %tscriptPath = filePath(%fullScriptPath); - %tscriptName = fileName(%fullScriptPath); - - %searchActive = AssetSearchTerms.count() != 0; - if(%searchActive) - { - if(startsWith(%tscriptPath, %breadcrumbPath)) + if(%typeGroup $= "Asset") { - if(matchesSearch(%tscriptName, "tscript")) + %classNameCount = getTokenCount(%filterDataList, ";"); + + for(%e=0; %e < %classNameCount; %e++) { - $AssetBrowser::AssetArray.add( %tscriptPath, "tscript" TAB %tscriptName ); - } + %className = getToken(%filterDataList, ";", %e); + + if(%className $= %looseFileExt) + { + if(matchesSearch(%looseFileName, %typeEntryName)) + $AssetBrowser::AssetArray.add( %looseFilePath, %typeEntryName TAB %looseFileName ); + } + } + } + else if(%typeGroup $= "File") + { + %extensionsCount = getTokenCount(%filterDataList, ";"); + + for(%e=0; %e < %extensionsCount; %e++) + { + %extension = getToken(%filterDataList, ";", %e); + + if(%extension $= %looseFileExt) + { + if(matchesSearch(%looseFileName, %typeEntryName)) + $AssetBrowser::AssetArray.add( %looseFilePath, %typeEntryName TAB %looseFileName ); + } + } + } + else if(%typeGroup $= "Object") + { + %classNameCount = getTokenCount(%filterDataList, ";"); + + //now we iterate over the root group to find any and all objects that match to class + for(%rgo = 0; %rgo < RootGroup.getCount(); %rgo++) + { + %rootGroupObj = RootGroup.getObject(%rgo); + %rootGroupObjPath = filePath(%rootGroupObj.getFileName()); + if(%rootGroupObjPath !$= %this.dirHandler.currentAddress) + continue; + + for(%c=0; %c < %classNameCount; %c++) + { + %className = getToken(%filterDataList, ";", %c); + + if(%rootGroupObj.isMemberOfClass(%className)) + { + if(matchesSearch(%className, %typeEntryName)) + $AssetBrowser::AssetArray.add( %rootGroupObj, %typeEntryName TAB %className ); + } + } + } + + //check the datablocks group for certainty as well` + for(%rgo = 0; %rgo < DatablockGroup.getCount(); %rgo++) + { + %dbGroupObject = DatablockGroup.getObject(%rgo); + %dbGroupObjPath = filePath(%dbGroupObject.getFileName()); + if(%dbGroupObjPath !$= %this.dirHandler.currentAddress) + continue; + + for(%c=0; %c < %classNameCount; %c++) + { + %className = getToken(%filterDataList, ";", %c); + + if(%dbGroupObject.isMemberOfClass(%className)) + { + if(matchesSearch(%className, %typeEntryName)) + $AssetBrowser::AssetArray.add( %dbGroupObject, %typeEntryName TAB %className ); + } + } + } } - } - else if(%tscriptPath $= %breadcrumbPath) - { - $AssetBrowser::AssetArray.add( %tscriptPath, "tscript" TAB %tscriptName ); } } } - - //If we've selected into the Creator section, we have special handling for that - if(startsWith(%breadcrumbPath, "Creator")) + else //If we've selected into the Creator section, we have special handling for that { //One of the creator folders was selected %creatorGroup = AssetBrowserFilterTree.getItemText(AssetBrowserFilterTree.getSelectedItem(0)); @@ -1835,9 +1812,9 @@ function AssetBrowser::doRebuildAssetArray(%this) } else { - for ( %i = 0; %i < AssetBrowser.creatorClassArray.count(); %i++ ) - { - %group = AssetBrowser.creatorClassArray.getKey( %i ); + for ( %i = 0; %i < %this.creatorClassArray.count(); %i++ ) + { + %group = %this.creatorClassArray.getKey( %i ); //Do some filter logic do skip out of groups if we're in the wrong editor mode for it %creatorEditorFilter = "WorldEditor"; @@ -1850,26 +1827,27 @@ function AssetBrowser::doRebuildAssetArray(%this) %creatorGroupKey = AssetBrowserCreatorGroupsList.getKey(%creatorGroupIndex); if ( %group $= %creatorGroup && %creatorGroupKey $= %creatorEditorFilter ) - { - %creatorObj = AssetBrowser.creatorClassArray.getValue( %i ); - %class = %creatorObj.val[0]; - %name = %creatorObj.val[1]; - %func = %creatorObj.val[2]; - + { + %creatorObj = %this.creatorClassArray.getValue( %i ); + %class = %creatorObj.val[0]; + %name = %creatorObj.val[1]; + %func = %creatorObj.val[2]; + %searchActive = AssetSearchTerms.count() != 0; if(%searchActive && !matchesSearch(%name, "Creator")) continue; - - $AssetBrowser::AssetArray.add( %name, "Creator" TAB %creatorObj ); - } - } - } + + $AssetBrowser::AssetArray.add( %name, "Creator" TAB %creatorObj ); + } + } + } } for(%i=0; %i < $AssetBrowser::AssetArray.count(); %i++) - AssetBrowser.buildAssetPreview( $AssetBrowser::AssetArray.getValue(%i), $AssetBrowser::AssetArray.getKey(%i) ); + %this.buildAssetPreview( $AssetBrowser::AssetArray.getValue(%i), $AssetBrowser::AssetArray.getKey(%i) ); + //Queue population of any non-Type Card preview images - AssetBrowser.schedule(32, "populatePreviewImages"); + %this.schedule(32, "populatePreviewImages"); AssetBrowser_FooterText.text = %finalAssetCount @ " Assets"; @@ -1878,13 +1856,14 @@ function AssetBrowser::doRebuildAssetArray(%this) { if(!AssetTypeListPopup.isItemChecked(0)) { - for(%f=1; %f < AssetFilterTypeList.Count(); %f++) + for(%f=0; %f < ABAssetTypesList.Count(); %f++) { - %isChecked = AssetTypeListPopup.isItemChecked(%f+1); + %isChecked = AssetTypeListPopup.isItemChecked(%f+2); if(%isChecked) { - %filterTypeName = AssetFilterTypeList.getKey(%f); + %filterTypeData = ABAssetTypesList.getKey(%f); + %filterTypeName = getField(%filterTypeData, 0); if(%activeTypeFilterList $= "") %activeTypeFilterList = %filterTypeName; @@ -2494,35 +2473,27 @@ function EWorldEditor::onControlDropped( %this, %payload, %position ) %assetType = %payload.assetType; - %pos = ObjectCreator.getCreateObjectPosition(); //LocalClientConnection.camera.position; - %module = %payload.moduleName; - %asset = %payload.assetName; + %createPosition = ObjectCreator.getCreateObjectPosition(); //LocalClientConnection.camera.position; + %targetPosition = EWorldEditor.unproject(%createPosition SPC 1); + %camPos = LocalClientConnection.camera.getPosition(); + %rayResult = containerRayCast(%camPos, %targetPosition, -1); - if(AssetBrowser.isMethod("on" @ %assetType @ "EditorDropped")) + %pos = ObjectCreator.getCreateObjectPosition(); + + if(%rayResult != 0) { - if(%assetType $= "Datablock") - { - %buildCommand = AssetBrowser @ ".on" @ %assetType @ "EditorDropped(" @ %asset @ ",\"" @ %pos @ "\");"; - } - else if(%assetType $= "Prefab") - { - %buildCommand = AssetBrowser @ ".on" @ %assetType @ "EditorDropped(\"" @ %module @ "/" @ %asset @ "\",\"" @ %pos @ "\");"; - } - else if(%assetType $= "Creator") - { - %buildCommand = AssetBrowser @ ".on" @ %assetType @ "EditorDropped(" @ %module @ ",\"" @ %pos @ "\");"; - } - else - { - %assetDef = AssetDatabase.acquireAsset(%module @ ":" @ %asset); - %buildCommand = AssetBrowser @ ".on" @ %assetType @ "EditorDropped(" @ %assetDef @ ",\"" @ %pos @ "\");"; - } - eval(%buildCommand); - - if(EditorSettings.value("AssetManagement/Assets/closeBrowserOnDragAction", false)) - { - AssetBrowser.hideDialog(); - } + %pos = getWords(%rayResult, 1, 3); + } + else + { + %pos = "0 0 0"; + } + + AssetBrowser.callAssetTypeFunc(%assetType, "onWorldEditorDropped", %payload.moduleName, %payload.assetName, %pos); + + if(EditorSettings.value("AssetManagement/Assets/closeBrowserOnDragAction", false)) + { + AssetBrowser.hideDialog(); } EWorldEditor.isDirty = true; @@ -2549,25 +2520,8 @@ function GuiEditor::onControlDropped(%this, %payload, %position) //dealing with an actual asset, so build the assetName %assetId = %payload.moduleName @ ":" @ %payload.assetName; %assetType = AssetDatabase.getAssetType(%assetId); - - if(AssetBrowser.isMethod("on" @ %assetType @ "GUIEditorDropped")) - { - %module = %payload.moduleName; - %asset = %payload.assetName; - %assetDef = AssetDatabase.acquireAsset(%module @ ":" @ %asset); - %buildCommand = AssetBrowser @ ".on" @ %assetType @ "GUIEditorDropped(" @ %assetDef @ ",\"" @ %pos @ "\");"; - eval(%buildCommand); - } - else - { - //fallback example - if(%assetType $= "ImageAsset") - { - %cmd = "return new guiBitmapCtrl();"; - %ctrl = eval( %cmd ); - %ctrl.bitmap = %assetId; - } - } + + AssetBrowser.callAssetTypeFunc(%assetType, "GUIEditorDropped", %payload.moduleName, %payload.assetName, %pos); } else { @@ -2616,38 +2570,18 @@ function AssetBrowserFilterTree::onControlDropped( %this, %payload, %position ) if(%path !$= AssetBrowser.dirHandler.CurrentAddress) { - //we're trying to move the asset to a different module! - //toolsMessageBoxYesNo( "Move Asset", "Do you wish to move asset " @ %assetName @ " to " @ %path @ "?", - // "AssetBrowser.moveAsset(\""@ %moduleName @ ":" @ %assetName @"\", \""@%path@"\");", ""); - - if(%assetType $= "Folder") + if(getModuleFromAddress(%path) !$= getModuleFromAddress(AssetBrowser.dirHandler.CurrentAddress)) { - %originFolder = %moduleName @ "/" @ %assetName; - %path = %path @ "/" @ %assetName; - - //Do any cleanup required given the type - if(AssetBrowser.isMethod("moveFolder")) - { - eval(AssetBrowser @ ".moveFolder(\""@%originFolder@"\",\""@%path@"\");"); - - AssetBrowser.refresh(); - } + AssetBrowser.callAssetTypeFunc(%this.assetType, "onMoveModule", %this.moduleName, %this.assetName, %path); } else { - %assetId = %moduleName @ ":" @ %assetName; - %assetDef = AssetDatabase.acquireAsset(%assetId); - %assetType = AssetDatabase.getAssetType(%assetId); - - //Do any cleanup required given the type - if(AssetBrowser.isMethod("move"@%assetType)) - { - %command = AssetBrowser @ ".move" @ %assetType @ "(" @ %assetDef @ ",\"" @ %path @ "\");"; - eval(AssetBrowser @ ".move" @ %assetType @ "(" @ %assetDef @ ",\"" @ %path @ "\");"); - - AssetBrowser.refresh(); - } + AssetBrowser.callAssetTypeFunc(%this.assetType, "onMovePath", %this.moduleName, %this.assetName, %path); } + + //we're trying to move the asset to a different module! + //toolsMessageBoxYesNo( "Move Asset", "Do you wish to move asset " @ %assetName @ " to " @ %path @ "?", + // "AssetBrowser.moveAsset(\""@ %moduleName @ ":" @ %assetName @"\", \""@%path@"\");", ""); } } } @@ -2760,6 +2694,44 @@ function AssetBrowser::getLooseFilesInDir(%this) %aq.delete(); return false; } + +function getNonAssetFilesInDir(%path, %arrayObj) +{ + //echo("getNonAssetFilesInDir() - path: " @ %path); + + if(!isObject(%arrayObj)) + { + error("getNonAssetFilesInDir() - requires a valid arrayObject to place found non-Asset files into!"); + return 0; + } + + %arrayObj.empty(); + + //First, wipe out any files inside the folder first + %file = findFirstFileMultiExpr( %path @ "/*.*", false); + + %aq = new AssetQuery(); + + while( %file !$= "" ) + { + if(!strIsMatchExpr("*.asset.taml", %file) && !strIsMatchExpr("*.taml", %file) && !strIsMatchExpr("*.cached.dts", %file)) + { + %assetsFound = AssetDatabase.findAssetLooseFile(%aq, %file); + + if(%assetsFound == 0) + { + %arrayObj.add(%file); + } + } + + %file = findNextFileMultiExpr( %path @ "/*.*" ); + } + + %aq.delete(); + + return %arrayObj.count(); +} + // // function AssetBrowser::importLooseFiles(%this) diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/assetTypeExample.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/assetTypeExample.tscript deleted file mode 100644 index 907bb851c..000000000 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/assetTypeExample.tscript +++ /dev/null @@ -1,86 +0,0 @@ - -//Builds the preview data of the asset for the Asset Browser -function AssetBrowser::build_AssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = ""; - %previewData.doubleClickCommand = ""; - - %previewData.previewImage = "tools/assetBrowser/art/gameObjectIcon"; - - %previewData.assetFriendlyName = %assetDef.gameObjectName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %assetDef.gameObjectName; -} - -//Some last-step setup for the creation of the new asset before we do the actual making -//This is generally intended to just set up any type-specific fields for creation -function AssetBrowser::setupCreateNew_Asset(%this) -{ -} - -//Performs the actual creation of the asset, including loose file copy or generation -function AssetBrowser::create_Asset(%this) -{ -} - -//This is a pre-process step to prepare the asset item for import -function AssetBrowser::prepareImport_Asset(%this, %assetItem) -{ - -} - -//Performs the action of actually importing the asset item -function AssetBrowser::import_Asset(%this, %assetDef) -{ -} - -//Editing the asset -function AssetBrowser::edit_Asset(%this, %assetDef) -{ -} - -//Duplicates the asset -function AssetBrowser::duplicate_Asset(%this, %assetDef, %targetModule) -{ -} - -//Renames the asset -function AssetBrowser::rename_Asset(%this, %assetDef, %newAssetName) -{ -} - -//Deletes the asset -function AssetBrowser::delete_Asset(%this, %assetDef) -{ -} - -//Moves the asset to a new path/module -function AssetBrowser::move_Asset(%this, %assetDef, %destinationFolder) -{ -} - -//Drag and drop action onto a GUI control -function AssetBrowser::dragAndDrop_Asset(%this, %assetDef, %dropTarget) -{ - if(!isObject(%dropTarget)) - return; -} - -//Even for when -function AssetBrowser::on_AssetEditorDropped(%this, %assetDef, %position) -{ -} - -//Clicking of the button for the asset type inspector field -function GuiInspectorType_AssetPtr::onClick( %this, %fieldName ) -{ - //Get our data - %obj = %this.getInspector().getInspectObject(0); -} - -//Drag and droppin onto the asset type inspector field -function GuiInspectorType_AssetPtr::onControlDropped( %this, %payload, %position ) -{ - -} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/component.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/component.tscript deleted file mode 100644 index d443157ea..000000000 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/component.tscript +++ /dev/null @@ -1,143 +0,0 @@ -function AssetBrowser::createComponentAsset(%this) -{ - %moduleName = AssetBrowser.newAssetSettings.moduleName; - %modulePath = "data/" @ %moduleName; - - %assetName = AssetBrowser.newAssetSettings.assetName; - - %tamlpath = %modulePath @ "/components/" @ %assetName @ ".asset.taml"; - %scriptPath = %modulePath @ "/components/" @ %assetName @ "." @ $TorqueScriptFileExtension; - - %asset = new ComponentAsset() - { - AssetName = %assetName; - versionId = 1; - componentName = %assetName; - componentClass = AssetBrowser.newAssetSettings.parentClass; - friendlyName = AssetBrowser.newAssetSettings.friendlyName; - componentType = AssetBrowser.newAssetSettings.componentGroup; - description = AssetBrowser.newAssetSettings.description; - scriptFile = %assetName @ "." @ $TorqueScriptFileExtension; - }; - - TamlWrite(%asset, %tamlpath); - - %file = new FileObject(); - %templateFile = new FileObject(); - - %templateCodeFilePath = %this.templateFilesPath @ "componentFile." @ $TorqueScriptFileExtension @ ".template"; - - if(%file.openForWrite(%scriptPath) && %templateFile.openForRead(%templateCodeFilePath)) - { - while( !%templateFile.isEOF() ) - { - %line = %templateFile.readline(); - %line = strreplace( %line, "@@", %assetName ); - - %file.writeline(%line); - echo(%line); - } - - %file.close(); - %templateFile.close(); - } - else - { - %file.close(); - %templateFile.close(); - - warnf("CreateComponentAsset - Something went wrong and we couldn't write the Component script file!"); - } - - Canvas.popDialog(AssetBrowser_newComponentAsset); - - %moduleDef = ModuleDatabase.findModule(%moduleName, 1); - AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); - - AssetBrowser.refresh(); - - return %tamlpath; -} - -function AssetBrowser::editComponentAsset(%this, %assetDef) -{ - %scriptFile = %assetDef.scriptFile; - - EditorOpenFileInTorsion(makeFullPath(%scriptFile), 0); -} - -function AssetBrowser::duplicateComponentAsset(%this, %assetId) -{ - -} - -function AssetBrowser::renameComponentAsset(%this, %assetDef, %newAssetId, %originalName, %newName) -{ - %assetPath = AssetDatabase.getAssetFilePath(%newAssetId); - - //rename the file to match - %path = filePath(%assetPath); - - %oldScriptFilePath = %assetDef.scriptFile; - %scriptFilePath = filePath(%assetDef.scriptFile); - %scriptExt = fileExt(%assetDef.scriptFile); - - %newScriptFileName = %scriptFilePath @ "/" @ %newName @ %scriptExt; - %newAssetFile = %path @ "/" @ %newName @ ".asset.taml"; - - %assetDef.componentName = %newName; - %assetDef.scriptFile = %newScriptFileName; - - TamlWrite(%assetDef, %newAssetFile); - fileDelete(%assetPath); - - pathCopy(%oldScriptFilePath, %newScriptFileName); - fileDelete(%oldScriptFilePath); - - //Go through our scriptfile and replace the old namespace with the new - %editedFileContents = ""; - - %file = new FileObject(); - if ( %file.openForRead( %newScriptFileName ) ) - { - while ( !%file.isEOF() ) - { - %line = %file.readLine(); - %line = trim( %line ); - - %editedFileContents = %editedFileContents @ strreplace(%line, %originalName, %newName) @ "\n"; - } - - %file.close(); - } - - if(%editedFileContents !$= "") - { - %file.openForWrite(%newScriptFileName); - - %file.writeline(%editedFileContents); - - %file.close(); - } - - exec(%newScriptFileName); -} - -//not used -function AssetBrowser::importComponentAsset(%this, %assetId) -{ - -} - -function AssetBrowser::buildComponentAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.scriptFile; - %previewData.doubleClickCommand = "EditorOpenFileInTorsion( "@%previewData.assetPath@", 0 );"; - - %previewData.previewImage = "ToolsModule:componentIcon_image"; - - %previewData.assetFriendlyName = %assetDef.friendlyName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %assetDef.friendlyName @ "\n" @ %assetDef; -} diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cpp.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cpp.tscript index da45e2059..ce28531c6 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cpp.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cpp.tscript @@ -1,44 +1,21 @@ -function AssetBrowser::buildCppPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.codeFilePath; - %previewData.doubleClickCommand = "echo(\"Not yet implemented to edit C++ files from the editor\");";//"EditorOpenFileInTorsion( "@%previewData.assetPath@", 0 );"; - - %previewData.previewImage = "ToolsModule:cppIcon_image"; - - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %assetDef.assetName; -} +AssetBrowser::registerAssetType("CppAsset", "C++ Assets", ""); +AssetBrowser::registerFileType("CPPFileType", "C++ Files", ".cpp;.h;.c", false); -function AssetBrowser::buildCppAssetPreview(%this, %assetDef, %previewData) +function CppAsset::onCreateNew() { - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.codeFilePath; - %previewData.doubleClickCommand = "echo(\"Not yet implemented to edit C++ files from the editor\");";//"EditorOpenFileInTorsion( "@%previewData.assetPath@", 0 );"; - - %previewData.previewImage = "ToolsModule:cppIcon_image"; - - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %assetDef.assetName; -} - -function AssetBrowser::createCppAsset(%this) -{ - %moduleName = AssetBrowser.newAssetSettings.moduleName; + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; - %assetName = AssetBrowser.newAssetSettings.assetName; + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; %assetPath = NewAssetTargetAddress.getText() @ "/"; - //%tamlpath = %assetPath @ %assetName @ ".asset.taml"; + %tamlpath = %assetPath @ %assetName @ ".asset.taml"; %codePath = %assetPath @ %assetName @ ".cpp"; %headerPath = %assetPath @ %assetName @ ".h"; //Do the work here - /*%assetType = AssetBrowser.newAssetSettings.assetType; + //%assetType = $CurrentAssetBrowser.newAssetSettings.assetType; %asset = new CppAsset() { @@ -48,7 +25,7 @@ function AssetBrowser::createCppAsset(%this) headerFile = %headerPath; }; - TamlWrite(%asset, %tamlpath);*/ + TamlWrite(%asset, %tamlpath); %tamlpath = %assetPath @ %assetName @ ".asset.taml"; @@ -67,28 +44,28 @@ function AssetBrowser::createCppAsset(%this) if($AssetBrowser::newAssetTypeOverride $= "StaticClass") { - %cppTemplateCodeFilePath = %this.templateFilesPath @ "CppStaticClassFile.cpp.template"; - %cppTemplateHeaderFilePath = %this.templateFilesPath @ "CppStaticClassFile.h.template"; + %cppTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppStaticClassFile.cpp.template"; + %cppTemplateHeaderFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppStaticClassFile.h.template"; } else if($AssetBrowser::newAssetTypeOverride $= "ScriptClass") { - %cppTemplateCodeFilePath = %this.templateFilesPath @ "CppScriptClassFile.cpp.template"; - %cppTemplateHeaderFilePath = %this.templateFilesPath @ "CppScriptClassFile.h.template"; + %cppTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppScriptClassFile.cpp.template"; + %cppTemplateHeaderFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppScriptClassFile.h.template"; } else if($AssetBrowser::newAssetTypeOverride $= "AssetTypeCppClass") { - %cppTemplateCodeFilePath = %this.templateFilesPath @ "CppAssetTypeClassFile.cpp.template"; - %cppTemplateHeaderFilePath = %this.templateFilesPath @ "CppAssetTypeClassFile.h.template"; + %cppTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppAssetTypeClassFile.cpp.template"; + %cppTemplateHeaderFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppAssetTypeClassFile.h.template"; } else if($AssetBrowser::newAssetTypeOverride $= "RenderCppClass") { - %cppTemplateCodeFilePath = %this.templateFilesPath @ "CppRenderClassFile.cpp.template"; - %cppTemplateHeaderFilePath = %this.templateFilesPath @ "CppRenderClassFile.h.template"; + %cppTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppRenderClassFile.cpp.template"; + %cppTemplateHeaderFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppRenderClassFile.h.template"; } else if($AssetBrowser::newAssetTypeOverride $= "SceneObjectCppClass") { - %cppTemplateCodeFilePath = %this.templateFilesPath @ "CppSceneObjectClassFile.cpp.template"; - %cppTemplateHeaderFilePath = %this.templateFilesPath @ "CppSceneObjectClassFile.h.template"; + %cppTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppSceneObjectClassFile.cpp.template"; + %cppTemplateHeaderFilePath = $CurrentAssetBrowser.templateFilesPath @ "CppSceneObjectClassFile.h.template"; } $AssetBrowser::newAssetTypeOverride = ""; @@ -101,7 +78,6 @@ function AssetBrowser::createCppAsset(%this) %line = strreplace( %line, "@", %assetName ); %file.writeline(%line); - //echo(%line); } %file.close(); @@ -134,7 +110,7 @@ function AssetBrowser::createCppAsset(%this) %file.close(); %templateFile.close(); - warnf("CreateNewCppAsset - Something went wrong and we couldn't write the C++ header file!"); + warn("CreateNewCppAsset - Something went wrong and we couldn't write the C++ header file!"); } //Last, check that we have a C++ Module definition. If not, make one so anything important can be initialized on startup there @@ -144,7 +120,7 @@ function AssetBrowser::createCppAsset(%this) %file = new FileObject(); %templateFile = new FileObject(); - if(%file.openForWrite(%cppModuleFilePath) && %templateFile.openForRead(%this.templateFilesPath @ "CppModuleFile.cpp")) + if(%file.openForWrite(%cppModuleFilePath) && %templateFile.openForRead($CurrentAssetBrowser.templateFilesPath @ "CppModuleFile.cpp")) { while( !%templateFile.isEOF() ) { @@ -163,57 +139,133 @@ function AssetBrowser::createCppAsset(%this) %file.close(); %templateFile.close(); - warnf("CreateNewCppAsset - Something went wrong and we couldn't write the C++ module file!"); + warn("CreateNewCppAsset - Something went wrong and we couldn't write the C++ module file!"); } } return ""; } -function AssetBrowser::editCppAsset(%this, %assetDef) +function CppAsset::buildBrowserElement(%this, %previewData) { + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.codeFilePath; + //%previewData.doubleClickCommand = "echo(\"Not yet implemented to edit C++ files from the editor\");";//"EditorOpenFileInTorsion( "@%previewData.assetPath@", 0 );"; + + %previewData.previewImage = "ToolsModule:cppIcon_image"; + + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = %this.assetName; } -//Renames the asset -function AssetBrowser::renameCppAsset(%this, %assetDef, %newAssetName) +function CppAsset::onEditCodeFile(%this, %filePath) { - %newCodeLooseFilename = renameAssetLooseFile(%assetDef.codefile, %newAssetName); - - if(!%newCodeLooseFilename $= "") - return; - - %newHeaderLooseFilename = renameAssetLooseFile(%assetDef.headerFile, %newAssetName); - - if(!%newHeaderLooseFilename $= "") - return; - - %assetDef.codefile = %newCodeLooseFilename; - %assetDef.headerFile = %newHeaderLooseFilename; - %assetDef.saveAsset(); - - renameAssetFile(%assetDef, %newAssetName); + if(isFunction("systemCommand")) + eval("systemCommand(\"start \\\"\\\" \\\"" @ %this.getCodeFile() @ "\\\"\");"); + else + warn("CppAsset::onEditCodeFile() - systemCommand function disabled in this build. Unable to launch application to edit file."); } -//Deletes the asset -function AssetBrowser::deleteCppAsset(%this, %assetDef) +function CppAsset::onEditHeaderFile(%this, %filePath) { - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); + if(isFunction("systemCommand")) + eval("systemCommand(\"start \\\"\\\" \\\"" @ %this.getHeaderFile() @ "\\\"\");"); + else + warn("CppAsset::onEditHeaderFile() - systemCommand function disabled in this build. Unable to launch application to edit file."); } -//Moves the asset to a new path/module -function AssetBrowser::moveCppAsset(%this, %assetDef, %destination) +function CppAsset::onShowActionMenu(%this) { - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); + GenericAsset::onShowActionMenu(%this); - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; + if( !isObject( EditCPPFilesSubmenuPopup ) ) + { + new PopupMenu( EditCPPFilesSubmenuPopup ) + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; - moveAssetLooseFile(%assetDef.codeFile, %destination); - moveAssetLooseFile(%assetDef.headerFile, %destination); + jumpFileName = ""; + jumpLineNumber = ""; + }; + } - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); + //Regen the menu so we're fully up and current with options and references + EditCPPFilesSubmenuPopup.clearItems(); + + EditCPPFilesSubmenuPopup.item[ 0 ] = "Code file" TAB "" TAB %this @ ".onEditCodeFile();"; + EditCPPFilesSubmenuPopup.item[ 1 ] = "Header file" TAB "" TAB %this @ ".onEditHeaderFile();"; + + EditCPPFilesSubmenuPopup.reloadItems(); + + EditAssetPopup.item[ 0 ] = "Edit C++ Files" TAB EditCPPFilesSubmenuPopup; + + EditAssetPopup.reloadItems(); + + EditAssetPopup.showPopup(Canvas); + + $CurrentAssetBrowser.popupMenu = EditAssetPopup; } +//============================================================================== +function CPPFileType::buildBrowserElement(%filePath, %previewData) +{ + %previewData.assetName = fileName(%filePath); + %previewData.assetPath = %filePath; + //%previewData.doubleClickCommand = "echo(\"Not yet implemented to edit C++ files from the editor\");";//"EditorOpenFileInTorsion( "@%previewData.assetPath@", 0 );"; + + %previewData.previewImage = "ToolsModule:cppIcon_image"; + + %previewData.assetFriendlyName = %previewData.assetName; + %previewData.assetDesc = %filePath; + %previewData.tooltip = %filePath; +} + +function CPPFileType::onEdit(%filePath) +{ + if(isFunction("systemCommand")) + eval("systemCommand(\"start \\\"\\\" \\\"" @ %filePath @ "\\\"\");"); + else + warn("CPPFileType::onEdit() - systemCommand function disabled in this build. Unable to launch application to edit file."); +} + +function CPPFileType::onShowActionMenu(%filePath) +{ + if( !isObject( EditCPPFileTypePopup ) ) + { + new PopupMenu( EditCPPFileTypePopup ) + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; + + jumpFileName = ""; + jumpLineNumber = ""; + }; + } + + //Regen the menu so we're fully up and current with options and references + EditCPPFileTypePopup.clearItems(); + + EditCPPFileTypePopup.item[ 0 ] = "Edit C++ File" TAB "" TAB $CurrentAssetBrowser @ ".editAsset();"; + EditCPPFileTypePopup.item[ 1 ] = "Rename File" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();"; + EditCPPFileTypePopup.item[ 2 ] = "-"; + EditCPPFileTypePopup.item[ 3 ] = "Duplicate File" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();"; + EditCPPFileTypePopup.item[ 4 ] = "-"; + EditCPPFileTypePopup.item[ 5 ] = "Open File Location" TAB "" TAB $CurrentAssetBrowser @ ".openFileLocation();"; + EditCPPFileTypePopup.item[ 6 ] = "-"; + EditCPPFileTypePopup.item[ 7 ] = "Delete File" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();"; + + EditCPPFileTypePopup.objectData = %filePath; + EditCPPFileTypePopup.objectType = "CPPFileType"; + + EditCPPFileTypePopup.reloadItems(); + + EditCPPFileTypePopup.showPopup(Canvas); + + $CurrentAssetBrowser.popupMenu = EditCPPFileTypePopup; +} + +function CPPFileType::onEditProperties(%this, %inspector) +{ + +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cubemap.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cubemap.tscript index 95872ca8d..067ececc5 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cubemap.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/cubemap.tscript @@ -1,9 +1,11 @@ -function AssetBrowser::createCubemapAsset(%this) +AssetBrowser::registerAssetType("CubemapAsset", "Cubemaps"); + +function CubemapAsset::onCreateNew() { Canvas.pushDialog(CubemapEditor); return; - %moduleName = AssetBrowser.newAssetSettings.moduleName; + /*%moduleName = AssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; %assetName = AssetBrowser.newAssetSettings.assetName; @@ -30,57 +32,13 @@ function AssetBrowser::createCubemapAsset(%this) AssetBrowser.refresh(); - return %tamlpath; + return %tamlpath;*/ } -function AssetBrowser::editCubemapAsset(%this, %assetDef) +function CubemapAsset::onEdit(%this) { %this.hideDialog(); - CubemapEditor.openCubemapAsset(%assetDef); -} - -//Renames the asset -function AssetBrowser::renameCubemapAsset(%this, %assetDef, %newAssetName) -{ - /*%newCodeLooseFilename = renameAssetLooseFile(%assetDef.codefile, %newAssetName); - - if(!%newCodeLooseFilename $= "") - return; - - %newHeaderLooseFilename = renameAssetLooseFile(%assetDef.headerFile, %newAssetName); - - if(!%newHeaderLooseFilename $= "") - return; - - %assetDef.codefile = %newCodeLooseFilename; - %assetDef.headerFile = %newHeaderLooseFilename; - %assetDef.saveAsset(); - - renameAssetFile(%assetDef, %newAssetName);*/ -} - -//Deletes the asset -function AssetBrowser::deleteCubemapAsset(%this, %assetDef) -{ - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); -} - -//Moves the asset to a new path/module -function AssetBrowser::moveCubemapAsset(%this, %assetDef, %destination) -{ - /*%currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.codeFile, %destination); - moveAssetLooseFile(%assetDef.headerFile, %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath);*/ + CubemapEditor.openCubemapAsset(%this); } function GuiInspectorTypeCubemapAssetPtr::onControlDropped( %this, %payload, %position ) diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/datablockObjects.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/datablockObjects.tscript index c9ee2c2d6..d46aeac57 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/datablockObjects.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/datablockObjects.tscript @@ -1,54 +1,29 @@ -function AssetBrowser::createNewDatablock(%this) -{ - AssetBrowser_newFolderNameTxt.text = "NewFolder"; - Canvas.pushDialog(AssetBrowser_newFolder); -} +AssetBrowser::registerObjectType("DatablockObjectType", "Datablock Objects", "GameBaseData"); -function AssetBrowser::doCreateNewDatablock(%this) +function DatablockObjectType::onEdit(%this, %className) { - %newFolderName = AssetBrowser_newFolderNameTxt.getText(); - - if(%newFolderName $= "") - %newFolderName = "NewFolder"; - - %newFolderIdx = ""; - %matched = true; - %newFolderPath = ""; - while(%matched == true) + if(EditorSettings.value("Assets/Browser/doubleClickAction", "Edit Asset") $= "Edit Asset") { - %newFolderPath = AssetBrowser.dirHandler.currentAddress @ "/" @ %newFolderName @ %newFolderIdx; - if(!isDirectory(%newFolderPath)) - { - %matched = false; - } - else - { - %newFolderIdx++; - } + $CurrentAssetBrowser.hideDialog(); + + DatablockEditorPlugin.openDatablock(%this); } - - //make a dummy file - %file = new FileObject(); - %file.openForWrite(%newFolderPath @ "/test"); - %file.close(); - - fileDelete(%newFolderPath @ "/test"); - - //refresh the directory - AssetBrowser.loadDirectories(); - - %this.navigateTo(%newFolderPath); + else + { + %this.onWorldEditorDropped(); + } } -function AssetBrowser::buildDatablockPreview(%this, %assetDef, %previewData) +function DatablockObjectType::buildBrowserElement(%this, %className, %previewData) { - %previewData.assetName = %assetDef; - %previewData.assetPath = ""; + echo("DatablockObjectType::buildBrowserElement() - " @ %this @ ", " @ %previewData); + %previewData.assetName = %this.getName(); + %previewData.assetPath = %this.getFileName(); %previewData.previewImage = "ToolsModule:datablockIcon_image"; //Lets see if we have a icon for specifically for this datablock type - %dataClass = %assetDef.getClassName(); + %dataClass = %this.getClassName(); %dataClass = strreplace(%dataClass, "Data", ""); %previewImage = "tools/classIcons/" @ %dataClass @ ".png"; @@ -58,27 +33,42 @@ function AssetBrowser::buildDatablockPreview(%this, %assetDef, %previewData) } //%previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef; - %previewData.tooltip = "\nDatablock Name: " @ %assetDef @ - "\nDatablock Type: " @ %assetDef.getClassName() @ - "\nDefinition Path: " @ %assetDef.getFilename(); + %previewData.assetDesc = %this; + %previewData.tooltip = "\nDatablock Name: " @ %previewData.assetName @ + "\nDatablock Type: " @ %dataClass @ + "\nDefinition Path: " @ %previewData.assetPath; +} + +function DatablockObjectType::onShowActionMenu(%this, %className) +{ + if( !isObject( EditDatablockObjectTypePopup ) ) + { + new PopupMenu( EditDatablockObjectTypePopup ) + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; + + jumpFileName = ""; + jumpLineNumber = ""; + }; + } - if(%this.selectMode) - { - %previewData.doubleClickCommand = "AssetBrowser.selectAsset( AssetBrowser.selectedAsset );"; - } - else - { - if(EditorSettings.value("Assets/Browser/doubleClickAction", "Edit Asset") $= "Edit Asset") - { - %previewData.doubleClickCommand = "DatablockEditorPlugin.openDatablock( "@%assetDef@" );"; - } - else - { - %previewData.doubleClickCommand = "AssetBrowser.onDatablockEditorDropped( "@%assetDef@" );"; - } - } + EditDatablockObjectTypePopup.objectData = %this; + EditDatablockObjectTypePopup.objectType = "DatablockObjectType"; + + //Regen the menu so we're fully up and current with options and references + EditDatablockObjectTypePopup.clearItems(); + + EditDatablockObjectTypePopup.item[ 0 ] = "Edit Datablock" TAB "" TAB $CurrentAssetBrowser @ ".editAsset();"; + EditDatablockObjectTypePopup.item[ 1 ] = "-"; + EditDatablockObjectTypePopup.item[ 2 ] = "Open Datablock Location" TAB "" TAB $CurrentAssetBrowser @ ".openFolderLocation(" @ %this.getFilename() @ ");"; + + EditDatablockObjectTypePopup.reloadItems(); + + EditDatablockObjectTypePopup.showPopup(Canvas); + + $CurrentAssetBrowser.popupMenu = EditDatablockObjectTypePopup; } function spawnDatablockObject(%datablock) @@ -92,96 +82,8 @@ function spawnDatablockObject(%datablock) return eval(%createCmd);//eval("showImportDialog( \"" @ %shapePath @ "\", \"" @ %createCmd @ "\" );"); } -function AssetBrowser::renameDatablock(%this, %folderPath, %newFolderName) +function DatablockObjectType::onWorldEditorDropped(%this, %position) { - %fullPath = makeFullPath(%folderPath); - %newFullPath = makeFullPath(%folderPath); - - %fullPath = strreplace(%fullPath, "//", "/"); - - %count = getTokenCount(%fullPath, "/"); - %basePath = getTokens(%fullPath, "/", 0, %count-2); - %oldName = getToken(%fullPath, "/", %count-1); - - //We need to ensure that no files are 'active' while we try and clean up behind ourselves with the delete action - //so, we nix any assets active for the module, do the delete action on the old folder, and then re-acquire our assets. - //This will have the added benefit of updating paths for asset items - - %module = AssetBrowser.dirHandler.getModuleFromAddress(AssetBrowser.dirHandler.currentAddress); - %moduleId = %module.ModuleId; - - AssetDatabase.removeDeclaredAssets(%moduleId); - - %copiedSuccess = %this.dirHandler.copyDatablock(%fullPath, %basePath @ "/" @ %newFolderName); - %this.dirHandler.deleteDatablock(%fullPath); - - %this.loadDirectories(); - - AssetDatabase.addModuleDeclaredAssets(%moduleId); -} - -function AssetBrowser::moveDatablock(%this, %folderPath, %newFolderPath) -{ - %fullPath = makeFullPath(%folderPath); - %newFullPath = makeFullPath(%newFolderPath); - - %fullPath = strreplace(%fullPath, "//", "/"); - %newFullPath = strreplace(%newFullPath, "//", "/"); - - %count = getTokenCount(%fullPath, "/"); - %basePath = getTokens(%fullPath, "/", 0, %count-2); - %oldName = getToken(%fullPath, "/", %count-1); - - %copiedSuccess = %this.dirHandler.copyDatablock(%fullPath, %newFullPath); - %this.dirHandler.deleteDatablock(%fullPath); - - %this.loadDirectories(); - - //thrash the modules and reload them - %oldModule = %this.dirHandler.getModuleFromAddress(%folderPath); - %newModule = %this.dirHandler.getModuleFromAddress(%newFolderPath); - - //if we didn't move modules, then we don't need to do anything other than refresh the assets within it - if(%oldModule == %newModule) - { - //only do a refresh to update asset loose file paths - AssetDatabase.refreshAllAssets(); - } - else - { - //they're different moduels now, so we gotta unload/reload both - ModuleDatabase.unloadExplicit(%oldModule.getModuleId()); - ModuleDatabase.loadExplicit(%oldModule.getModuleId()); - - ModuleDatabase.unloadExplicit(%newModule.getModuleId()); - ModuleDatabase.loadExplicit(%newModule.getModuleId()); - } -} - -function AssetBrowser::deleteDatablock(%this, %folderPath) -{ - %this.dirHandler.deleteDatablock(%folderPath); - - %this.refresh(); -} - -function AssetBrowser::onDatablockEditorDropped(%this, %assetDef, %position) -{ - %targetPosition = EWorldEditor.unproject(%position SPC 1); - %camPos = LocalClientConnection.camera.getPosition(); - %rayResult = containerRayCast(%camPos, %targetPosition, -1); - - %pos = ObjectCreator.getCreateObjectPosition(); - - if(%rayResult != 0) - { - %pos = getWords(%rayResult, 1, 3); - } - else - { - %pos = "0 0 0"; - } - - %newObj = spawnDatablockObject(%assetDef); - %newObj.position = %pos; + %newObj = spawnDatablockObject(%this); + %newObj.position = %position; } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/decal.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/decal.tscript deleted file mode 100644 index e69de29bb..000000000 diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/explosion.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/explosion.tscript deleted file mode 100644 index e69de29bb..000000000 diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/folder.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/folder.tscript index 8817cc337..0dd37bf1b 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/folder.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/folder.tscript @@ -1,4 +1,6 @@ -function AssetBrowser::createNewFolder(%this) +AssetBrowser::registerFileType("FolderObjectType", "Folder", "/", false); + +function FolderObjectType::setupCreateNew() { AssetBrowser_newFolderNameTxt.text = "NewFolder"; Canvas.pushDialog(AssetBrowser_newFolder, 99, true); @@ -6,7 +8,7 @@ function AssetBrowser::createNewFolder(%this) AssetBrowser_newFolderNameTxt.selectAllText(); } -function AssetBrowser::doCreateNewFolder(%this) +function FolderObjectType::onCreateNew() { %newFolderName = AssetBrowser_newFolderNameTxt.getText(); @@ -19,7 +21,7 @@ function AssetBrowser::doCreateNewFolder(%this) } else { - %currentAddressPath = AssetBrowser.dirHandler.currentAddress; + %currentAddressPath = $CurrentAssetBrowser.dirHandler.currentAddress; } %newFolderIdx = ""; @@ -46,29 +48,65 @@ function AssetBrowser::doCreateNewFolder(%this) fileDelete(%newFolderPath @ "/test"); //refresh the directory - AssetBrowser.loadDirectories(); + $CurrentAssetBrowser.loadDirectories(); - %this.navigateTo(%newFolderPath); + $CurrentAssetBrowser.navigateTo(%newFolderPath); //On the off chance we're trying to select a path, we'll update the select path window too if(SelectAssetPath.isAwake()) SelectAssetPath.showDialog(%newFolderPath, SelectAssetPath.callback); } -function AssetBrowser::buildFolderPreview(%this, %assetDef, %previewData) +function FolderObjectType::buildBrowserElement(%folderPath, %previewData) { - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.dirPath; + %folderName = fileBase(%folderPath); + + %previewData.assetName = %folderName; + %previewData.assetPath = %folderPath; %previewData.previewImage = "ToolsModule:FolderIcon_image"; //%previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %assetDef.dirPath; - %previewData.doubleClickCommand = "AssetBrowser.schedule(10, \"navigateTo\",\""@ %assetDef.dirPath @ "/" @ %assetDef.assetName @"\");";//browseTo %assetDef.dirPath / %assetDef.assetName + %previewData.assetDesc = "Folder"; + %previewData.tooltip = %folderPath; + %previewData.doubleClickCommand = "AssetBrowser.schedule(10, \"navigateTo\",\""@ %folderPath @"\");"; //browseTo %this.dirPath / %this.assetName } -function AssetBrowser::renameFolder(%this, %folderPath, %newFolderName) +function FolderObjectType::onShowActionMenu(%folderPath) +{ + if( !isObject( EditFolderTypePopup ) ) + { + new PopupMenu( EditFolderTypePopup ) + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; + + jumpFileName = ""; + jumpLineNumber = ""; + }; + } + + EditFolderTypePopup.objectData = %folderPath; + EditFolderTypePopup.objectType = "FolderObjectType"; + + //Regen the menu so we're fully up and current with options and references + EditFolderTypePopup.clearItems(); + + EditFolderTypePopup.item[ 0 ] = "Rename Folder" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();"; + EditFolderTypePopup.item[ 1 ] = "Duplicate Folder" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();"; + EditFolderTypePopup.item[ 2 ] = "-"; + EditFolderTypePopup.item[ 3 ] = "Open Folder Location" TAB "" TAB $CurrentAssetBrowser @ ".openFolderLocation(" @ %folderPath @ ");"; + EditFolderTypePopup.item[ 4 ] = "-"; + EditFolderTypePopup.item[ 5 ] = "Delete Folder" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();"; + + EditFolderTypePopup.reloadItems(); + + EditFolderTypePopup.showPopup(Canvas); + + $CurrentAssetBrowser.popupMenu = EditFolderTypePopup; +} + +function FolderObjectType::onRename(%folderPath, %newFolderName) { %fullPath = makeFullPath(%folderPath); %newFullPath = makeFullPath(%folderPath); @@ -83,23 +121,40 @@ function AssetBrowser::renameFolder(%this, %folderPath, %newFolderName) //so, we nix any assets active for the module, do the delete action on the old folder, and then re-acquire our assets. //This will have the added benefit of updating paths for asset items - %module = AssetBrowser.dirHandler.getModuleFromAddress(AssetBrowser.dirHandler.currentAddress); + %module = $CurrentAssetBrowser.dirHandler.getModuleFromAddress($CurrentAssetBrowser.dirHandler.currentAddress); %moduleId = %module.ModuleId; - AssetDatabase.removeDeclaredAssets(%moduleId); + $CurrentAssetBrowser.removeDeclaredAssets(%moduleId); - %copiedSuccess = %this.dirHandler.copyFolder(%fullPath, %basePath @ "/" @ %newFolderName); - %this.dirHandler.deleteFolder(%fullPath); + %copiedSuccess = $CurrentAssetBrowser.dirHandler.copyFolder(%fullPath, %basePath @ "/" @ %newFolderName); + $CurrentAssetBrowser.dirHandler.deleteFolder(%fullPath); - %this.loadDirectories(); + $CurrentAssetBrowser.loadDirectories(); - AssetDatabase.addModuleDeclaredAssets(%moduleId); + $CurrentAssetBrowser.addModuleDeclaredAssets(%moduleId); } -function AssetBrowser::moveFolder(%this, %folderPath, %newFolderPath) +function FolderObjectType::onDelete(%folderPath) +{ + $CurrentAssetBrowser.dirHandler.deleteFolder(%folderPath); + + $CurrentAssetBrowser.refresh(); +} + +function FolderObjectType::onWorldEditorDropped(%folderPath, %position) +{ + //We do nothing with this as we don't process entire folders for spawning +} + +function FolderObjectType::onGuiEditorDropped(%folderPath, %position) +{ + //We do nothing with this as we don't process entire folders for spawning +} + +function FolderObjectType::onMovePath(%folderPath, %destinationPath) { %fullPath = makeFullPath(%folderPath); - %newFullPath = makeFullPath(%newFolderPath); + %newFullPath = makeFullPath(%destinationPath); %fullPath = strreplace(%fullPath, "//", "/"); %newFullPath = strreplace(%newFullPath, "//", "/"); @@ -108,14 +163,20 @@ function AssetBrowser::moveFolder(%this, %folderPath, %newFolderPath) %basePath = getTokens(%fullPath, "/", 0, %count-2); %oldName = getToken(%fullPath, "/", %count-1); - %copiedSuccess = %this.dirHandler.copyFolder(%fullPath, %newFullPath); - %this.dirHandler.deleteFolder(%fullPath); + %copiedSuccess = $CurrentAssetBrowser.dirHandler.copyFolder(%fullPath, %newFullPath); + $CurrentAssetBrowser.dirHandler.deleteFolder(%fullPath); - %this.loadDirectories(); + $CurrentAssetBrowser.loadDirectories(); +} + +function FolderObjectType::onMoveModule(%folderPath, %destinationPath) +{ + //Same logic as a regular folder move + FolderObjectType::onMovePath(%folderPath, %destinationPath); //thrash the modules and reload them - %oldModule = %this.dirHandler.getModuleFromAddress(%folderPath); - %newModule = %this.dirHandler.getModuleFromAddress(%newFolderPath); + %oldModule = $CurrentAssetBrowser.dirHandler.getModuleFromAddress(%folderPath); + %newModule = $CurrentAssetBrowser.dirHandler.getModuleFromAddress(%destinationPath); //if we didn't move modules, then we don't need to do anything other than refresh the assets within it if(%oldModule == %newModule) @@ -132,11 +193,4 @@ function AssetBrowser::moveFolder(%this, %folderPath, %newFolderPath) ModuleDatabase.unloadExplicit(%newModule.getModuleId()); ModuleDatabase.loadExplicit(%newModule.getModuleId()); } -} - -function AssetBrowser::deleteFolder(%this, %folderPath) -{ - %this.dirHandler.deleteFolder(%folderPath); - - %this.refresh(); } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/forest.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/forest.tscript deleted file mode 100644 index e69de29bb..000000000 diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/gameObject.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/gameObject.tscript deleted file mode 100644 index c8e682669..000000000 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/gameObject.tscript +++ /dev/null @@ -1,285 +0,0 @@ -function AssetBrowser::createGameObjectAsset(%this) -{ - GameObjectCreatorObjectName.text = ""; - - %activeSelection = EWorldEditor.getActiveSelection(); - if( %activeSelection.getCount() == 0 ) - return; - - GameObjectCreator.selectedEntity = %activeSelection.getObject( 0 ); - - Canvas.pushDialog(GameObjectCreator); -} - -function AssetBrowser::editGameObjectAsset(%this, %assetDef) -{ - //We have no dedicated GO editor for now, so just defer to the script editing aspect - %this.editGameObjectAssetScript(%assetDef); -} - -function AssetBrowser::editGameObjectAssetScript(%this, %assetDef) -{ - %scriptFile = %assetDef.scriptFile; - - if(%scriptFile !$= "") - EditorOpenFileInTorsion(makeFullPath(%scriptFile), 0); -} - -function AssetBrowser::applyInstanceToGameObject(%this, %assetDef) -{ - %obj = EditGameObjectAssetPopup.object; - - //TODO add proper validation against the original GO asset - %obj.dirtyGameObject = true; - - TamlWrite(%obj, %assetDef.TAMLFilePath); -} - -function AssetBrowser::duplicateGameObjectAsset(%this, %assetDef, %targetModule) -{ - //Check if we have a target module, if not we need to select one - if(%targetModule $= "") - { - error("AssetBrowser::duplicateGameObjectAsset - No target module selected!"); - return; - } - - %assetId = %assetDef.getAssetId(); - %assetName = AssetDatabase.getAssetName(%assetId); - - //First step, copy the files - %modulePath = "data/" @ %targetModule @ "/gameObjects/"; - - if(!isDirectory(%modulePath)) - createPath(%modulePath); - - %assetFile = AssetDatabase.getAssetFilePath(%assetId); - %scriptFile = %assetDef.scriptFile; - %gameObjectFile = %assetDef.TAMLFile; - - echo("AssetBrowser::duplicateGameObjectAsset - duplicating! " @ %assetId @ " to " @ %targetModule); - - %tamlPath = %modulePath @ fileName(%assetFile); - - pathCopy(%assetFile, %tamlPath); - pathCopy(%scriptFile, %modulePath @ fileName(%scriptFile)); - pathCopy(%gameObjectFile, %modulePath @ fileName(%gameObjectFile)); - - echo("AssetBrowser::duplicateGameObjectAsset - duplicated!"); - - //Register the asset - %moduleDef = ModuleDatabase.findModule(%targetModule, 1); - - AssetDatabase.addDeclaredAsset(%moduleDef, %tamlPath); - - //Refresh the browser - AssetBrowser.refresh(); - - //Rename it for convenience - AssetBrowser.performRenameAsset(%assetName, "New" @ %assetName); -} - -//not used -function AssetBrowser::importGameObjectAsset(%this, %assetId) -{ - -} - -function AssetBrowser::dragAndDropGameObjectAsset(%this, %assetDef, %dropTarget) -{ - if(!isObject(%dropTarget)) - return; - - if(%dropTarget.getId() == EWorldEditor.getId()) - { - if(isObject(%assetDef)) - { - %gameObject = %assetDef.createObject(); - - getScene(0).add(%gameObject); - - %pos = ObjectCreator.getCreateObjectPosition(); //LocalClientConnection.camera.position; - - %gameObject.position = %pos; - - EWorldEditor.clearSelection(); - EWorldEditor.selectObject(%gameObject); - } - else - { - error("WorldEditor::onControlDropped - unable to create GameObject"); - } - } -} - -function AssetBrowser::onGameObjectAssetEditorDropped(%this, %assetDef, %position) -{ - //echo("DROPPED A SHAPE ON THE EDITOR WINDOW!"); - - %targetPosition = EWorldEditor.unproject(%position SPC 1); - %camPos = LocalClientConnection.camera.getPosition(); - %rayResult = containerRayCast(%camPos, %targetPosition, -1); - - %pos = ObjectCreator.getCreateObjectPosition(); - - if(%rayResult != 0) - { - %pos = getWords(%rayResult, 1, 3); - } - - %gameObject = %assetDef.createObject(); - - getScene(0).add(%gameObject); - - %gameObject.position = %pos; - - EWorldEditor.clearSelection(); - EWorldEditor.selectObject(%gameObject); - - EWorldEditor.isDirty = true; - -} - -function AssetBrowser::renameGameObjectAsset(%this, %assetDef, %newAssetId, %originalName, %newName) -{ - %oldScriptFilePath = %assetDef.scriptFile; - %scriptFilePath = filePath(%assetDef.scriptFile); - %scriptExt = fileExt(%assetDef.scriptFile); - - %oldGOFilePath = %assetDef.TAMLFile; - - %filepath = AssetDatabase.getAssetFilePath(%assetDef.getAssetId()); - %path = makeRelativePath(filePath(%filepath)); - - %newScriptFileName = %path @ "/" @ %newName @ %scriptExt; - %newAssetFile = %path @ "/" @ %newName @ ".asset.taml"; - %newGOFile = %path @ "/" @ %newName @ ".taml"; - - %assetDef.gameObjectName = %newName; - %assetDef.scriptFile = %newScriptFileName; - %assetDef.TAMLFile = %newGOFile; - - TamlWrite(%assetDef, %newAssetFile); - fileDelete(%filepath); - - //Quick check, if we duplicated the asset to a new module, the old path may not line up so we'll want to update that to be relevent - if(filePath(%oldScriptFilePath) !$= %path) - { - %oldFileBase = fileName(%oldScriptFilePath); - %oldScriptFilePath = %path @ "/" @ %oldFileBase; - } - - %scriptFileCopySuccess = pathCopy(%oldScriptFilePath, %newScriptFileName); - fileDelete(%oldScriptFilePath); - - if(!%scriptFileCopySuccess) - error("AssetBrowser::renameGameObjectAsset - unable to copy scriptFile"); - - if(filePath(%oldGOFilePath) !$= %path) - { - %oldFileBase = fileName(%oldGOFilePath); - %oldGOFilePath = %path @ "/" @ %oldFileBase; - } - - %goFileCopySuccess = pathCopy(%oldGOFilePath, %newGOFile); - fileDelete(%oldGOFilePath); - - if(!%scriptFileCopySuccess) - error("AssetBrowser::renameGameObjectAsset - unable to copy gameObject"); - - //Go through our scriptfile and replace the old namespace with the new - %editedFileContents = ""; - - %file = new FileObject(); - if ( %file.openForRead( %newScriptFileName ) ) - { - while ( !%file.isEOF() ) - { - %line = %file.readLine(); - %line = trim( %line ); - - %editedFileContents = %editedFileContents @ strreplace(%line, %originalName, %newName) @ "\n"; - } - - %file.close(); - } - - if(%editedFileContents !$= "") - { - %file.openForWrite(%newScriptFileName); - - %file.writeline(%editedFileContents); - - %file.close(); - } - - exec(%newScriptFileName); - - %gameObj = TAMLRead(%newGOFile); - - %gameObj.className = %newName; - %gameObj.GameObject = %assetDef.getAssetId(); - - TAMLWrite(%gameObj, %newGOFile); -} - -//Deletes the asset -function AssetBrowser::deleteGameObjectAsset(%this, %assetDef) -{ - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); -} - -//Moves the asset to a new path/module -function AssetBrowser::moveGameObjectAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.scriptFile, %destination); - moveAssetLooseFile(%assetDef.TAMLFile, %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - - -function AssetBrowser::buildGameObjectAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.scriptFile; - %previewData.doubleClickCommand = "EditorOpenFileInTorsion( "@%previewData.assetPath@", 0 );"; - - %previewData.previewImage = "ToolsModule:gameObjectIcon_image"; - - %previewData.assetFriendlyName = %assetDef.gameObjectName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.gameObjectName @ "\nDefinition Path: " @ %assetDef.getFilename(); -} - -function GuiInspectorTypeGameObjectAssetPtr::onClick( %this, %fieldName ) -{ - //Get our data - %obj = %this.getInspector().getInspectObject(0); - - EditGameObjectAssetPopup.object = %obj; - - %assetId = %obj.getFieldValue(%fieldName); - - if(%assetId !$= "") - { - EditGameObjectAssetPopup.assetId = %assetId; - - - EditGameObjectAssetPopup.showPopup(Canvas); - } - else - { - //We've gotta be trying to create a GameObject, so kick that off - AssetBrowser.createGameObjectAsset(); - } -} diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/genericAsset.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/genericAsset.tscript new file mode 100644 index 000000000..1b7b569ac --- /dev/null +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/genericAsset.tscript @@ -0,0 +1,351 @@ +//This function registers the asset type, associated file extensions and human-readable name with the Asset Browser +//This is primarily so there's an easy hook-in point for the 'type' namespace, as well as registration for AB +//filter modes and file scanning +AssetBrowser::registerAssetType("GenericAsset", "", "", false); + +//This function is called when we want to have adjustable field information for a new asset +//This could be a secondary name or description, a 'preview image' or anything else beyond +//just the basic AssetName and AssetDescription files +function GenericAsset::setupCreateNew() +{ + echo("GenericAsset::setupCreateNew()"); +} + +//This is called when we actually finally decide to create the new asset itself +//We create the asset definition, supplemental files, save everything to disk and +//then register with the AssetDatabase so it's available for use +function GenericAsset::onCreateNew() +{ + echo("GenericAsset::onCreateNew()"); +} + +//This us called when we are creating a 'card' in the AssetBrowser for viewing +//when navigating. It packs whatever relevent information such as the preview image +//into the %previewData object that the AssetBrowser then uses to actually display +function GenericAsset::buildBrowserElement(%this, %previewData) +{ + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.getFileName(); + %previewData.doubleClickCommand = ""; + + %previewData.previewImage = "ToolsModule:genericAssetIcon_image"; + + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = "Asset Name: " @ %this.assetName @ "\nDefinition Path: " @ %previewData.assetPath; +} + +//This is called when we specifically need to special-handle generation of a preview image +//Such as rendering a shape or material to a cached image file, rather than simply using a +//basic image to represent the type in the AssetBrowser +function GenericAsset::generatePreviewImage(%this, %previewButton, %forceRegenerate) +{ +} + +//Called when we rename the asset. The place we would change the asset definition name +//as well as update the name for any associated files +function GenericAsset::onRename(%this, %newAssetName) +{ + %assetPath = makeFullPath($CurrentAssetBrowser.getAssetFilePath(%this.getAssetId())); + %assetFilepath = filePath(%assetPath); + %assetFileExt = fileExt(%assetPath); + + %newAssetPath = %assetFilepath @ "/" @ %newAssetName @ ".asset.taml"; + + %copiedSuccess = pathCopy(%assetPath, %newAssetPath); + + if(!%copiedSuccess) + return ""; + + replaceInFile(%newAssetPath, %this.assetName, %newAssetName); + + %looseFilesList = AssetDatabase.getAssetLooseFiles(%this.getAssetId()); + + for(%i=0; %i < getFieldCount(%looseFilesList); %i++) + { + %looseFile = getField(%looseFilesList, %i); + + if(!isFile(%looseFile)) + { + errorf("GenericAsset::onMovePath() - could not find loose file: " @ %looseFile); + continue; + } + + %newLooseFileName = strReplace(fileName(%looseFile), %this.assetName, %newAssetName); + %looseFileExt = fileExt(%looseFile); + + echo("GenericAsset::onRename() - renamed loose file from: " @ %looseFile @ " to: " @ %assetPath @ "/" @ %newLooseFileName @ %looseFileExt); + + %looseFileNewPath = %assetPath @ "/" @ %newLooseFileName @ %looseFileExt; + %copiedSuccess = pathCopy(%looseFile, %looseFileNewPath); + if(!%copiedSuccess) + { + errorf("GenericAsset::onDuplicate() - failed to duplicate loose file: " @ %looseFile); + } + else + { + fileDelete(%looseFile); + replaceInFile(%looseFileNewPath, %this.assetName, %newAssetName); + } + } + + %module = getModuleFromAddress(%newAssetPath); + + //Add with the new file + $CurrentAssetBrowser.addDeclaredAsset(%module, %newAssetPath); + + GenericAsset::onDelete(%this); + + return %newAssetPath; +} + +//Called when duplicating the asset. A copy is made, and then we perform a rename action +//on the copy to ensure there's no name conflicts +function GenericAsset::onDuplicate(%this, %newAssetName) +{ + %assetPath = makeFullPath($CurrentAssetBrowser.getAssetFilePath(%this.getAssetId())); + %assetFilepath = filePath(%assetPath); + %assetFileExt = fileExt(%assetPath); + + %newAssetPath = %assetFilepath @ "/" @ %newAssetName @ ".asset.taml"; + + %copiedSuccess = pathCopy(%assetPath, %newAssetPath); + + if(!%copiedSuccess) + return ""; + + replaceInFile(%newAssetPath, %this.assetName, %newAssetName); + + %looseFilesList = AssetDatabase.getAssetLooseFiles(%this.getAssetId()); + + for(%i=0; %i < getFieldCount(%looseFilesList); %i++) + { + %looseFile = getField(%looseFilesList, %i); + + if(!isFile(%looseFile)) + { + errorf("GenericAsset::onMovePath() - could not find loose file: " @ %looseFile); + continue; + } + + %newLooseFileName = strReplace(fileName(%looseFile), %this.assetName, %newAssetName); + %looseFileExt = fileExt(%looseFile); + + echo("GenericAsset::onDuplicate() - renamed loose file from: " @ %looseFile @ " to: " @ %assetPath @ "/" @ %newLooseFileName @ %looseFileExt); + + %looseFileNewPath = %assetPath @ "/" @ %newLooseFileName @ %looseFileExt; + %copiedSuccess = pathCopy(%looseFile, %looseFileNewPath); + if(!%copiedSuccess) + { + errorf("GenericAsset::onDuplicate() - failed to duplicate loose file: " @ %looseFile); + } + else + { + replaceInFile(%looseFileNewPath, %this.assetName, %newAssetName); + } + } + + %module = getModuleFromAddress(%newAssetPath); + + //Add with the new file + $CurrentAssetBrowser.addDeclaredAsset(%module, %newAssetPath); + + $CurrentAssetBrowser.refresh(); + + return %newAssetPath; +} + +//Called when the asset is deleted. This would be where and associated files +//are also removed from the system +function GenericAsset::onDelete(%this) +{ + AssetDatabase.deleteAsset(%this.getAssetId(), true); + + $CurrentAssetBrowser.refresh(); +} + +//Called when the asset is moved from one file path to another. Associated files would be +//likewise moved +function GenericAsset::onMovePath(%this, %destinationPath) +{ + %assetPath = makeFullPath($CurrentAssetBrowser.getAssetFilePath(%this.getAssetId())); + + moveAssetFile(%this, %destinationPath); + + %looseFilesList = AssetDatabase.getAssetLooseFiles(%this.getAssetId()); + for(%i=0; %i < getFieldCount(%looseFilesList); %i++) + { + %looseFile = getField(%looseFilesList, %i); + + moveAssetLooseFile(%looseFile, %destinationPath); + } + + AssetDatabase.refreshAsset(%this.getAssetId()); + + $CurrentAssetBrowser.refresh(); +} + +function GenericAsset::onMoveModule(%this, %destinationPath) +{ + %assetPath = makeFullPath($CurrentAssetBrowser.getAssetFilePath(%this.getAssetId())); + + %newAssetPath = moveAssetFile(%this, %destinationPath); + + %looseFilesList = AssetDatabase.getAssetLooseFiles(%this.getAssetId()); + for(%i=0; %i < getFieldCount(%looseFilesList); %i++) + { + %looseFile = getField(%looseFilesList, %i); + + moveAssetLooseFile(%looseFile, %destinationPath); + } + + %targetModule = $CurrentAssetBrowser.dirHandler.getModuleFromAddress(%destinationPath); + + AssetDatabase.removeDeclaredAsset(%this.getAssetId()); + AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); + + $CurrentAssetBrowser.refresh(); +} + +//Called when there is an InspectorType field for this AssetType and a Asset preview +//has been dragged and dropped from the AssetBrowser onto this field in the inspector +//Generally this would see the field assigned with the dropped asset's ID +/*function GuiInspectorTypePtr::onControlDropped(%this, %payload, %position) +{ +}*/ + +//Called when the asset is edited. This can either open the asset in the respective +//editor(MaterialAsset opens MaterialEd, etc) or performs some other action, such as loading +//a LevelAsset in the editor or possibly opening an associated file in a system program for editing +function GenericAsset::onEdit(%this) +{ + echo("GenericAsset::onEdit() - " @ %this); +} + +//Called when the asset's Preview has been right-clicked to do a context popup menu +//Allows for special-menu population per-type if desired, as not all assets will or should +//offer the same 'actions' for editing and management +function GenericAsset::onShowActionMenu(%this) +{ + if( !isObject( EditAssetPopup ) ) + { + new PopupMenu( EditAssetPopup ) + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; + + jumpFileName = ""; + jumpLineNumber = ""; + }; + } + + //Regen the menu so we're fully up and current with options and references + EditAssetPopup.clearItems(); + + EditAssetPopup.item[ 0 ] = "Edit Asset" TAB "" TAB $CurrentAssetBrowser @ ".editAsset();"; + EditAssetPopup.item[ 1 ] = "Rename Asset" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();"; + EditAssetPopup.item[ 2 ] = "Reload Asset" TAB "" TAB $CurrentAssetBrowser @ ".refreshAsset();"; + EditAssetPopup.item[ 3 ] = "Asset Properties" TAB "" TAB $CurrentAssetBrowser @ ".editAssetInfo();"; + EditAssetPopup.item[ 4 ] = "-"; + EditAssetPopup.item[ 5 ] = "Duplicate Asset" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();"; + EditAssetPopup.item[ 6 ] = "-"; + EditAssetPopup.item[ 7 ] = "Regenerate Preview Image" TAB "" TAB $CurrentAssetBrowser @ ".regeneratePreviewImage();"; + EditAssetPopup.item[ 8 ] = "-"; + EditAssetPopup.item[ 9 ] = "Re-Import Asset" TAB "" TAB $CurrentAssetBrowser @ ".reImportAsset();"; + EditAssetPopup.item[ 10 ] = "-"; + EditAssetPopup.item[ 11 ] = "Open File Location" TAB "" TAB $CurrentAssetBrowser @ ".openFileLocation();"; + EditAssetPopup.item[ 12 ] = "-"; + EditAssetPopup.item[ 13 ] = "Delete Asset" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();"; + + %assetId = %this.getAssetId(); + %assetType = AssetDatabase.getAssetType(%assetId); + + EditAssetPopup.objectData = %assetId; + EditAssetPopup.objectType = %assetType; + + EditAssetPopup.reloadItems(); + + EditAssetPopup.showPopup(Canvas); + + $CurrentAssetBrowser.popupMenu = EditAssetPopup; +} + +//Called when editing the asset's properties, generally presented via an inspector +//This function allows for special type handling if just inspecting the Type's object +//is insufficient +function GenericAsset::onEditProperties(%this) +{ + Canvas.pushDialog(AssetBrowser_editAsset); + + AssetBrowser_editAssetWindow.text = "Asset Properties - " @ %this.getAssetId(); + + AssetEditInspector.tempAsset = %this.deepClone(); + + AssetEditInspector.inspect(AssetEditInspector.tempAsset); + AssetBrowser_editAsset.editedObjectData = %this.getAssetId(); + AssetBrowser_editAsset.editedObject = AssetEditInspector.tempAsset; + AssetBrowser_editAsset.editedObjectType = AssetDatabase.getAssetType(%this.getAssetId()); + + //remove some of the groups we don't need: + for(%i=0; %i < AssetEditInspector.getCount(); %i++) + { + %caption = AssetEditInspector.getObject(%i).caption; + + if(%caption $= "Ungrouped" || %caption $= "Object" || %caption $= "Editing" + || %caption $= "Persistence" || %caption $= "Dynamic Fields") + { + AssetEditInspector.remove(AssetEditInspector.getObject(%i)); + %i--; + } + } +} + +//Called when the AssetType has it's properties saved from the onEditProperties process +function GenericAsset::onSaveProperties(%this) +{ + %assetId = %this.getAssetId(); + %file = AssetDatabase.getAssetFilePath(%assetId); + %success = TamlWrite(AssetBrowser_editAsset.editedObject, %file); + + AssetDatabase.releaseAsset(%assetId); + + $CurrentAssetBrowser.reloadAsset(%assetId); + + $CurrentAssetBrowser.refresh(); + + %this.refreshAsset(); +} + +//Called when the asset's Preview has been dragged and dropped into the world editor +//This would usually spawn an associated instance, or a scene object that can utilize the +//asset in question(ie, Dropping a SoundAsset spawns a SoundEmitter) +function GenericAsset::onWorldEditorDropped(%this, %position) +{ + echo("GenericAsset::onWorldEditorDropped() - " @ %this @ ", " @ %position); +} + +//Called when the asset's Preview has been dragged and dropped into the GUI editor +//This would usually spawn an associated instance, or a gui object that can utilize the +//asset in question(ie, Dropping a SoundAsset spawns a guiAudioCtrl) +function GenericAsset::onGuiEditorDropped(%this, %position) +{ + echo("GenericAsset::onGuiEditorDropped() - " @ %this @ ", " @ %position); +} + +//An example case of handling other specialized editors, such as dropping a Datablock +//Preview into the DatablockEditor. This would be very case-by-case +/*function GenericAsset::onEditorDropped() +{ +}*/ + +//Called when the asset has been detected as having had files on the system changed. Allows +//for automatically responding to those changes, such as re-execing script files +function GenericAsset::onChanged(%this) +{ + echo("GenericAsset::onChanged() - " @ %this); +} + +function GenericAsset::onStatusChanged(%this, %newStstus) +{ + echo("GenericAsset::onStatusChanged() - " @ %this @ ", " @ %newStstus); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/gui.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/gui.tscript index ae78030f7..6d091fccc 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/gui.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/gui.tscript @@ -1,9 +1,12 @@ -function AssetBrowser::createGUIAsset(%this) +AssetBrowser::registerFileType("GUIFileType", "GUI Files", ".gui;.gui.dso", false); +AssetBrowser::registerAssetType("GUIAsset", "GUIs"); + +function GUIAsset::onCreateNew() { - %moduleName = AssetBrowser.newAssetSettings.moduleName; + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; - %assetName = AssetBrowser.newAssetSettings.assetName; + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; %assetPath = NewAssetTargetAddress.getText() @ "/"; @@ -24,7 +27,7 @@ function AssetBrowser::createGUIAsset(%this) %file = new FileObject(); %templateFile = new FileObject(); - %guiTemplateCodeFilePath = %this.templateFilesPath @ "guiFile.gui.template"; + %guiTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "guiFile.gui.template"; if(%file.openForWrite(%guipath) && %templateFile.openForRead(%guiTemplateCodeFilePath)) { @@ -45,10 +48,10 @@ function AssetBrowser::createGUIAsset(%this) %file.close(); %templateFile.close(); - warnf("CreateGUIAsset - Something went wrong and we couldn't write the GUI file!"); + warn("GUIAsset::onCreateNew() - Something went wrong and we couldn't write the GUI file!"); } - %scriptTemplateCodeFilePath = %this.templateFilesPath @ "guiFile." @ $TorqueScriptFileExtension @ ".template"; + %scriptTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "guiFile." @ $TorqueScriptFileExtension @ ".template"; if(%file.openForWrite(%scriptPath) && %templateFile.openForRead(%scriptTemplateCodeFilePath)) { @@ -69,7 +72,7 @@ function AssetBrowser::createGUIAsset(%this) %file.close(); %templateFile.close(); - warnf("CreateGUIAsset - Something went wrong and we couldn't write the GUI script file!"); + warn("GUIAsset::onCreateNew() - Something went wrong and we couldn't write the GUI script file!"); } //load the gui @@ -79,31 +82,17 @@ function AssetBrowser::createGUIAsset(%this) %moduleDef = ModuleDatabase.findModule(%moduleName, 1); AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); - AssetBrowser.refresh(); + $CurrentAssetBrowser.refresh(); return %tamlpath; } -function AssetBrowser::inspectImportingGUIAsset(%this, %assetItem) +function GUIAsset::onEdit(%this) { - AssetImportCtrl-->NewAssetsInspector.startGroup("GUI"); - - AssetImportCtrl-->NewAssetsInspector.addField("GUIFile", "GUI File Path", "filename", "Intended usage case of this image. Used to map to material slots and set up texture profiles.", "", - "", %assetItem); - - //Make this a callback so that if it's set we can callback to a validator function - //This function(and others for other asset types) would check if the loosefile audit window is open, and if it is, remove the file from the list - AssetImportCtrl-->NewAssetsInspector.addField("ScriptFile", "Script File Path", "filename", "Intended usage case of this image. Used to map to material slots and set up texture profiles.", "", - "", %assetItem); - AssetImportCtrl-->NewAssetsInspector.endGroup(); -} - -function AssetBrowser::editGUIAsset(%this, %assetDef) -{ - if(!isObject(%assetDef.assetName)) + if(!isObject(%this.assetName)) { - exec(%assetDef.GUIFilePath); - exec(%assetDef.mScriptFilePath); + exec(%this.GUIFilePath); + exec(%this.mScriptFilePath); } if( EditorIsActive() && !GuiEditor.toggleIntoEditorGui ) @@ -112,63 +101,20 @@ function AssetBrowser::editGUIAsset(%this, %assetDef) if( !$InGuiEditor && !GuiEditorIsActive() ) GuiEditor.open(); - GuiEditContent(%assetDef.assetName); + GuiEditContent(%this.assetName); } -//Renames the asset -function AssetBrowser::renameGUIAsset(%this, %assetDef, %newAssetName) +function GUIAsset::buildBrowserElement(%this, %previewData) { - %newScriptLooseFilename = renameAssetLooseFile(%assetDef.scriptFile, %newAssetName); - - if(!%newScriptLooseFilename $= "") - return; - - %newGUILooseFilename = renameAssetLooseFile(%assetDef.guiFile, %newAssetName); - - if(!%newGUILooseFilename $= "") - return; - - %assetDef.scriptFile = %newScriptLooseFilename; - %assetDef.guiFile = %newGUILooseFilename; - %assetDef.saveAsset(); - - renameAssetFile(%assetDef, %newAssetName); -} - -//Deletes the asset -function AssetBrowser::deleteGUIAsset(%this, %assetDef) -{ - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); -} - -//Moves the asset to a new path/module -function AssetBrowser::moveGUIAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getGUIPath(), %destination); - moveAssetLooseFile(%assetDef.getScriptPath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - - -function AssetBrowser::buildGUIAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.GUIFilePath; + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.getGUIPath(); %previewData.doubleClickCommand = ""; %previewData.previewImage = "ToolsModule:guiIcon_image"; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ "\nDefinition Path: " @ %assetDef.getScriptPath(); -} + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = "Asset Name: " @ %this.assetName @ + "\nGUI Path: " @ %this.getGUIPath() @ + "\nScript Path: " @ %this.getScriptPath(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript index 6e2e9deea..673652b70 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript @@ -1,208 +1,33 @@ -function AssetBrowser::prepareImportImageAsset(%this, %assetItem) -{ - if((getAssetImportConfigValue("Images/GenerateMaterialOnImport", "1") == 1 && %assetItem.parentAssetItem $= "") || %assetItem.parentAssetItem !$= "") - { - //First, see if this already has a suffix of some sort based on our import config logic. Many content pipeline tools like substance automatically appends them - %foundSuffixType = parseImageSuffixes(%assetItem); - - if(%foundSuffixType $= "") - { - %noSuffixName = %assetItem.AssetName; - } - else - { - %suffixPos = strpos(strlwr(%assetItem.AssetName), strlwr(%assetItem.ImageType), 0); - %noSuffixName = getSubStr(%assetItem.AssetName, 0, %suffixPos); - } - - //Check if our material already exists - //First, lets double-check that we don't already have an - %materialAsset = ImportAssetWindow.findImportingAssetByName(%noSuffixName); - %cratedNewMaterial = false; - - //Sanity catch in the case we have some naming convention shenanigans in play - if(%materialAsset != 0 && %materialAsset.assetType !$= "MaterialAsset") - %materialAsset = 0; - - if(%materialAsset == 0) - { - %filePath = %assetItem.filePath; - if(%filePath !$= "") - %materialAsset = AssetBrowser.addImportingAsset("MaterialAsset", "", "", %noSuffixName); - - //%materialAsset.filePath = filePath(%assetItem.filePath) @ "/" @ %noSuffixName; - - ImportAssetItems.add(%materialAsset); - - %cratedNewMaterial = true; - } - - if(isObject(%materialAsset)) - { - if(%assetItem.parentAssetItem !$= "") - { - %parentIndex = %assetItem.parentAssetItem.childAssetItems.getIndexFromKey(%assetItem); - %assetItem.parentAssetItem.childAssetItems.erase(%parentIndex); - } - else - { - //if we didn't have a parent until now, we're going to pull from it from our ImportAssetItems list - %itemIndex = ImportAssetItems.getIndexFromKey(%assetItem); - ImportAssetItems.erase(%itemIndex); - } - - //Establish parentage - %materialAsset.childAssetItems.add(%assetItem); - %assetItem.parentAssetItem = %materialAsset; - - ImportAssetWindow.assetHeirarchyChanged = true; - } - - //Lets do some cleverness here. If we're generating a material we can parse like assets being imported(similar file names) but different suffixes - //if we find these, we'll just populate into the original's material - - //If we need to append the diffuse suffix and indeed didn't find a suffix on the name, do that here - if(%foundSuffixType $= "") - { - if(getAssetImportConfigValue("Images/UseDiffuseSuffixOnOriginImg", "1") == 1) - { - if(%foundSuffixType $= "") - { - %diffuseToken = getToken(getAssetImportConfigValue("Images/DiffuseTypeSuffixes", ""), ",", 0); - %assetItem.AssetName = %assetItem.AssetName @ %diffuseToken; - } - } - else - { - //We need to ensure that our image asset doesn't match the same name as the material asset, so if we're not trying to force the diffuse suffix - //we'll give it a generic one - if(%materialAsset.assetName $= %assetItem.assetName) - { - %assetItem.AssetName = %assetItem.AssetName @ "_image"; - } - } - - %foundSuffixType = "diffuse"; - } - - if(%foundSuffixType !$= "") - { - //otherwise, if we have some sort of suffix, we'll want to figure out if we've already got an existing material, and should append to it - - if(getAssetImportConfigValue("Materials/PopulateMaterialMaps", "1") == 1) - { - if(%foundSuffixType $= "diffuse") - %assetItem.ImageType = "Albedo"; - else if(%foundSuffixType $= "normal") - %assetItem.ImageType = "Normal"; - else if(%foundSuffixType $= "metalness") - %assetItem.ImageType = "metalness"; - else if(%foundSuffixType $= "roughness") - %assetItem.ImageType = "roughness"; - else if(%foundSuffixType $= "specular") - %assetItem.ImageType = "specular"; - else if(%foundSuffixType $= "AO") - %assetItem.ImageType = "AO"; - else if(%foundSuffixType $= "composite") - %assetItem.ImageType = "composite"; - } - } - - //If we JUST created this material, we need to do a process pass on it to do any other setup for it - /*if(%cratedNewMaterial) - { - AssetBrowser.prepareImportMaterialAsset(%materialAsset); - }*/ - } +AssetBrowser::registerAssetType("ImageAsset", "Images"); - %assetItem.processed = true; - - refreshImportAssetWindow(); -} - -function AssetBrowser::inspectImportingImageAsset(%this, %assetItem) -{ - AssetImportCtrl-->NewAssetsInspector.startGroup("Image"); - AssetImportCtrl-->NewAssetsInspector.addField("ImageType", "Image Type", "list", "Intended usage case of this image. Used to map to material slots and set up texture profiles.", "GUI", - "Albedo,Normal,Composite,Roughness,AO,Metalness,Glow,GUI,Particle,Decal", %assetItem); - - AssetImportCtrl-->NewAssetsInspector.endGroup(); -} - -function AssetBrowser::importImageAsset(%this, %assetItem) -{ - %moduleName = AssetImportTargetModule.getText(); - - %assetType = %assetItem.AssetType; - %filePath = %assetItem.filePath; - %assetName = %assetItem.assetName; - %assetImportSuccessful = false; - %assetId = %moduleName@":"@%assetName; - - %assetPath = NewAssetTargetAddress.getText() @ "/"; - - %assetFullPath = %assetPath @ "/" @ fileName(%filePath); - - %newAsset = new ImageAsset() - { - assetName = %assetName; - versionId = 1; - imageFile = fileName(%filePath); - imageType = %assetItem.imageType; - }; - - //No point in indicating the original path data if it was imported in-place - if(!startsWith(makeFullPath(%filePath), getMainDotCsDir())) - { - %newAsset.originalFilePath = %filePath; - } - - %assetImportSuccessful = TAMLWrite(%newAsset, %assetPath @ "/" @ %assetName @ ".asset.taml"); - - //and copy the file into the relevent directory - %doOverwrite = !AssetBrowser.isAssetReImport; - if(!pathCopy(%filePath, %assetFullPath, %doOverwrite)) - { - error("Unable to import asset: " @ %filePath); - return; - } - - %moduleDef = ModuleDatabase.findModule(%moduleName,1); - - if(!AssetBrowser.isAssetReImport) - AssetDatabase.addDeclaredAsset(%moduleDef, %assetPath @ "/" @ %assetName @ ".asset.taml"); - else - AssetDatabase.refreshAsset(%assetId); -} - -function AssetBrowser::buildImageAssetPreview(%this, %assetDef, %previewData) +function ImageAsset::buildBrowserElement(%this, %previewData) { //%module = %this.dirHandler.getModuleFromAddress(makeRelativePath(filePath(%assetDef.getImagePath()))); %previewData.previewImage = "ToolsModule:genericAssetIcon_image"; %previewData.previewLoaded = false; //this marks it for loading progressively later - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.scriptFile; + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.scriptFile; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; //image info //%info = %assetDef.getImageInfo(); - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ "\n" @ + %previewData.tooltip = "Asset Name: " @ %this.assetName @ "\n" @ "Asset Type: Image Asset\n" @ - "Asset Definition ID: " @ %assetDef @ "\n" @ - "Image Type: " @ %assetDef.imageType @ "\n" @ + "Asset Definition ID: " @ %this @ "\n" @ + "Image Type: " @ %this.imageType @ "\n" @ /* "Format: " @ getWord(%info, 0) @ "\n" @ "Height: " @ getWord(%info, 1) @ "\n" @ "Width: " @ getWord(%info, 2) @ "\n" @ "Depth: " @ getWord(%info, 3) @ "\n" @ */ - "Image File path: " @ %assetDef.getImagePath(); + "Image File path: " @ %this.getImagePath(); } -function AssetBrowser::generateImageAssetPreviewImage(%this, %previewButton, %forceRegenerate) +function ImageAsset::generatePreviewImage(%this, %previewButton, %forceRegenerate) { if(%forceRegenerate $= "") %forceRegenerate = false; @@ -211,24 +36,20 @@ function AssetBrowser::generateImageAssetPreviewImage(%this, %previewButton, %fo if(!IsDirectory(%previewPath)) { - %this.dirHandler.createFolder(%previewPath); + $CurrentAssetBrowser.dirHandler.createFolder(%previewPath); } - %assetId = %previewButton.moduleName @ ":" @ %previewButton.assetName; - - %assetDef = AssetDatabase.acquireAsset(%assetId); - - %previewFilePath = %previewPath @ %assetDef.assetName @ "_Preview.png"; - if(!isFile(%previewFilePath) || (compareFileTimes(%assetDef.getImagePath(), %previewFilePath) == 1)) + %previewFilePath = %previewPath @ %this.assetName @ "_Preview.png"; + if(!isFile(%previewFilePath) || (compareFileTimes(%this.getImagePath(), %previewFilePath) == 1)) { %generatePreview = true; } - %previewAssetName = %previewButton.moduleName @ "_" @ %assetDef.assetName @ "_PreviewImage"; + %previewAssetName = %previewButton.moduleName @ "_" @ %this.assetName @ "_PreviewImage"; if(%generatePreview || %forceRegenerate) { - %success = saveScaledImage(%assetDef.getImagePath(), %previewFilePath, EditorSettings.value("Assets/Browser/PreviewImageSize")); + %success = saveScaledImage(%this.getImagePath(), %previewFilePath, EditorSettings.value("Assets/Browser/PreviewImageSize")); if(%success) { @@ -252,79 +73,58 @@ function AssetBrowser::generateImageAssetPreviewImage(%this, %previewButton, %fo if(!%success) { return false; //failed to register the preview image for some reason? + } + } + + %previewButton.bitmapAsset = %previewAssetName; + return true; } } - - %previewButton.bitmapAsset = %previewAssetName; - return true; + else + { + //just map the existing one then + if(AssetDatabase.isDeclaredAsset("ToolsModule:" @ %previewAssetName)) + { + %previewButton.bitmapAsset = "ToolsModule:" @ %previewAssetName; + return true; } } - else - { - //just map the existing one then - if(AssetDatabase.isDeclaredAsset("ToolsModule:" @ %previewAssetName)) - { - %previewButton.bitmapAsset = "ToolsModule:" @ %previewAssetName; - return true; - } - } return false; } -//Renames the asset -function AssetBrowser::renameImageAsset(%this, %assetDef, %newAssetName) +function ImageAsset::onShowActionMenu(%this) { - %newFilename = renameAssetLooseFile(%assetDef.getImagePath(), %newAssetName); - - if(!%newFilename $= "") - return; + if( !isObject( EditImageAssetTypePopup ) ) + { + new PopupMenu( EditImageAssetTypePopup ) + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; - %assetDef.imageFile = %newFilename; - %assetDef.saveAsset(); + jumpFileName = ""; + jumpLineNumber = ""; + }; + } - renameAssetFile(%assetDef, %newAssetName); -} + EditImageAssetTypePopup.objectData = %this; + EditImageAssetTypePopup.objectType = "ImageAsset"; + + //Regen the menu so we're fully up and current with options and references + EditImageAssetTypePopup.clearItems(); + + EditImageAssetTypePopup.item[ 0 ] = "Rename Folder" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();"; + EditImageAssetTypePopup.item[ 1 ] = "Duplicate Folder" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();"; + EditImageAssetTypePopup.item[ 2 ] = "-"; + EditImageAssetTypePopup.item[ 3 ] = "Open File Location" TAB "" TAB $CurrentAssetBrowser @ ".openFolderLocation(" @ filePath(%this.getFilename()) @ ");"; + EditImageAssetTypePopup.item[ 4 ] = "-"; + EditImageAssetTypePopup.item[ 5 ] = "Delete Folder" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();"; -//Duplicates the asset -function AssetBrowser::duplicateImageAsset(%this, %assetDef, %newAssetName) -{ - %duplicatedAsset = duplicateAssetFile(%assetDef, %newAssetName); + EditImageAssetTypePopup.reloadItems(); - %newFilename = duplicateAssetLooseFile(%assetDef.imageFile, %newAssetName); + EditImageAssetTypePopup.showPopup(Canvas); - if(!%newFilename $= "") - return; - - %module = AssetBrowser.dirHandler.getModuleFromAddress(%duplicatedAsset); - - %dupAssetDef = AssetDatabase.acquireAsset(%module.ModuleId @ ":" @ %newAssetName); - - %dupAssetDef.imageFile = fileName(%newFilename); - %dupAssetDef.saveAsset(); -} - -//Deletes the asset -function AssetBrowser::deleteImageAsset(%this, %assetDef) -{ - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); -} - -//Moves the asset to a new path/module -function AssetBrowser::moveImageAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getImagePath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); + $CurrentAssetBrowser.popupMenu = EditImageAssetTypePopup; } function GuiInspectorTypeImageAssetPtr::onControlDropped( %this, %payload, %position ) @@ -346,176 +146,4 @@ function GuiInspectorTypeImageAssetPtr::onControlDropped( %this, %payload, %posi } EWorldEditor.isDirty = true; -} - -function parseImageSuffixes(%assetItem) -{ - //diffuse - %suffixCount = getTokenCount(getAssetImportConfigValue("Images/DiffuseTypeSuffixes", ""), ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(getAssetImportConfigValue("Images/DiffuseTypeSuffixes", ""), ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %assetItem.AssetName)) - { - %assetItem.ImageType = %suffixToken; - return "diffuse"; - } - } - - //normal - %suffixCount = getTokenCount(getAssetImportConfigValue("Images/NormalTypeSuffixes", ""), ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(getAssetImportConfigValue("Images/NormalTypeSuffixes", ""), ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %assetItem.AssetName)) - { - %assetItem.ImageType = %suffixToken; - return "normal"; - } - } - - //roughness - %suffixCount = getTokenCount(getAssetImportConfigValue("Images/RoughnessTypeSuffixes", ""), ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(getAssetImportConfigValue("Images/RoughnessTypeSuffixes", ""), ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %assetItem.AssetName)) - { - %assetItem.ImageType = %suffixToken; - return "roughness"; - } - } - - //Ambient Occlusion - %suffixCount = getTokenCount(getAssetImportConfigValue("Images/AOTypeSuffixes", ""), ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(getAssetImportConfigValue("Images/AOTypeSuffixes", ""), ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %assetItem.AssetName)) - { - %assetItem.ImageType = %suffixToken; - return "AO"; - } - } - - //metalness - %suffixCount = getTokenCount(getAssetImportConfigValue("Images/MetalnessTypeSuffixes", ""), ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(getAssetImportConfigValue("Images/MetalnessTypeSuffixes", ""), ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %assetItem.AssetName)) - { - %assetItem.ImageType = %suffixToken; - return "metalness"; - } - } - - //composite - %suffixCount = getTokenCount(getAssetImportConfigValue("Images/CompositeTypeSuffixes", ""), ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(getAssetImportConfigValue("Images/CompositeTypeSuffixes", ""), ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %assetItem.AssetName)) - { - %assetItem.ImageType = %suffixToken; - return "composite"; - } - } - - //specular - /*%suffixCount = getTokenCount(ImportAssetWindow.activeImportConfig.SpecularTypeSuffixes, ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(ImportAssetWindow.activeImportConfig.SpecularTypeSuffixes, ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %assetItem.AssetName)) - { - %assetItem.imageSuffixType = %suffixToken; - return "specular"; - } - }*/ - - return ""; -} - -function parseImagePathSuffixes(%filePath) -{ - //diffuse - %diffuseSuffixes = getAssetImportConfigValue("Images/DiffuseTypeSuffixes", ""); - %suffixCount = getTokenCount(%diffuseSuffixes, ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(%diffuseSuffixes, ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %filePath)) - { - return "diffuse"; - } - } - - //normal - %suffixCount = getTokenCount(ImportAssetWindow.activeImportConfig.NormalTypeSuffixes, ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(ImportAssetWindow.activeImportConfig.NormalTypeSuffixes, ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %filePath)) - { - return "normal"; - } - } - - //roughness - %suffixCount = getTokenCount(ImportAssetWindow.activeImportConfig.RoughnessTypeSuffixes, ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(ImportAssetWindow.activeImportConfig.RoughnessTypeSuffixes, ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %filePath)) - { - return "roughness"; - } - } - - //Ambient Occlusion - %suffixCount = getTokenCount(ImportAssetWindow.activeImportConfig.AOTypeSuffixes, ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(ImportAssetWindow.activeImportConfig.AOTypeSuffixes, ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %filePath)) - { - return "AO"; - } - } - - //metalness - %suffixCount = getTokenCount(ImportAssetWindow.activeImportConfig.MetalnessTypeSuffixes, ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(ImportAssetWindow.activeImportConfig.MetalnessTypeSuffixes, ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %filePath)) - { - return "metalness"; - } - } - - //composite - %suffixCount = getTokenCount(ImportAssetWindow.activeImportConfig.CompositeTypeSuffixes, ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(ImportAssetWindow.activeImportConfig.CompositeTypeSuffixes, ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %filePath)) - { - return "composite"; - } - } - - //specular - %suffixCount = getTokenCount(ImportAssetWindow.activeImportConfig.SpecularTypeSuffixes, ",;"); - for(%sfx = 0; %sfx < %suffixCount; %sfx++) - { - %suffixToken = getToken(ImportAssetWindow.activeImportConfig.SpecularTypeSuffixes, ",;", %sfx); - if(strIsMatchExpr("*"@%suffixToken, %filePath)) - { - return "specular"; - } - } - - return ""; } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/level.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/level.tscript index 2d538ae4b..5dc9bfa0c 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/level.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/level.tscript @@ -1,22 +1,20 @@ -function AssetBrowser::setupCreateNewLevelAsset(%this) +AssetBrowser::registerAssetType("LevelAsset", "Levels"); + +function LevelAsset::setupCreateNew() { 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("LevelName", "Level Name", "String", "Human-readable name of new level", "", "", $CurrentAssetBrowser.newAssetSettings); + NewAssetPropertiesInspector.addField("levelPreviewImage", "Level Preview Image", "Image", "Preview Image for the level", "", "", $CurrentAssetBrowser.newAssetSettings); NewAssetPropertiesInspector.endGroup(); } -function AssetImporter::importLevelAsset(%this, %assetItem) +function LevelAsset::onCreateNew(%this) { -} - -function AssetBrowser::createLevelAsset(%this) -{ - %moduleName = AssetBrowser.newAssetSettings.moduleName; + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; - %assetName = AssetBrowser.newAssetSettings.assetName; + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; %assetPath = NewAssetTargetAddress.getText() @ "/"; @@ -32,9 +30,9 @@ function AssetBrowser::createLevelAsset(%this) PostFXPresetFile = %assetName @ ".postfxpreset." @ $TorqueScriptFileExtension; ForestFile = %assetName @ ".forest"; NavmeshFile = %assetName @ ".nav"; - LevelName = AssetBrowser.newAssetSettings.levelName; - AssetDescription = AssetBrowser.newAssetSettings.description; - PreviewImage = AssetBrowser.newAssetSettings.levelPreviewImage; + LevelName = $CurrentAssetBrowser.newAssetSettings.levelName; + AssetDescription = $CurrentAssetBrowser.newAssetSettings.description; + PreviewImage = $CurrentAssetBrowser.newAssetSettings.levelPreviewImage; }; TamlWrite(%asset, %tamlpath); @@ -59,95 +57,33 @@ function AssetBrowser::createLevelAsset(%this) %moduleDef = ModuleDatabase.findModule(%moduleName, 1); %addSuccess = AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); - AssetBrowser.refresh(); + $CurrentAssetBrowser.refresh(); return %tamlpath; } -function AssetBrowser::editLevelAsset(%this, %assetDef) +function LevelAsset::onEdit(%this) { - schedule( 1, 0, "EditorOpenMission", %assetDef); + schedule( 1, 0, "EditorOpenMission", %this); } -//Renames the asset -function AssetBrowser::renameLevelAsset(%this, %assetDef, %newAssetName) +function LevelAsset::buildBrowserElement(%this, %previewData) { - %newFilename = renameAssetLooseFile(%assetDef.LevelFile, %newAssetName); + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.getLevelPath(); + %previewData.doubleClickCommand = "schedule( 1, 0, \"EditorOpenMission\", "@%this@");"; - if(!%newFilename $= "") - return; - - //TODO the other loose files - - %assetDef.LevelFile = %newFilename; - %assetDef.saveAsset(); - - renameAssetFile(%assetDef, %newAssetName); -} - -//Duplicates the asset -function AssetBrowser::duplicateLevelAsset(%this, %assetDef, %newAssetName) -{ - %duplicatedAsset = duplicateAssetFile(%assetDef, %newAssetName); - - %newFilename = duplicateAssetLooseFile(%assetDef.LevelFile, %newAssetName); - - if(!%newFilename $= "") - return; - - %module = AssetBrowser.dirHandler.getModuleFromAddress(%duplicatedAsset); - - %dupAssetDef = AssetDatabase.acquireAsset(%module.ModuleId @ ":" @ %newAssetName); - - %dupAssetDef.LevelFile = fileName(%newFilename); - %dupAssetDef.saveAsset(); -} - -//Deletes the asset -function AssetBrowser::deleteLevelAsset(%this, %assetDef) -{ - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); -} - -//Moves the asset to a new path/module -function AssetBrowser::moveLevelAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getLevelPath(), %destination); - moveAssetLooseFile(%assetDef.getPreviewImagePath(), %destination); - moveAssetLooseFile(%assetDef.getPostFXPresetPath(), %destination); - moveAssetLooseFile(%assetDef.getDecalsPath(), %destination); - moveAssetLooseFile(%assetDef.getForestPath(), %destination); - moveAssetLooseFile(%assetDef.getNavmeshPath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::buildLevelAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.getLevelPath(); - %previewData.doubleClickCommand = "schedule( 1, 0, \"EditorOpenMission\", "@%assetDef@");"; - - %levelPreviewImage = %assetDef.PreviewImage; + %levelPreviewImage = %this.PreviewImage; if(isFile(%levelPreviewImage)) %previewData.previewImage = %levelPreviewImage; else %previewData.previewImage = "ToolsModule:levelIcon_image"; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ "\n" @ + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = "Asset Name: " @ %this.assetName @ "\n" @ "Asset Type: Level Asset\n" @ - "Asset Definition ID: " @ %assetDef @ "\n" @ - "Level File path: " @ %assetDef.getLevelPath(); + "Asset Definition ID: " @ %this @ "\n" @ + "Level File path: " @ %this.getLevelPath(); } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/looseFiles.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/looseFiles.tscript index 2c036c064..8d719c1da 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/looseFiles.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/looseFiles.tscript @@ -1,14 +1,17 @@ -function AssetBrowser::buildLooseFilePreview(%this, %assetDef, %previewData) +AssetBrowser::registerFileType("NonAssetLooseFile", "Loose Files", "-"); + +function NonAssetLooseFile::buildBrowserElement(%filePath, %previewData) { - %fullPath = %assetDef.dirPath @ "/" @ %assetDef.assetName; - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %fullPath; + %fileName = fileName(%filePath); + + %previewData.assetName = %fileName; + %previewData.assetPath = %filePath; %previewData.previewImage = "ToolsModule:looseFileIcon_image"; //%previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %fullPath; + %previewData.assetDesc = %filePath; + %previewData.tooltip = %filePath; //%previewData.doubleClickCommand = "AssetBrowser.schedule(10, \"navigateTo\",\""@ %assetDef.dirPath @ "/" @ %assetDef.assetName @"\");";//browseTo %assetDef.dirPath / %assetDef.assetName - %previewData.doubleClickCommand = "AssetBrowser.autoImportFile(\"" @ %fullPath @ "\");"; + //%previewData.doubleClickCommand = "AssetBrowser.autoImportFile(\"" @ %fullPath @ "\");"; } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/material.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/material.tscript index 42a2f05f2..0a7f0a6ff 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/material.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/material.tscript @@ -1,4 +1,6 @@ -function AssetBrowser::createMaterialAsset(%this) +AssetBrowser::registerAssetType("MaterialAsset", "Materials"); + +function MaterialAsset::createMaterialAsset(%this) { %assetName = AssetBrowser.newAssetSettings.assetName; @@ -33,15 +35,15 @@ function AssetBrowser::createMaterialAsset(%this) return %tamlpath; } -function AssetBrowser::editMaterialAsset(%this, %assetDef) +function MaterialAsset::onEdit(%this) { - %assetDef.materialDefinitionName.reload(); + %this.materialDefinitionName.reload(); EditorGui.setEditor(MaterialEditorPlugin); - MaterialEditorGui.currentMaterialAsset = %assetDef.getAssetId(); - MaterialEditorGui.currentMaterial = %assetDef.materialDefinitionName; - MaterialEditorGui.setActiveMaterial( %assetDef.materialDefinitionName ); + MaterialEditorGui.currentMaterialAsset = %this.getAssetId(); + MaterialEditorGui.currentMaterial = %this.materialDefinitionName; + MaterialEditorGui.setActiveMaterial( %this.materialDefinitionName ); AssetBrowser.hideDialog(); } @@ -60,451 +62,82 @@ function AssetBrowser::renameMaterialAsset(%this, %assetDef, %newAssetName) renameAssetFile(%assetDef, %newAssetName); } -//Deletes the asset -function AssetBrowser::deleteMaterialAsset(%this, %assetDef) -{ - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); -} - -//Moves the asset to a new path/module -function AssetBrowser::moveMaterialAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getScriptPath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::prepareImportMaterialAsset(%this, %assetItem) -{ - ImportActivityLog.add("Preparing Material for Import: " @ %assetItem.assetName); - - //Iterate over to find appropriate images for - - //Fetch just the fileBase name - %fileDir = filePath(%assetItem.filePath); - %fileName = fileBase(%assetItem.filePath); - %fileExt = fileExt(%assetItem.filePath); - - %assetItem.generatedAsset = true; - - //Check if we need to filter this material out or not - if(getAssetImportConfigValue("Materials/IgnoreMaterials", "") !$= "") - { - %ignoredMatNamesCount = getTokenCount(getAssetImportConfigValue("Materials/IgnoreMaterials", ""), ",;"); - for(%i=0; %i < %ignoredMatNamesCount; %i++) - { - %ignoreName = getToken(getAssetImportConfigValue("Materials/IgnoreMaterials", ""), ",;", %i); - - if(strIsMatchExpr(%ignoreName, %fileName)) - { - //We fit the bill, ignore this material and skip it - %assetItem.skip = true; - - ImportActivityLog.add(%assetItem.assetName @ " has been ignored due to config Materials/IgnoreMaterials settings"); - - return; - } - } - } - - if(getAssetImportConfigValue("Materials/PopulateMaterialMaps", "1") == 1) - { - ImportActivityLog.add("Attempting to Auto-Populate Material Maps"); - - for(%i=0; %i < %assetItem.childAssetItems.count(); %i++) - { - %childAssetItem = %assetItem.childAssetItems.getKey(%i); - - if(!isObject(%childAssetItem) || %childAssetItem.skip || %childAssetItem.processed == true || %childAssetItem.assetType !$= "ImageAsset") - return; - - if(%childAssetItem.imageType $= "Albedo") - { - %assetItem.diffuseImageAsset = %childAssetItem; - } - } - - /*%materialItemId = ImportAssetTree.findItemByObjectId(%assetItem); - - if(%assetItem.diffuseImageAsset $= "") - { - %diffuseTypeSuffixes = getAssetImportConfigValue("Images/DiffuseTypeSuffixes", ""); - - %targetFilePath = %this.findMaterialMapFileWSuffix(%fileDir, %fileName, %fileExt, %diffuseTypeSuffixes); - - if(%targetFilePath !$= "") - { - ImportActivityLog.add("Auto-Populated Diffuse Map Image Asset via file: " @ %targetFilePath); - - %diffuseAsset = AssetBrowser.addImportingAsset("ImageAsset", %targetFilePath, %assetItem); - %assetItem.diffuseImageAsset = %diffuseAsset; - } - } - - //Now, iterate over our comma-delimited suffixes to see if we have any matches. We'll use the first match in each case, if any. - if(%assetItem.normalImageAsset $= "") - { - %normalTypeSuffixes = getAssetImportConfigValue("Images/NormalTypeSuffixes", ""); - - //First, normal map - %targetFilePath = %this.findMaterialMapFileWSuffix(%fileDir, %fileName, %fileExt, %normalTypeSuffixes); - - if(%targetFilePath !$= "") - { - ImportActivityLog.add("Auto-Populated Normal Map Image Asset via file: " @ %targetFilePath); - - %normalAsset = AssetBrowser.addImportingAsset("ImageAsset", %targetFilePath, %assetItem); - %assetItem.normalImageAsset = %normalAsset; - } - } - - if(%assetItem.metalImageAsset $= "") - { - %metalnessTypeSuffixes = getAssetImportConfigValue("Images/MetalnessTypeSuffixes", ""); - - %targetFilePath = %this.findMaterialMapFileWSuffix(%fileDir, %fileName, %fileExt, %metalnessTypeSuffixes); - - if(%targetFilePath !$= "") - { - ImportActivityLog.add("Auto-Populated Metalness Map Image Asset via file: " @ %targetFilePath); - - %metalAsset = AssetBrowser.addImportingAsset("ImageAsset", %targetFilePath, %assetItem); - %assetItem.metalImageAsset = %metalAsset; - } - } - - if(%assetItem.roughnessImageAsset $= "") - { - %roughnessTypeSuffixes = getAssetImportConfigValue("Images/RoughnessTypeSuffixes", ""); - - %targetFilePath = %this.findMaterialMapFileWSuffix(%fileDir, %fileName, %fileExt, %roughnessTypeSuffixes); - - if(%targetFilePath !$= "") - { - ImportActivityLog.add("Auto-Populated Roughness Map Image Asset via file: " @ %targetFilePath); - - %roughnessAsset = AssetBrowser.addImportingAsset("ImageAsset", %targetFilePath, %assetItem); - %assetItem.roughnessImageAsset = %roughnessAsset; - } - } - - if(%assetItem.smoothnessImageAsset $= "") - { - %smoothnessTypeSuffixes = getAssetImportConfigValue("Images/SmoothnessTypeSuffixes", ""); - - %targetFilePath = %this.findMaterialMapFileWSuffix(%fileDir, %fileName, %fileExt, %smoothnessTypeSuffixes); - - if(%targetFilePath !$= "") - { - ImportActivityLog.add("Auto-Populated Smoothness Map Image Asset via file: " @ %targetFilePath); - - %smoothnessAsset = AssetBrowser.addImportingAsset("ImageAsset", %targetFilePath, %assetItem); - %assetItem.SmoothnessImageAsset = %smoothnessAsset; - } - } - - if(%assetItem.AOImageAsset $= "") - { - %aoTypeSuffixes = getAssetImportConfigValue("Images/AOTypeSuffixes", ""); - - %targetFilePath = %this.findMaterialMapFileWSuffix(%fileDir, %fileName, %fileExt, %aoTypeSuffixes); - - if(%targetFilePath !$= "") - { - ImportActivityLog.add("Auto-Populated AO Map Image Asset via file: " @ %targetFilePath); - - %AOAsset = AssetBrowser.addImportingAsset("ImageAsset", %targetFilePath, %assetItem); - %assetItem.AOImageAsset = %AOAsset; - } - } - - if(%assetItem.compositeImageAsset $= "") - { - %compositeTypeSuffixes = getAssetImportConfigValue("Images/CompositeTypeSuffixes", ""); - - %targetFilePath = %this.findMaterialMapFileWSuffix(%fileDir, %fileName, %fileExt, %compositeTypeSuffixes); - - if(%targetFilePath !$= "") - { - ImportActivityLog.add("Auto-Populated Composite Map Image Asset via file: " @ %targetFilePath); - - %compositeAsset = AssetBrowser.addImportingAsset("ImageAsset", %targetFilePath, %assetItem); - %assetItem.compositeImageAsset = %compositeAsset; - } - } - - //If after the above we didn't find any, check to see if we should be generating one - if(%assetItem.compositeImageAsset $= "" && - (%assetItem.roughnessImageAsset !$= "" || %assetItem.AOImageAsset !$= "" || %assetItem.metalnessImageAsset !$= "") && - getAssetImportConfigValue("Materials/CreateComposites", "1") == 1) - { - %assetItem.roughnessImageAsset.skip = true; - %assetItem.AOImageAsset.skip = true; - %assetItem.metalnessImageAsset.skip = true; - - %compositeAssetPath = AssetBrowser.dirHandler.currentAddress @ "/"; - %saveAsPath = %compositeAssetPath @ "/" @ %assetItem.assetName @ "_composite.png"; - - ImportActivityLog.add("Auto-Generated Composite Map from ORM maps"); - - %compositeAsset = AssetBrowser.addImportingAsset("ImageAsset", "", %assetItem, %assetItem.assetName @ "_composite"); - %compositeAsset.generatedAsset = true; - %compositeAsset.filePath = %saveAsPath; - - %assetItem.compositeImageAsset = %compositeAsset; - }*/ - } - - %assetItem.processed = true; - - refreshImportAssetWindow(); -} - -function AssetBrowser::findMaterialMapFileWSuffix(%this, %fileDir, %filename, %fileExt, %suffixesList) -{ - %listCount = getTokenCount(%suffixesList, ",;"); - - %foundFile = 0; - %filePath = ""; - for(%i=0; %i < %listCount; %i++) - { - %entryText = getToken(%suffixesList, ",;", %i); - - if(%fileExt $= "") - { - %filePath = findFirstFile(%fileDir @ "/" @ %filename @ %entryText @ ".*"); - %foundFile = isFile(%filePath); - } - else - { - %filePath = %fileDir @ "/" @ %filename @ %entryText @ %fileExt; - %foundFile = isFile(%filePath); - } - - if(%foundFile) - { - return %filePath; - } - } - - return ""; -} - -function AssetBrowser::importMaterialAsset(%this, %assetItem) -{ - %moduleName = AssetImportTargetModule.getText(); - - %assetType = %assetItem.AssetType; - %filePath = %assetItem.filePath; - %assetName = %assetItem.assetName; - %assetImportSuccessful = false; - %assetId = %moduleName@":"@%assetName; - - %assetPath = NewAssetTargetAddress.getText() @ "/"; - %tamlpath = %assetPath @ %assetName @ ".asset.taml"; - %sgfPath = %assetPath @ %assetName @ ".sgf"; - %scriptPath = %assetPath @ %assetName @ "." @ $TorqueScriptFileExtension; - - %newAsset = new MaterialAsset() - { - assetName = %assetName; - versionId = 1; - shaderGraph = %sgfPath; - scriptFile = %assetName @ "." @ $TorqueScriptFileExtension; - materialDefinitionName = %assetName; - }; - - //No point in indicating the original path data if it was imported in-place - %mainPath = getMainDotCsDir(); - if(!startsWith(makeFullPath(%filePath), getMainDotCsDir())) - { - %newAsset.originalFilePath = %filePath; - } - - //check dependencies - %dependencySlotId = 0; - for(%i=0; %i < %assetItem.childAssetItems.count(); %i++) - { - %childAssetItem = %assetItem.childAssetItems.getKey(%i); - - if(!isObject(%childAssetItem) || %childAssetItem.skip || %childAssetItem.processed == false) - continue; - - %depAssetType = %childAssetItem.assetType; - if(%depAssetType $= "ImageAsset") - { - %matSet = "%newAsset.imageMap"@%dependencySlotId@"=\"@asset="@%moduleName@":"@%childAssetItem.assetName@"\";"; - eval(%matSet); - %dependencySlotId++; - } - } - - %assetImportSuccessful = TamlWrite(%newAsset, %tamlpath); - - //if we're set to save a composite image, we do that first - if(getAssetImportConfigValue("Materials/CreateComposites", "1") == 1) - { - //don't save a composite if we've already got one bound - if(%assetItem.compositeImageAsset !$= "" && %assetItem.compositeImageAsset.generatedAsset) - { - if(%assetItem.roughnessImageAsset !$= "" || %assetItem.AOImageAsset !$= "" || %assetItem.metalnessImageAsset !$= "") - { - %channelKey = "0 1 2 3"; - - saveCompositeTexture(%assetItem.AOImageAsset.filePath, - %assetItem.roughnessImageAsset.filePath, - %assetItem.metalnessImageAsset.filePath,"", - %channelKey, - %assetItem.compositeImageAsset.filePath); - - %compositeAssetId = %moduleName @ ":" @ assetItem.compositeImageAsset.assetName; - AssetDatabase.refreshAsset(%compositeAssetId); - } - } - } - - %file = new FileObject(); - - if(%file.openForWrite(%scriptPath)) - { - %file.writeline("//--- OBJECT WRITE BEGIN ---"); - %file.writeline("singleton Material(" @ %assetName @ ") {"); - - //TODO: pass along the shape's target material for this just to be sure - %file.writeLine(" mapTo = \"" @ %assetName @ "\";"); - - //now we re-iterate back over our child items so we can map them correctly - for(%i=0; %i < %assetItem.childAssetItems.count(); %i++) - { - %childAssetItem = %assetItem.childAssetItems.getKey(%i); - - if(!isObject(%childAssetItem) || %childAssetItem.skip || %childAssetItem.processed == false) - continue; - - if(%childAssetItem.assetType $= "ImageAsset") - { - %mapFieldName = ""; - if(%childAssetItem.imageType $= "Albedo") - %mapFieldName = "DiffuseMap"; - else if(%childAssetItem.imageType $= "Normal") - %mapFieldName = "NormalMap"; - else if(%childAssetItem.imageType $= "Metalness") - %mapFieldName = "MetalMap"; - else if(%childAssetItem.imageType $= "Roughness") - %mapFieldName = "RoughnessMap"; - else if(%childAssetItem.imageType $= "AO") - %mapFieldName = "AOMap"; - else if(%childAssetItem.imageType $= "Composite") - %mapFieldName = "ORMConfigMap"; - - %path = fileName(%childAssetItem.filePath); - %file.writeline(" "@ %mapFieldName @ "[0] = \"" @ %path @"\";"); - %file.writeline(" "@ %mapFieldName @ "Asset[0] = \"" @ %moduleName @ ":" @ %childAssetItem.assetName @"\";"); - } - } - %file.writeline("};"); - %file.writeline("//--- OBJECT WRITE END ---"); - - %file.close(); - } - - %moduleDef = ModuleDatabase.findModule(%moduleName,1); - - if(!AssetBrowser.isAssetReImport) - AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); - else - AssetDatabase.refreshAsset(%assetId); -} - -function AssetBrowser::buildMaterialAssetPreview(%this, %assetDef, %previewData, %forcePreviewRegenerate) +function MaterialAsset::buildBrowserElement(%this, %previewData) { %previewData.previewImage = "ToolsModule:genericAssetIcon_image"; %previewData.previewLoaded = false; //this marks it for loading progressively later - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.scriptFile; + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.scriptFile; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; if(%this.selectMode) - %previewData.doubleClickCommand = "AssetBrowser.selectAsset( AssetBrowser.selectedAsset );"; + %previewData.doubleClickCommand = $CurrentAssetBrowser @ ".selectAsset( " @ $CurrentAssetBrowser @ ".selectedAsset );"; else - %previewData.doubleClickCommand = "AssetBrowser.editAsset( "@%assetDef@" );"; + %previewData.doubleClickCommand = $CurrentAssetBrowser @ ".editAsset( "@%this@" );"; - %definitionPath = %assetDef.getScriptPath(); + %definitionPath = %this.getScriptPath(); if(%definitionPath $= "") - %definitionPath = %assetDef.getFilename(); + %definitionPath = %this.getFilename(); - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ + %previewData.tooltip = "Asset Name: " @ %this.assetName @ "\nAsset Type: Material Asset" @ - "\nAsset Definition ID: " @ %assetDef @ + "\nAsset Definition ID: " @ %this @ "\nDefinition Path: " @ %definitionPath; - if(!%this.selectMode) + if(!$CurrentAssetBrowser.selectMode) { - %previewData.doubleClickCommand = "AssetBrowser.editAsset( "@%assetDef@" );"; + %previewData.doubleClickCommand = $CurrentAssetBrowser @ ".editAsset( "@%this@" );"; } } -function AssetBrowser::generateMaterialAssetPreviewImage(%this, %previewButton, %forceRegenerate) +function MaterialAsset::generatePreviewImage(%this, %previewButton, %forceRegenerate) { if(%forceRegenerate $= "") %forceRegenerate = false; - %assetId = %previewButton.moduleName @ ":" @ %previewButton.assetName; - - %assetDef = AssetDatabase.acquireAsset(%assetId); - - %module = %this.dirHandler.getModuleFromAddress(makeRelativePath(filePath(AssetDatabase.getAssetFilePath(%assetDef.getAssetId())))); + %module = $CurrentAssetBrowser.dirHandler.getModuleFromAddress(makeRelativePath(filePath(AssetDatabase.getAssetFilePath(%this.getAssetId())))); %previewPath = "tools/resources/previewCache/" @ %module.moduleId @ "/"; if(!IsDirectory(%previewPath)) { - %this.dirHandler.createFolder(%previewPath); + $CurrentAssetBrowser.dirHandler.createFolder(%previewPath); } %generatePreview = false; - %previewFilePath = %previewPath @ %assetDef.assetName @ "_Preview.dds"; + %previewFilePath = %previewPath @ %this.assetName @ "_Preview.dds"; if(!isFile(%previewFilePath)) { %generatePreview = true; } else { - if(isObject(%assetDef.materialDefinitionName)) + if(isObject(%this.materialDefinitionName)) { - if(compareFileTimes(%assetDef.materialDefinitionName.getDiffuseMap(0), %previewFilePath) == 1 || - compareFileTimes(%assetDef.materialDefinitionName.getFilename(), %previewFilePath) == 1) + if(compareFileTimes(%this.materialDefinitionName.getDiffuseMap(0), %previewFilePath) == 1 || + compareFileTimes(%this.materialDefinitionName.getFilename(), %previewFilePath) == 1) %generatePreview = true; } } - %previewAssetName = %module.moduleId @ "_" @ %assetDef.assetName @ "_PreviewImage"; + %previewAssetName = %module.moduleId @ "_" @ %this.assetName @ "_PreviewImage"; if(%generatePreview || %forceRegenerate) { - if(isObject(%assetDef.materialDefinitionName)) + if(isObject(%this.materialDefinitionName)) { //real fast, we'll be 100% sure that the image resource we need is loaded - %diffuseMapAssetId = %assetDef.materialDefinitionName.getDiffuseMapAsset(0); + %diffuseMapAssetId = %this.materialDefinitionName.getDiffuseMapAsset(0); if(AssetDatabase.isDeclaredAsset(%diffuseMapAssetId)) { %diffuseMapAsset = AssetDatabase.acquireAsset(%diffuseMapAssetId); AssetDatabase.releaseAsset(%diffuseMapAssetId); } %previewShapeDef = AssetDatabase.acquireAsset("ToolsModule:previewSphereShape"); - %generatedFilePath = %previewShapeDef.generateCachedPreviewImage(256, %assetDef.materialDefinitionName); + %generatedFilePath = %previewShapeDef.generateCachedPreviewImage(256, %this.materialDefinitionName); pathCopy(%generatedFilePath, %previewFilePath, false); fileDelete(%generatedFilePath); @@ -548,7 +181,7 @@ function AssetBrowser::generateMaterialAssetPreviewImage(%this, %previewButton, return false; } -function AssetBrowser::onMaterialAssetEditorDropped(%this, %assetDef, %position) +function MaterialAsset::onWorldEditorDropped(%this, %position) { //echo("DROPPED A SHAPE ON THE EDITOR WINDOW!"); //first, see if we hit a static shape @@ -558,26 +191,24 @@ function AssetBrowser::onMaterialAssetEditorDropped(%this, %assetDef, %position) %camPos = LocalClientConnection.camera.getPosition(); %rayResult = materialRayCast(%camPos, %targetPosition, -1, 0, false); - %validTarget = false; if(%rayResult != 0) { %obj = getWord(%rayResult, 0); if(%obj.isMemberOfClass("TSStatic")) { //oh, cool a valid target! - %obj.materialSlot0 = %assetDef.getAssetId(); - echo("MaterialSlot0 set to " @ %assetDef.getAssetId()); + %obj.materialSlot0 = %this.getAssetId(); + //echo("MaterialSlot0 set to " @ %this.getAssetId()); } else if(%obj.isField("materialAsset")) { - %obj.materialAsset = %assetDef.getAssetId(); - echo("materialAsset set to " @ %assetDef.getAssetId()); + %obj.materialAsset = %this.getAssetId(); + //echo("materialAsset set to " @ %this.getAssetId()); } %obj.inspectPostApply(); + + EWorldEditor.isDirty = true; } - - EWorldEditor.isDirty = true; - } function GuiInspectorTypeMaterialAssetPtr::onControlDropped( %this, %payload, %position ) diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/particle.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/particle.tscript deleted file mode 100644 index e69de29bb..000000000 diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/postFX.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/postFX.tscript index 3d684bf47..4933a06e0 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/postFX.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/postFX.tscript @@ -1,8 +1,10 @@ -function AssetBrowser::createPostEffectAsset(%this) +AssetBrowser::registerAssetType("PostEffectAsset", "Post Effects"); + +function PostEffectAsset::onCreateNew() { - %moduleName = AssetBrowser.newAssetSettings.moduleName; + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; - %assetName = AssetBrowser.newAssetSettings.assetName; + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; %assetPath = NewAssetTargetAddress.getText() @ "/"; %tamlpath = %assetPath @ %assetName @ ".asset.taml"; @@ -27,7 +29,7 @@ function AssetBrowser::createPostEffectAsset(%this) %file = new FileObject(); %templateFile = new FileObject(); - %postFXTemplateCodeFilePath = %this.templateFilesPath @ "postFXFile." @ $TorqueScriptFileExtension @ ".template"; + %postFXTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "postFXFile." @ $TorqueScriptFileExtension @ ".template"; if(%file.openForWrite(%scriptPath) && %templateFile.openForRead(%postFXTemplateCodeFilePath)) { @@ -37,7 +39,7 @@ function AssetBrowser::createPostEffectAsset(%this) %line = strreplace( %line, "@@", %assetName ); %file.writeline(%line); - echo(%line); + //echo(%line); } %file.close(); @@ -48,11 +50,11 @@ function AssetBrowser::createPostEffectAsset(%this) %file.close(); %templateFile.close(); - warnf("CreatePostFXAsset - Something went wrong and we couldn't write the PostFX script file!"); + warn("PostEffectAsset::onCreateNew() - Something went wrong and we couldn't write the PostFX script file!"); } //hlsl shader - %postFXTemplateCodeFilePath = %this.templateFilesPath @ "postFXFileP.hlsl.template"; + %postFXTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "postFXFileP.hlsl.template"; if(%file.openForWrite(%hlslPath) && %templateFile.openForRead(%postFXTemplateCodeFilePath)) { @@ -62,7 +64,7 @@ function AssetBrowser::createPostEffectAsset(%this) %line = strreplace( %line, "@@", %assetName ); %file.writeline(%line); - echo(%line); + //echo(%line); } %file.close(); @@ -73,11 +75,11 @@ function AssetBrowser::createPostEffectAsset(%this) %file.close(); %templateFile.close(); - warnf("CreatePostFXAsset - Something went wrong and we couldn't write the PostFX hlsl file!"); + warn("PostEffectAsset::onCreateNew() - Something went wrong and we couldn't write the PostFX hlsl file!"); } //glsl shader - %postFXTemplateCodeFilePath = %this.templateFilesPath @ "postFXFileP.glsl.template"; + %postFXTemplateCodeFilePath = $CurrentAssetBrowser.templateFilesPath @ "postFXFileP.glsl.template"; if(%file.openForWrite(%glslPath) && %templateFile.openForRead(%postFXTemplateCodeFilePath)) { @@ -87,7 +89,7 @@ function AssetBrowser::createPostEffectAsset(%this) %line = strreplace( %line, "@@", %assetName ); %file.writeline(%line); - echo(%line); + //echo(%line); } %file.close(); @@ -98,72 +100,80 @@ function AssetBrowser::createPostEffectAsset(%this) %file.close(); %templateFile.close(); - warnf("CreatePostFXAsset - Something went wrong and we couldn't write the PostFX glsl file!"); + warn("PostEffectAsset::onCreateNew() - Something went wrong and we couldn't write the PostFX glsl file!"); } return %tamlpath; } -//Renames the asset -function AssetBrowser::renamePostEffectAsset(%this, %assetDef, %newAssetName) +function PostEffectAsset::buildBrowserElement(%this, %previewData) { - %newScriptFilename = renameAssetLooseFile(%assetDef.scriptPath, %newAssetName); - - if(!%newScriptFilename $= "") - return; - - %newHLSLFilename = renameAssetLooseFile(%assetDef.hlslShader, %newAssetName); - - if(!%newHLSLFilename $= "") - return; - - %newGLSLFilename = renameAssetLooseFile(%assetDef.glslShader, %newAssetName); - - if(!%newGLSLFilename $= "") - return; - - %assetDef.scriptPath = %newScriptFilename; - %assetDef.hlslShader = %newHLSLFilename; - %assetDef.glslShader = %newGLSLFilename; - %assetDef.saveAsset(); - - renameAssetFile(%assetDef, %newAssetName); -} - -//Deletes the asset -function AssetBrowser::deletePostEffectAsset(%this, %assetDef) -{ - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); -} - -//Moves the asset to a new path/module -function AssetBrowser::movePostEffectAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getScriptPath(), %destination); - moveAssetLooseFile(%assetDef.getHLSLShaderPath(), %destination); - moveAssetLooseFile(%assetDef.getGLSLShaderPath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::buildPostEffectAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.scriptFilePath; + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.scriptFilePath; %previewData.doubleClickCommand = ""; %previewData.previewImage = "ToolsModule:postEffectIcon_image"; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ "\nDefinition Path: " @ %assetDef.getFilename(); + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = "Asset Name: " @ %this.assetName @ + "\nDefinition Path: " @ %this.getFilename(); +} + +function PostEffectAsset::onShowActionMenu(%this) +{ + GenericAsset::onShowActionMenu(%this); + + if( !isObject( EditPostFXAssetSubmenuPopup ) ) + { + new PopupMenu( EditPostFXAssetSubmenuPopup ) + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; + + jumpFileName = ""; + jumpLineNumber = ""; + }; + } + + //Regen the menu so we're fully up and current with options and references + EditPostFXAssetSubmenuPopup.clearItems(); + + EditPostFXAssetSubmenuPopup.item[ 0 ] = "Script file" TAB "" TAB %this @ ".onEditScriptFile();"; + EditPostFXAssetSubmenuPopup.item[ 1 ] = "HLSL file" TAB "" TAB %this @ ".onEditHLSLFile();"; + EditPostFXAssetSubmenuPopup.item[ 2 ] = "GLSL file" TAB "" TAB %this @ ".onEditHLSLFile();"; + + EditPostFXAssetSubmenuPopup.reloadItems(); + + EditAssetPopup.item[ 0 ] = "Edit PostFX Asset Files" TAB EditPostFXAssetSubmenuPopup; + + EditAssetPopup.reloadItems(); + + EditAssetPopup.showPopup(Canvas); + + $CurrentAssetBrowser.popupMenu = EditAssetPopup; +} + +function PostEffectAsset::onEditScriptFile(%this) +{ + if(isFunction("systemCommand")) + eval("systemCommand(\"start \\\"\\\" \\\"" @ %this.getScriptPath() @ "\\\"\");"); + else + warn("PostEffectAsset::onEditScriptFile() - systemCommand function disabled in this build. Unable to launch application to edit file."); +} + +function PostEffectAsset::onEditHLSLFile(%this) +{ + if(isFunction("systemCommand")) + eval("systemCommand(\"start \\\"\\\" \\\"" @ %this.getHLSLShaderPath() @ "\\\"\");"); + else + warn("PostEffectAsset::onEditHLSLFile() - systemCommand function disabled in this build. Unable to launch application to edit file."); +} + +function PostEffectAsset::onEditGLSLFile(%this) +{ + if(isFunction("systemCommand")) + eval("systemCommand(\"start \\\"\\\" \\\"" @ %this.getGLSLShaderPath() @ "\\\"\");"); + else + warn("PostEffectAsset::onEditGLSLFile() - systemCommand function disabled in this build. Unable to launch application to edit file."); } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/prefab.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/prefab.tscript index dba63f846..08a8cf393 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/prefab.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/prefab.tscript @@ -1,9 +1,11 @@ -function AssetBrowser::createPrefab(%this) +AssetBrowser::registerFileType("PrefabFileType", "Prefabs", ".prefab"); + +function PrefabFileType::onCreateNew() { - %moduleName = AssetBrowser.newAssetSettings.moduleName; + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; - %assetName = AssetBrowser.newAssetSettings.assetName; + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; %assetPath = NewAssetTargetAddress.getText() @ "/"; @@ -13,46 +15,32 @@ function AssetBrowser::createPrefab(%this) EWorldEditor.makeSelectionPrefab( %prefabFilePath ); EditorTree.buildVisibleTree( true ); + + $CurrentAssetBrowser.refresh(); } -function AssetBrowser::buildPrefabPreview(%this, %assetDef, %previewData) +function PrefabFileType::buildBrowserElement(%filePath, %previewData) { - %fullPath = %assetDef.dirPath @ "/" @ %assetDef.assetName; - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %fullPath; + %previewData.assetName = fileName(%filePath); + %previewData.assetPath = %filePath; + + echo("PrefabFileType::buildBrowserElement() - name, path: " @ %previewData.assetName @ ", " @ %filePath); %previewData.previewImage = "ToolsModule:prefabIcon_image"; //%previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %fullPath; + %previewData.assetDesc = %filePath; + %previewData.tooltip = %filePath; //%previewData.doubleClickCommand = "AssetBrowser.schedule(10, \"navigateTo\",\""@ %assetDef.dirPath @ "/" @ %assetDef.assetName @"\");";//browseTo %assetDef.dirPath / %assetDef.assetName - %previewData.doubleClickCommand = "AssetBrowser.autoImportFile(\"" @ %fullPath @ "\");"; + %previewData.doubleClickCommand = "AssetBrowser.autoImportFile(\"" @ %filePath @ "\");"; } -function AssetBrowser::onPrefabEditorDropped(%this, %assetDef, %position) +function PrefabFileType::onWorldEditorDropped(%filePath, %position) { - //echo("DROPPED A SHAPE ON THE EDITOR WINDOW!"); - - %targetPosition = EWorldEditor.unproject(%position SPC 1); - %camPos = LocalClientConnection.camera.getPosition(); - %rayResult = containerRayCast(%camPos, %targetPosition, -1); - - %pos = ObjectCreator.getCreateObjectPosition(); - - if(%rayResult != 0) - { - %pos = getWords(%rayResult, 1, 3); - } - else - { - %pos = "0 0 0"; - } - %newPrefab = new Prefab() { - position = %pos; - fileName = %assetDef; + position = %position; + fileName = %filePath; }; getScene(0).add(%newPrefab); diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/script.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/script.tscript index cc93fcdc1..2d9d2fcbe 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/script.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/script.tscript @@ -1,9 +1,12 @@ -function AssetBrowser::createScriptAsset(%this) +AssetBrowser::registerFileType("ScriptFileType", "Script Files", ".tscript;.cs;.dso;.tscript.dso;.cs.dso", false); +AssetBrowser::registerAssetType("ScriptAsset", "Scripts"); + +function ScriptAsset::onCreateNew() { - %moduleName = AssetBrowser.newAssetSettings.moduleName; + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; - %assetName = AssetBrowser.newAssetSettings.assetName; + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; %assetPath = NewAssetTargetAddress.getText() @ "/"; @@ -32,97 +35,122 @@ function AssetBrowser::createScriptAsset(%this) return %tamlpath; } -function AssetBrowser::editScriptAsset(%this, %assetDef) +function ScriptAsset::onEdit(%this) { - %scriptFile = %assetDef.scriptFile; + %scriptFile = %this.scriptFile; - //EditorOpenFileInTorsion(makeFullPath(%scriptFile), 0); + if(isFunction("systemCommand")) + eval("systemCommand(\"start \\\"\\\" \\\"" @ %this.fileName @ "\\\"\");"); + else + warn("ScriptAsset::onEdit() - systemCommand function disabled in this build. Unable to launch application to edit file."); } -function AssetBrowser::duplicateScriptAsset(%this, %assetDef, %targetModule) +function ScriptAsset::buildBrowserElement(%this, %previewData) { -} - -function AssetBrowser::importScriptAsset(%this, %assetId) -{ -} - -function AssetBrowser::onScriptAssetEditorDropped(%this, %assetDef, %position) -{ - -} - -//Renames the asset -function AssetBrowser::renameScriptAsset(%this, %assetDef, %newAssetName) -{ - %newFilename = renameAssetLooseFile(%assetDef.scriptFile, %newAssetName); + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.fileName; - if(!%newFilename $= "") - return; - - %assetDef.scriptFile = %newFilename; - %assetDef.saveAsset(); - - renameAssetFile(%assetDef, %newAssetName); -} - -//Deletes the asset -function AssetBrowser::deleteScriptAsset(%this, %assetDef) -{ - AssetDatabase.deleteAsset(%assetDef.getAssetId(), true); -} - -//Moves the asset to a new path/module -function AssetBrowser::moveScriptAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getScriptPath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::buildScriptAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.scriptFile; - //%previewData.doubleClickCommand = "EditorOpenFileInTorsion( \""@%previewData.assetPath@"\", 0 );"; - - if(%assetDef.isServerSide) + if(%this.isServerSide) %previewData.previewImage = "ToolsModule:serverScriptIcon_image"; else %previewData.previewImage = "ToolsModule:clientScriptIcon_image"; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ "\nDefinition Path: " @ %assetDef.getFilename(); -} - -function AssetBrowser::buildTScriptPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.scriptFile; - //%previewData.doubleClickCommand = "EditorOpenFileInTorsion( \""@%previewData.assetPath@"\", 0 );"; - - if(%assetDef.isServerSide) - %previewData.previewImage = "ToolsModule:serverScriptIcon_image"; - else - %previewData.previewImage = "ToolsModule:clientScriptIcon_image"; - - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ "\nDefinition Path: " @ %assetDef.getFilename(); + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.fileName; + %previewData.tooltip = "Script Asset: " @ %this.getAssetId() @ "\n" @ + "File path: " @ %this.fileName; } function GuiInspectorTypeScriptAssetPtr::onClick( %this, %fieldName ) { //Get our data %obj = %this.getInspector().getInspectObject(0); +} + +//============================================================================== +// Handling for random script files in the browser +//============================================================================== +function ScriptFileType::buildBrowserElement(%filePath, %previewData) +{ + %previewData.assetName = fileName(%filePath); + %previewData.assetPath = %filePath; + %previewData.doubleClickCommand = "systemCommand(\"start \\\"\\\" \\\"" @ %filePath @ "\\\"\");"; + + /*if(%assetDef.isServerSide) + %previewData.previewImage = "ToolsModule:serverScriptIcon_image"; + else + %previewData.previewImage = "ToolsModule:clientScriptIcon_image";*/ + %previewData.previewImage = "ToolsModule:clientScriptIcon_image"; + + %previewData.assetFriendlyName = %previewData.assetName; + %previewData.assetDesc = %filePath; + %previewData.tooltip = "Script File: " @ %filePath; +} + +function ScriptAssetType::onEdit(%filePath) +{ + if(isFunction("systemCommand")) + eval("systemCommand(\"start \\\"\\\" \\\"" @ %filePath @ "\\\"\");"); + else + warn("ScriptAssetType::onEdit() - systemCommand function disabled in this build. Unable to launch application to edit file."); +} + +function ScriptFileType::onShowActionMenu(%filePath) +{ + if( !isObject( EditScriptFileTypePopup ) ) + { + new PopupMenu( EditScriptFileTypePopup ) + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; + + jumpFileName = ""; + jumpLineNumber = ""; + }; + } + + //Regen the menu so we're fully up and current with options and references + EditScriptFileTypePopup.clearItems(); + + EditScriptFileTypePopup.item[ 0 ] = "Edit File" TAB "" TAB $CurrentAssetBrowser @ ".editAsset();"; + EditScriptFileTypePopup.item[ 1 ] = "Rename File" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();"; + EditScriptFileTypePopup.item[ 2 ] = "-"; + EditScriptFileTypePopup.item[ 3 ] = "Duplicate File" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();"; + EditScriptFileTypePopup.item[ 4 ] = "-"; + EditScriptFileTypePopup.item[ 5 ] = "Open File Location" TAB "" TAB $CurrentAssetBrowser @ ".openFileLocation();"; + EditScriptFileTypePopup.item[ 6 ] = "-"; + EditScriptFileTypePopup.item[ 7 ] = "Delete File" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();"; + EditScriptFileTypePopup.item[ 8 ] = "-"; + EditScriptFileTypePopup.item[ 9 ] = "File Properties" TAB "" TAB $CurrentAssetBrowser @ ".editAssetInfo();"; + + EditScriptFileTypePopup.objectData = %filePath; + EditScriptFileTypePopup.objectType = "ScriptFileType"; + + EditScriptFileTypePopup.reloadItems(); + + EditScriptFileTypePopup.showPopup(Canvas); + + $CurrentAssetBrowser.popupMenu = EditScriptFileTypePopup; +} + +function ScriptFileType::onEditProperties(%filePath) +{ + Canvas.pushDialog(AssetBrowser_editAsset); + + AssetBrowser_editAssetWindow.text = "Script File Properties - " @ %filePath; + + AssetBrowser_editAsset.editedObjectData = %filePath; + //AssetBrowser_editAsset.editedObject = SpecialAssetEditInspector.tempAsset; + AssetBrowser_editAsset.editedObjectType = "ScriptFileType"; + + AssetEditInspector.startGroup("Script File Properties"); + AssetEditInspector.addField("$ScriptFileType::ExecutionMode", "Execution Mode", "list", "How should this script file be executed by the module", "Server", "Client;Server;Shared;None"); + AssetEditInspector.addField("$ScriptFileType::ExecutionOverride", "Do Execution Override", "bool", "Should this execution override files that share the same name?", "false"); + AssetEditInspector.addField("$ScriptFileType::ExecutionConditions", "Execution Conditional", "command", "Does the execution of this script file have special conditionals?", ""); + AssetEditInspector.endGroup("Script File Properties"); +} + +function ScriptFileType::onSaveProperties(%filePath) +{ + //Do voodoo here } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript index 7ca8a3efe..7330738ad 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shape.tscript @@ -1,285 +1,60 @@ -function AssetBrowser::createShapeAsset(%this) +AssetBrowser::registerAssetType("ShapeAsset", "Shapes"); + +function ShapeAsset::onEdit(%this) { - %moduleName = AssetBrowser.newAssetSettings.moduleName; - %modulePath = "data/" @ %moduleName; - - %assetName = AssetBrowser.newAssetSettings.assetName; - - %assetPath = NewAssetTargetAddress.getText() @ "/"; - - %tamlpath = %assetPath @ %assetName @ ".asset.taml"; - %shapeFilePath = %assetPath @ %assetName @ ".dae"; - - %asset = new ShapeAsset() + if(EditorSettings.value("Assets/Browser/doubleClickAction", "Edit Asset") $= "Edit Asset") { - AssetName = %assetName; - versionId = 1; - friendlyName = AssetBrowser.newAssetSettings.friendlyName; - description = AssetBrowser.newAssetSettings.description; - fileName = %assetName @ ".dae"; - }; - - TamlWrite(%asset, %tamlpath); - - Canvas.popDialog(AssetBrowser_newComponentAsset); - - %moduleDef = ModuleDatabase.findModule(%moduleName, 1); - AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); - - AssetBrowser.refresh(); - - return %tamlpath; -} - -function AssetBrowser::editShapeAsset(%this, %assetDef) -{ - %this.hideDialog(); - EditorGui.setEditor( ShapeEditorPlugin ); - ShapeEditorPlugin.openShapeAsset(%assetDef); -} - -function AssetBrowser::onShapeAssetChanged(%this, %assetDef) -{ -} - -function AssetBrowser::deleteShapeAsset(%this, %assetDef) -{ - -} - -function AssetBrowser::moveShapeAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getShapePath(), %destination); - moveAssetLooseFile(%assetDef.getShapeConstructorFilePath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::prepareImportShapeAsset(%this, %assetItem) -{ - ImportActivityLog.add("Preparing Shape for Import: " @ %assetItem.assetName); - - %fileExt = fileExt(%assetItem.filePath); - - if(!isObject(%assetItem.shapeInfo)) - { - %shapeInfo = new GuiTreeViewCtrl(); - if(%fileExt $= ".dae") - { - enumColladaForImport(%assetItem.filePath, %shapeInfo, false); - } - else if(%fileExt $= ".dts") - { - %shapeInfo.insertItem(0, "Shape", 1); - %shapeInfo.insertItem(0, "Animations", 0); - } - else - { - GetShapeInfo(%assetItem.filePath, %shapeInfo, false); - } - - %assetItem.shapeInfo = %shapeInfo; + $CurrentAssetBrowser.hideDialog(); + EditorGui.setEditor( ShapeEditorPlugin ); + ShapeEditorPlugin.openShapeAsset(%this); } - - %shapeCount = %assetItem.shapeInfo._meshCount; - %shapeItem = %assetItem.shapeInfo.findItemByName("Meshes"); - - //%shapeId = ImportAssetTree.findItemByObjectId(%assetItem); - - if(getAssetImportConfigValue("Meshes/ImportMesh", "1") == 1 && %shapeCount > 0) + else { - } - - %animCount = %assetItem.shapeInfo._animCount; - %animItem = %assetItem.shapeInfo.findItemByName("Animations"); - - if(getAssetImportConfigValue("Animations/ImportAnimations", "1") == 1 && %animCount > 0) - { - } - - %matCount = %assetItem.shapeInfo._materialCount; - %matItem = %assetItem.shapeInfo.findItemByName("Materials"); - - ImportActivityLog.add(" Shape Info: Mesh Count: " @ %shapeCount @ " | Material Count: " @ %matCount @ " | Anim Count: " @ %animCount); - - if(getAssetImportConfigValue("Materials/ImportMaterials", "1") == 1 && %matCount > 0) - { - %materialItem = %assetItem.shapeInfo.getChild(%matItem); - processShapeMaterialInfo(%assetItem, %materialItem); - - %materialItem = %assetItem.shapeInfo.getNextSibling(%materialItem); - while(%materialItem != 0) - { - processShapeMaterialInfo(%assetItem, %materialItem); - - %materialItem = %assetItem.shapeInfo.getNextSibling(%materialItem); - } - } -} - -function AssetBrowser::importShapeAsset(%this, %assetItem) -{ - %moduleName = AssetImportTargetModule.getText(); - - %assetType = %assetItem.AssetType; - %filePath = %assetItem.filePath; - %assetName = %assetItem.assetName; - %assetImportSuccessful = false; - %assetId = %moduleName@":"@%assetName; - - %assetPath = NewAssetTargetAddress.getText() @ "/"; - %assetFullPath = %assetPath @ fileName(%filePath); - - %newAsset = new ShapeAsset() - { - assetName = %assetName; - versionId = 1; - fileName = fileName(%filePath); - isNewShape = true; - }; - - //No point in indicating the original path data if it was imported in-place - if(!startsWith(makeFullPath(%filePath), getMainDotCsDir())) - { - %newAsset.originalFilePath = %filePath; - } - - //check dependencies - %dependencySlotId = 0; - for(%i=0; %i < %assetItem.childAssetItems.count(); %i++) - { - %childAssetItem = %assetItem.childAssetItems.getKey(%i); - - if(!isObject(%childAssetItem) || %childAssetItem.skip || %childAssetItem.processed == false) - continue; - - %depAssetType = %childAssetItem.assetType; - if(%childAssetItem.assetType $= "MaterialAsset") - { - %matSet = "%newAsset.materialSlot"@%dependencySlotId@"=\"@asset="@%moduleName@":"@%childAssetItem.assetName@"\";"; - eval(%matSet); - %dependencySlotId++; - } - else if(%depAssetType $= "AnimationAsset") - { - %matSet = "%newAsset.animationSequence"@%dependencySlotId@"=\"@asset="@%moduleName@":"@%childAssetItem.assetName@"\";"; - eval(%matSet); - %dependencySlotId++; - } - } - - %assetImportSuccessful = TAMLWrite(%newAsset, %assetPath @ %assetName @ ".asset.taml"); - - //and copy the file into the relevent directory - if(filePath(%filePath) !$= filePath(%assetFullPath)) - { - %doOverwrite = !AssetBrowser.isAssetReImport; - if(!pathCopy(%filePath, %assetFullPath, %doOverwrite)) - { - error("Unable to import asset: " @ %filePath); - } - } - - %constructor = ShapeEditor.findConstructor( %assetFullPath ); - - if(!isObject(%constructor)) - %constructor = ShapeEditor.createConstructor(%assetFullPath); - - //We'll update any relevent bits to the ShapeConstructor here - $TSShapeConstructor::neverImportMat = ""; - - if(getAssetImportConfigValue("Materials/IgnoreMaterials", "") !$= "") - { - %ignoreMaterialList = getAssetImportConfigValue("Materials/IgnoreMaterials", ""); - %ignoredMatNamesCount = getTokenCount(%ignoreMaterialList, ",;"); - for(%i=0; %i < %ignoredMatNamesCount; %i++) - { - if(%i==0) - $TSShapeConstructor::neverImportMat = getToken(%ignoreMaterialList, ",;", %i); - else - $TSShapeConstructor::neverImportMat = $TSShapeConstructor::neverImportMat TAB getToken(%ignoreMaterialList, ",;", %i); - } + %this.onWorldEditorDropped(); } - - if(getAssetImportConfigValue("Materials/DoUpAxisOverride", "") $= "1") - %constructor.upAxis = getAssetImportConfigValue("Meshes/UpAxisOverride", "Z_AXIS"); - - if(getAssetImportConfigValue("Meshes/DoScaleOverride", "0") $= "1") - %constructor.unit = getAssetImportConfigValue("Meshes/ScaleOverride", "1"); - else - %constructor.unit = -1; - - %constructor.lodType = getAssetImportConfigValue("Meshes/LODType", "0"); - //%constructor.singleDetailSize = getAssetImportConfigValue("Meshes/convertLeftHanded", "0"); - %constructor.alwaysImport = getAssetImportConfigValue("Meshes/AlwaysImportedNodes", ""); - %constructor.neverImport = getAssetImportConfigValue("Meshes/AlwaysIgnoreNodes", ""); - %constructor.alwaysImportMesh = getAssetImportConfigValue("Meshes/AlwaysImportMeshes", ""); - %constructor.neverImportMesh = getAssetImportConfigValue("Meshes/AlwaysIgnoreMeshes", ""); - %constructor.ignoreNodeScale = getAssetImportConfigValue("Meshes/IgnoreNodeScale", "0"); - %constructor.adjustCenter = getAssetImportConfigValue("Meshes/AdjustCenter", "0"); - %constructor.adjustFloor = getAssetImportConfigValue("Meshes/AdjustFloor", "0"); - - %constructor.convertLeftHanded = getAssetImportConfigValue("Meshes/convertLeftHanded", "0"); - %constructor.calcTangentSpace = getAssetImportConfigValue("Meshes/calcTangentSpace", "0"); - %constructor.genUVCoords = getAssetImportConfigValue("Meshes/genUVCoords", "0"); - %constructor.flipUVCoords = getAssetImportConfigValue("Meshes/flipUVCoords", "0"); - %constructor.findInstances = getAssetImportConfigValue("Meshes/findInstances", "0"); - %constructor.limitBoneWeights = getAssetImportConfigValue("Meshes/limitBoneWeights", "0"); - %constructor.joinIdenticalVerts = getAssetImportConfigValue("Meshes/joinIdenticalVerts", "0"); - %constructor.reverseWindingOrder = getAssetImportConfigValue("Meshes/reverseWindingOrder", "0"); - %constructor.invertNormals = getAssetImportConfigValue("Meshes/invertNormals", "0"); - %constructor.removeRedundantMats = getAssetImportConfigValue("Meshes/removeRedundantMats", "0"); - %constructor.animTiming = getAssetImportConfigValue("Animations/animTiming", "Seconds"); - %constructor.animFPS = getAssetImportConfigValue("Animations/animFPS", "2"); - - %constructor.neverImportMat = $TSShapeConstructor::neverImportMat; - ShapeEditor.saveConstructor( %constructor ); - - //now, force-load the file if it's collada - /*%fileExt = fileExt(%assetFullPath); - if(isSupportedFormat(getSubStr(%fileExt,1))) - { - %tempShape = new TSStatic() - { - shapeName = %assetFullPath; - }; - - %tempShape.delete(); - }*/ - - %moduleDef = ModuleDatabase.findModule(%moduleName,1); - - if(!AssetBrowser.isAssetReImport) - AssetDatabase.addDeclaredAsset(%moduleDef, %assetPath @ %assetName @ ".asset.taml"); - else - AssetDatabase.refreshAsset(%assetId); } -function AssetBrowser::buildShapeAssetPreview(%this, %assetDef, %previewData, %forcePreviewRegenerate) +function ShapeAsset::onShowActionMenu(%assetDef) +{ + GenericAsset::onShowActionMenu(%assetDef); +} + +function ShapeAsset::onChanged(%this) +{ + echo("ShapeAsset::onChanged() - asset " @ %this.assetId @ " has changed!"); +} + +function ShapeAsset::onDelete(%this) +{ + //Special handle the cache preview image + %module = $CurrentAssetBrowser.dirHandler.getModuleFromAddress(makeRelativePath(filePath(%this.getShapePath()))); + %previewPath = "tools/resources/previewCache/" @ %module.moduleId @ "/" @ %this.assetName @ "_Preview.dds"; + + if(isFile(%previewPath)) + { + $CurrentAssetBrowser.dirHandler.deleteFile(%previewPath); + } + + //then just let the Generic function handle the rest + GenericAsset::onDelete(%this); +} + +function ShapeAsset::buildBrowserElement(%this, %previewData) { %previewData.previewImage = "ToolsModule:genericAssetIcon_image"; %previewData.previewLoaded = false; //this marks it for loading progressively later - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.fileName; + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.fileName; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ "\n" @ + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = "Asset Name: " @ %this.assetName @ "\n" @ "Asset Type: Shape Asset\n" @ - "Asset Definition ID: " @ %assetDef @ "\n" @ - "Shape File path: " @ %assetDef.getShapePath(); + "Asset Definition ID: " @ %this @ "\n" @ + "Shape File path: " @ %this.getShapePath(); - if(%this.selectMode) + /*if(%this.selectMode) { %previewData.doubleClickCommand = "AssetBrowser.selectAsset( AssetBrowser.selectedAsset );"; } @@ -293,41 +68,39 @@ function AssetBrowser::buildShapeAssetPreview(%this, %assetDef, %previewData, %f { %previewData.doubleClickCommand = "AssetBrowser.onShapeAssetEditorDropped( "@%assetDef@" );"; } - } + }*/ } -function AssetBrowser::generateShapeAssetPreviewImage(%this, %previewButton, %forceRegenerate) +function ShapeAsset::generatePreviewImage(%this, %previewButton, %forceRegenerate) { if(%forceRegenerate $= "") %forceRegenerate = false; - %assetId = %previewButton.moduleName @ ":" @ %previewButton.assetName; - - %assetDef = AssetDatabase.acquireAsset(%assetId); - - %module = %this.dirHandler.getModuleFromAddress(makeRelativePath(filePath(%assetDef.getShapePath()))); + %assetId = %this.getAssetId(); + + %module = %previewButton.assetBrowser.dirHandler.getModuleFromAddress(makeRelativePath(filePath(%this.getShapePath()))); %previewPath = "tools/resources/previewCache/" @ %module.moduleId @ "/"; if(!IsDirectory(%previewPath)) { - %this.dirHandler.createFolder(%previewPath); + %previewButton.assetBrowser.dirHandler.createFolder(%previewPath); } %generatePreview = false; - %previewFilePath = %previewPath @ %assetDef.assetName @ "_Preview.dds"; - if(!isFile(%previewFilePath) || (compareFileTimes(%assetDef.getShapePath(), %previewFilePath) == 1)) + %previewFilePath = %previewPath @ %this.assetName @ "_Preview.dds"; + if(!isFile(%previewFilePath) || (compareFileTimes(%this.getShapePath(), %previewFilePath) == 1)) { %generatePreview = true; } - %previewAssetName = %module.moduleId @ "_" @ %assetDef.assetName @ "_PreviewImage"; + %previewAssetName = %module.moduleId @ "_" @ %this.assetName @ "_PreviewImage"; if(%generatePreview || %forceRegenerate) { //real fast, we'll be 100% sure that the image resource we need is loaded - %matSlot0AssetId = %assetDef.materialSlot0; + %matSlot0AssetId = %this.materialSlot0; if(AssetDatabase.isDeclaredAsset(%matSlot0AssetId)) { %matAsset = AssetDatabase.acquireAsset(%matSlot0AssetId); @@ -337,7 +110,7 @@ function AssetBrowser::generateShapeAssetPreviewImage(%this, %previewButton, %fo //This is slightly hacky, but we're going to utilize the imposter/last detail system //to generate our previews for us and then clean up the unneeded bits - %filePath = %assetDef.generateCachedPreviewImage(); + %filePath = %this.generateCachedPreviewImage(); pathCopy(%filePath, %previewFilePath, false); fileDelete(%filePath); //cleanup @@ -380,7 +153,7 @@ function AssetBrowser::generateShapeAssetPreviewImage(%this, %previewButton, %fo return false; } -function AssetBrowser::onShapeAssetEditorDropped(%this, %assetDef, %position) +function ShapeAsset::onWorldEditorDropped(%assetDef, %position) { %assetId = %assetDef.getAssetId(); @@ -402,6 +175,11 @@ function AssetBrowser::onShapeAssetEditorDropped(%this, %assetDef, %position) MECreateUndoAction::submit(%newStatic ); } +function ShapeAsset::onGUIEditorDropped(%assetDef, %position) +{ + +} + function GuiInspectorTypeShapeAssetPtr::onControlDropped( %this, %payload, %position ) { Canvas.popDialog(EditorDragAndDropLayer); diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shapeAnimation.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shapeAnimation.tscript index 2593599b5..d0aa7ac03 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shapeAnimation.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/shapeAnimation.tscript @@ -1,57 +1,16 @@ -function AssetBrowser::createShapeAnimationAsset(%this) -{ - %dlg = new OpenFileDialog() - { - Filters = "Animation Files(*.dae, *.cached.dts)|*.dae;*.cached.dts"; - DefaultPath = $Pref::WorldEditor::LastPath; - DefaultFile = ""; - ChangePath = false; - OverwritePrompt = true; - forceRelativePath = false; - //MultipleFiles = true; - }; +AssetBrowser::registerAssetType("ShapeAnimationAsset", "Shape Anims"); - %ret = %dlg.Execute(); - - if ( %ret ) - { - $Pref::WorldEditor::LastPath = filePath( %dlg.FileName ); - %fullPath = %dlg.FileName; - } - - %dlg.delete(); - - if ( !%ret ) - return; -} - -function AssetBrowser::editShapeAnimationAsset(%this, %assetDef) +function ShapeAnimationAsset::onEdit(%this) { - %this.hideDialog(); + $CurrentAssetBrowser.hideDialog(); EditorGui.setEditor( ShapeEditorPlugin ); - ShapeEditorPlugin.openShapeAsset(%assetDef); + ShapeEditorPlugin.openShapeAsset(%this); } -function AssetBrowser::moveShapeAnimationAsset(%this, %assetDef, %destination) +function ShapeAnimationAsset::buildBrowserElement(%this, %previewData) { - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getAnimationPath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::buildShapeAnimationAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.animationName; - %previewData.assetPath = %assetDef.scriptFile; + %previewData.assetName = %this.animationName; + %previewData.assetPath = %this.scriptFile; //Lotta prepwork /*%previewData.doubleClickCommand = %assetDef@".materialDefinitionName.reload(); " @@ -66,7 +25,9 @@ function AssetBrowser::buildShapeAnimationAssetPreview(%this, %assetDef, %previe %previewData.previewImage = "ToolsModule:animationIcon_image"; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %assetDef.friendlyName @ "\n" @ %assetDef @ "\nShape File path: " @ %assetDef.getShapePath(); + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = %this.friendlyName @ "\n" + @ %this @ "\nShape File path: " + @ %this.getShapePath(); } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/sound.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/sound.tscript index f5a072d5b..8ed6919bf 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/sound.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/sound.tscript @@ -1,61 +1,50 @@ -function AssetBrowser::editSoundAsset(%this, %assetDef) +AssetBrowser::registerAssetType("SoundAsset", "Sounds"); + +function SoundAsset::onEdit(%this) { if (isObject($PreviewSoundSource)) sfxStop($PreviewSoundSource); - $PreviewSoundSource = %assetDef.playSound(); + $PreviewSoundSource = %this.playSound(); } -function AssetBrowser::onSoundAssetChanged(%this, %assetDef) +function SoundAsset::onChanged(%this) { if (isObject($PreviewSoundSource)) sfxStop($PreviewSoundSource); } -function AssetBrowser::moveSoundAsset(%this, %assetDef, %destination) +function SoundAsset::buildBrowserElement(%this, %previewData) { - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getSoundPath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::buildSoundAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.soundFilePath; + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.soundFilePath; if(%this.selectMode) { - %previewData.doubleClickCommand = "AssetBrowser.selectAsset( AssetBrowser.selectedAsset );"; + %previewData.doubleClickCommand = $CurrentAssetBrowser @ ".selectAsset( " @ $CurrentAssetBrowser @ ".selectedAsset );"; } else { - if(EditorSettings.value("Assets/Browser/doubleClickAction", "Edit Asset") $= "Edit Asset") + %previewData.doubleClickCommand = $CurrentAssetBrowser @ ".editAsset( "@ %this @" );"; + + /*if(EditorSettings.value("Assets/Browser/doubleClickAction", "Edit Asset") $= "Edit Asset") { - %previewData.doubleClickCommand = "AssetBrowser.editAsset( "@%assetDef@" );"; + %previewData.doubleClickCommand = $CurrentAssetBrowser @ ".editAsset( "@ %this @" );"; } else { - %previewData.doubleClickCommand = "AssetBrowser.onSoundAssetEditorDropped( "@%assetDef@" );"; - } + %previewData.doubleClickCommand = $CurrentAssetBrowser @ ".onSoundAssetEditorDropped( "@ %this @" );"; + }*/ } %previewData.previewImage = "ToolsModule:soundIcon_image"; - %previewData.assetFriendlyName = %assetDef.assetName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ "\nDefinition Path: " @ %assetDef.getFilename(); + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = "Asset Name: " @ %this.assetName @ + "\nDefinition Path: " @ %this.getFilename(); } -function AssetBrowser::onSoundAssetEditorDropped(%this, %assetDef, %position) +function SoundAsset::onWorldEditorDropped(%this, %position) { %targetPosition = EWorldEditor.unproject(%position SPC 1); %camPos = LocalClientConnection.camera.getPosition(); @@ -72,14 +61,12 @@ function AssetBrowser::onSoundAssetEditorDropped(%this, %assetDef, %position) %pos = "0 0 0"; } - %assetId = %assetDef.getAssetId(); - %newSFXEmitter = new SFXEmitter() { position = %pos; - soundAsset = %assetDef.getAssetId(); - pitch = %assetDef.pitchAdjust; - volume = %assetDef.volumeAdjust; + soundAsset = %this.getAssetId(); + pitch = %this.pitchAdjust; + volume = %this.volumeAdjust; }; getScene(0).add(%newSFXEmitter); @@ -91,15 +78,15 @@ function AssetBrowser::onSoundAssetEditorDropped(%this, %assetDef, %position) } -function AssetBrowser::onSoundAssetGUIEditorDropped(%this, %assetDef, %position) +function SoundAsset::onGUIEditorDropped(%this, %position) { - %assetId = %assetDef.getAssetId(); + %assetId = %this.getAssetId(); %cmd = "new GuiAudioCtrl(){"; %cmd = %cmd @ "SoundAsset =\""@ %assetId @"\";"; %cmd = %cmd @ "position =\""@ %position @"\";"; %cmd = %cmd @ "};"; %ctrl = GuiEditCanvas.createObject(%cmd); - echo(%ctrl SPC "created"); + //echo(%ctrl SPC "created"); } function GuiInspectorTypeSoundAssetPtr::onControlDropped( %this, %payload, %position ) diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/stateMachine.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/stateMachine.tscript deleted file mode 100644 index 4234f6d28..000000000 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/stateMachine.tscript +++ /dev/null @@ -1,161 +0,0 @@ -function AssetBrowser::createStateMachineAsset(%this) -{ - %assetName = AssetBrowser.newAssetSettings.assetName; - %moduleName = AssetBrowser.selectedModule; - %assetPath = NewAssetTargetAddress.getText() @ "/"; - - %assetQuery = new AssetQuery(); - - %matchingAssetCount = AssetDatabase.findAssetName(%assetQuery, %assetName); - - %i=1; - while(%matchingAssetCount > 0) - { - %newAssetName = %assetName @ %i; - %i++; - - %matchingAssetCount = AssetDatabase.findAssetName(%assetQuery, %newAssetName); - } - - %assetName = %newAssetName; - - %assetQuery.delete(); - - %tamlpath = %assetPath @ %assetName @ ".asset.taml"; - %smFilePath = %assetPath @ %assetName @ ".xml"; - - %asset = new StateMachineAsset() - { - AssetName = %assetName; - versionId = 1; - stateMachineFile = %assetName @ ".xml"; - }; - - %xmlDoc = new SimXMLDocument(); - %xmlDoc.saveFile(%smFilePath); - %xmlDoc.delete(); - - TamlWrite(%asset, %tamlpath); - - //Now write our XML file - %xmlFile = new FileObject(); - %xmlFile.openForWrite(%smFilePath); - %xmlFile.writeLine(""); - %xmlFile.writeLine(""); - %xmlFile.close(); - - %moduleDef = ModuleDatabase.findModule(%moduleName, 1); - AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); - - AssetBrowser.refresh(); - - return %tamlpath; -} - -function AssetBrowser::editStateMachineAsset(%this, %assetDef) -{ - eval("AssetBrowser.tempAsset = new " @ %assetDef.getClassName() @ "();"); - AssetBrowser.tempAsset.assignFieldsFrom(%assetDef); - - SMAssetEditInspector.inspect(AssetBrowser.tempAsset); - AssetBrowser_editAsset.editedAssetId = EditAssetPopup.assetId; - AssetBrowser_editAsset.editedAsset = AssetBrowser.tempAsset; - - //remove some of the groups we don't need: - for(%i=0; %i < SMAssetEditInspector.getCount(); %i++) - { - %caption = SMAssetEditInspector.getObject(%i).caption; - - if(%caption $= "Ungrouped" || %caption $= "Object" || %caption $= "Editing" - || %caption $= "Persistence" || %caption $= "Dynamic Fields") - { - SMAssetEditInspector.remove(SMAssetEditInspector.getObject(%i)); - %i--; - } - } - - Canvas.pushDialog(StateMachineEditor); - StateMachineEditor.loadStateMachineAsset(EditAssetPopup.assetId); - StateMachineEditor-->Window.text = "State Machine Editor ("@EditAssetPopup.assetId@")"; -} - -function AssetBrowser::duplicateStateMachineAsset(%this, %assetDef) -{ - // TODO: - %targetModule = ""; - - //Check if we have a target module, if not we need to select one - if(%targetModule $= "") - { - error("AssetBrowser::duplicateStateMachineAsset - No target module selected!"); - return; - } - - %assetId = %assetDef.getAssetId(); - %assetName = AssetDatabase.getAssetName(%assetId); - - //First step, copy the files - %modulePath = "data/" @ %targetModule @ "/stateMachines/"; - - if(!isDirectory(%modulePath)) - createPath(%modulePath); - - %assetFile = AssetDatabase.getAssetFilePath(%assetId); - %stateMachineFile = %assetDef.stateMachineFile; - - echo("AssetBrowser::duplicateGameObjectAsset - duplicating! " @ %assetId @ " to " @ %targetModule); - - %tamlPath = %modulePath @ fileName(%assetFile); - - pathCopy(%assetFile, %tamlPath); - pathCopy(%stateMachineFile, %modulePath @ fileName(%stateMachineFile)); - - echo("AssetBrowser::duplicateStateMachineAsset - duplicated!"); - - //Register the asset - %moduleDef = ModuleDatabase.findModule(%targetModule, 1); - - AssetDatabase.addDeclaredAsset(%moduleDef, %tamlPath); - - //Refresh the browser - AssetBrowser.refresh(); - - //Rename it for convenience - AssetBrowser.performRenameAsset(%assetName, "New" @ %assetName); -} - -function AssetBrowser::renameGameObjectAsset(%this, %assetDef, %newAssetId, %originalName, %newName) -{ - %assetPath = AssetDatabase.getAssetFilePath(%newAssetId); - - //rename the file to match - %path = filePath(%assetPath); - - %oldScriptFilePath = %assetDef.stateMachineFile; - %scriptFilePath = filePath(%assetDef.stateMachineFile); - %scriptExt = fileExt(%assetDef.stateMachineFile); - - %newScriptFileName = %scriptFilePath @ "/" @ %newName @ %scriptExt; - %newAssetFile = %path @ "/" @ %newName @ ".asset.taml"; - - %assetDef.stateMachineFile = %newScriptFileName; - - TamlWrite(%assetDef, %newAssetFile); - fileDelete(%assetPath); - - pathCopy(%oldScriptFilePath, %newScriptFileName); - fileDelete(%oldScriptFilePath); -} - -function AssetBrowser::buildStateMachineAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; - %previewData.assetPath = %assetDef.scriptFile; - %previewData.doubleClickCommand = "AssetBrowser.editStateMachineAsset( "@%assetDef@" );"; - - %previewData.previewImage = "ToolsModule:stateMachineIcon_image"; - - %previewData.assetFriendlyName = %assetDef.friendlyName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %assetDef.friendlyName @ "\n" @ %assetDef; -} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/terrain.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/terrain.tscript index 410360801..dd8e17524 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/terrain.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/terrain.tscript @@ -1,23 +1,25 @@ -function AssetBrowser::setupCreateNewTerrainAsset(%this) +AssetBrowser::registerAssetType("TerrainAsset", "Terrains"); + +function TerrainAsset::setupCreateNew() { NewAssetPropertiesInspector.startGroup("Terrain"); - NewAssetPropertiesInspector.addField("resolution", "Terrain Texture Resolution", "list", "Is this script used on the server?", "1024", "256,512,1024,2048,4096", %this.newAssetSettings); - NewAssetPropertiesInspector.addField("genWithNoise", "Generate Terrain With Noise", "bool", "Is this script used on the server?", "0", "2", %this.newAssetSettings); + NewAssetPropertiesInspector.addField("resolution", "Terrain Texture Resolution", "list", "Is this script used on the server?", "1024", "256,512,1024,2048,4096", $CurrentAssetBrowser.newAssetSettings); + NewAssetPropertiesInspector.addField("genWithNoise", "Generate Terrain With Noise", "bool", "Is this script used on the server?", "0", "2", $CurrentAssetBrowser.newAssetSettings); NewAssetPropertiesInspector.endGroup(); /*NewAssetPropertiesInspector.startGroup("Terrain - Import"); - NewAssetPropertiesInspector.addField("importDetails", "Import Heightmap", "button", "Import an existing heightmap", "", "Canvas.pushDialog( TerrainImportGui );", %this.newAssetSettings); + NewAssetPropertiesInspector.addField("importDetails", "Import Heightmap", "button", "Import an existing heightmap", "", "Canvas.pushDialog( TerrainImportGui );", $CurrentAssetBrowser.newAssetSettings); NewAssetPropertiesInspector.endGroup();*/ } -function AssetBrowser::createTerrainAsset(%this) +function TerrainAsset::onCreateNew() { - %moduleName = AssetBrowser.newAssetSettings.moduleName; + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; - %assetName = AssetBrowser.newAssetSettings.assetName; + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; - %assetType = AssetBrowser.newAssetSettings.assetType; + %assetType = $CurrentAssetBrowser.newAssetSettings.assetType; %assetPath = NewAssetTargetAddress.getText() @ "/"; //Ensure anything we generate goes into the right directory @@ -31,8 +33,8 @@ function AssetBrowser::createTerrainAsset(%this) AssetName = %assetName; versionId = 1; terrainFile = %assetName @ ".ter"; - resolution = %this.newAssetSettings.resolution; - genWithNoise = %this.newAssetSettings.genWithNoise; + resolution = $CurrentAssetBrowser.newAssetSettings.resolution; + genWithNoise = $CurrentAssetBrowser.newAssetSettings.genWithNoise; }; TamlWrite(%asset, %tamlpath); @@ -40,9 +42,9 @@ function AssetBrowser::createTerrainAsset(%this) %moduleDef = ModuleDatabase.findModule(%moduleName, 1); AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); - AssetBrowser.refresh(); + $CurrentAssetBrowser.refresh(); - $createdTerrainBlock = TerrainBlock::createNew( %assetName, %this.newAssetSettings.resolution, "", %this.newAssetSettings.genWithNoise ); + $createdTerrainBlock = TerrainBlock::createNew( %assetName, $CurrentAssetBrowser.newAssetSettings.resolution, "", $CurrentAssetBrowser.newAssetSettings.genWithNoise ); MECreateUndoAction::submit($createdTerrainBlock); @@ -55,12 +57,12 @@ function AssetBrowser::createTerrainAsset(%this) // This will update an existing terrain with the name %terrainName, // or create a new one if %terrainName isn't a TerrainBlock $createdTerrainBlock = TerrainBlock::import( $createdTerrainBlock, - AssetBrowser.newAssetSettings.heightMapPng, - AssetBrowser.newAssetSettings.metersPerPixel, - AssetBrowser.newAssetSettings.heightScale, - AssetBrowser.newAssetSettings.opacityNames, - AssetBrowser.newAssetSettings.materialNames, - AssetBrowser.newAssetSettings.flipYAxis ); + $CurrentAssetBrowser.newAssetSettings.heightMapPng, + $CurrentAssetBrowser.newAssetSettings.metersPerPixel, + $CurrentAssetBrowser.newAssetSettings.heightScale, + $CurrentAssetBrowser.newAssetSettings.opacityNames, + $CurrentAssetBrowser.newAssetSettings.materialNames, + $CurrentAssetBrowser.newAssetSettings.flipYAxis ); if ( isObject( $createdTerrainBlock ) ) { @@ -130,87 +132,35 @@ function createTerrainBlock(%assetId) // } -function AssetBrowser::editTerrainAsset(%this, %assetDef) + +function TerrainAsset::onWorldEditorDropped(%this, %position) { + createTerrainBlock(%this.getAssetId()); } -function AssetBrowser::duplicateTerrainAsset(%this, %assetDef, %targetModule) +function TerrainAsset::buildBrowserElement(%this, %previewData) { -} - -function AssetBrowser::importTerrainAsset(%this, %assetDef) -{ -} - -function AssetBrowser::dragAndDropTerrainAsset(%this, %assetDef, %dropTarget) -{ - if(!isObject(%dropTarget)) - return; -} - -function AssetBrowser::onTerrainAssetEditorDropped(%this, %assetDef, %position) -{ - createTerrainBlock(%assetDef.getAssetId()); -} - -function AssetBrowser::renameTerrainAsset(%this, %assetDef, %newAssetId, %originalName, %newName) -{ -} - -function AssetBrowser::deleteTerrainAsset(%this, %assetDef) -{ -} - -function AssetBrowser::moveTerrainAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getTerrainFilePath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::buildTerrainAssetPreview(%this, %assetDef, %previewData) -{ - %previewData.assetName = %assetDef.assetName; + %previewData.assetName = %this.assetName; %previewData.assetPath = ""; %previewData.previewImage = "ToolsModule:terrainIcon_image"; - %previewData.assetFriendlyName = %assetDef.gameObjectName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ + %previewData.assetFriendlyName = %this.gameObjectName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = "Asset Name: " @ %this.assetName @ "\nAsset Type: Terrain Asset" @ - "\nAsset Definition ID: " @ %assetDef @ - "\nDefinition Path: " @ %assetDef.getTerrainFilePath(); + "\nAsset Definition ID: " @ %this @ + "\nDefinition Path: " @ %this.getTerrainFilePath(); if(%this.selectMode) { - %previewData.doubleClickCommand = "AssetBrowser.selectAsset( AssetBrowser.selectedAsset );"; + %previewData.doubleClickCommand = $CurrentAssetBrowser @ ".selectAsset( " @ $CurrentAssetBrowser @ ".selectedAsset );"; } else { if(EditorSettings.value("Assets/Browser/doubleClickAction", "Edit Asset") $= "Edit Asset") %previewData.doubleClickCommand = ""; else - %previewData.doubleClickCommand = "createTerrainBlock(\""@%assetDef.getAssetId()@"\");"; + %previewData.doubleClickCommand = "createTerrainBlock(\""@%this.getAssetId()@"\");"; } -} - -function GuiInspectorTypeTerrainAssetPtr::onClick( %this, %fieldName ) -{ - //Get our data - %obj = %this.getInspector().getInspectObject(0); -} - -function GuiInspectorTypeTerrainAssetPtr::onControlDropped( %this, %payload, %position ) -{ - } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/terrainMaterial.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/terrainMaterial.tscript index e67a8274a..5b73b94ac 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/terrainMaterial.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/terrainMaterial.tscript @@ -1,11 +1,13 @@ -function AssetBrowser::createTerrainMaterialAsset(%this) +AssetBrowser::registerAssetType("TerrainMaterialAsset", "Terrain Materials"); + +function TerrainMaterialAsset::onCreateNew() { - %moduleName = AssetBrowser.newAssetSettings.moduleName; + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; %modulePath = "data/" @ %moduleName; - %assetName = AssetBrowser.newAssetSettings.assetName; + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; - %assetType = AssetBrowser.newAssetSettings.assetType; + %assetType = $CurrentAssetBrowser.newAssetSettings.assetType; %assetPath = NewAssetTargetAddress.getText() @ "/"; %tamlpath = %assetPath @ %assetName @ ".asset.taml"; @@ -57,7 +59,7 @@ function AssetBrowser::createTerrainMaterialAsset(%this) AssetDatabase.acquireAsset("\"" @ %moduleName @ ":" @ %assetName @ "\""); - AssetBrowser.refresh(); + $CurrentAssetBrowser.refresh(); //If we've got the terrain mat editor open, go ahead and update it all TerrainMaterialDlg.onWake(); @@ -65,112 +67,70 @@ function AssetBrowser::createTerrainMaterialAsset(%this) return %tamlpath; } -function AssetBrowser::editTerrainMaterialAsset(%this, %assetDef) +function TerrainAssetMaterial::onEdit(%this) { TerrainMaterialDlg.show(0, 0, 0); - TerrainMaterialDlg.setActiveMaterial(%assetDef.assetName); + TerrainMaterialDlg.setActiveMaterial(%this.assetName); } -function AssetBrowser::duplicateTerrainMaterialAsset(%this, %assetDef, %targetModule) -{ -} - -function AssetBrowser::importTerrainMaterialAsset(%this, %assetDef) -{ -} - -function AssetBrowser::dragAndDropTerrainMaterialAsset(%this, %assetDef, %dropTarget) -{ - if(!isObject(%dropTarget)) - return; -} - -function AssetBrowser::renameTerrainMaterialAsset(%this, %assetDef, %newAssetId, %originalName, %newName) -{ -} - -function AssetBrowser::deleteTerrainMaterialAsset(%this, %assetDef) -{ -} - -function AssetBrowser::moveTerrainMaterialAsset(%this, %assetDef, %destination) -{ - %currentModule = AssetDatabase.getAssetModule(%assetDef.getAssetId()); - %targetModule = AssetBrowser.dirHandler.getModuleFromAddress(%destination); - - %newAssetPath = moveAssetFile(%assetDef, %destination); - - if(%newAssetPath $= "") - return false; - - moveAssetLooseFile(%assetDef.getScriptPath(), %destination); - - AssetDatabase.removeDeclaredAsset(%assetDef.getAssetId()); - AssetDatabase.addDeclaredAsset(%targetModule, %newAssetPath); -} - -function AssetBrowser::buildTerrainMaterialAssetPreview(%this, %assetDef, %previewData, %forcePreviewRegenerate) +function TerrainAssetMaterial::buildBrowserElement(%this, %previewData) { %previewData.previewImage = "ToolsModule:genericAssetIcon_image"; %previewData.previewLoaded = false; //this marks it for loading progressively later - %previewData.assetName = %assetDef.assetName; + %previewData.assetName = %this.assetName; %previewData.assetPath = ""; %previewData.doubleClickCommand = ""; - %previewData.assetFriendlyName = %assetDef.gameObjectName; - %previewData.assetDesc = %assetDef.description; - %previewData.tooltip = %assetDef.gameObjectName; + %previewData.assetFriendlyName = %this.gameObjectName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = %this.gameObjectName; - %definitionPath = %assetDef.getScriptPath(); + %definitionPath = %this.getScriptPath(); if(%definitionPath $= "") - %definitionPath = %assetDef.getFilename(); + %definitionPath = %this.getFilename(); - %previewData.tooltip = "Asset Name: " @ %assetDef.assetName @ + %previewData.tooltip = "Asset Name: " @ %this.assetName @ "\nAsset Type: Terrain Material Asset" @ - "\nAsset Definition ID: " @ %assetDef @ + "\nAsset Definition ID: " @ %this @ "\nDefinition Path: " @ %definitionPath; } -function AssetBrowser::generateTerrainMaterialAssetPreviewImage(%this, %previewButton, %forceRegenerate) +function TerrainAssetMaterial::generatePreviewImage(%this, %previewButton, %forceRegenerate) { if(%forceRegenerate $= "") %forceRegenerate = false; - %assetId = %previewButton.moduleName @ ":" @ %previewButton.assetName; - - %assetDef = AssetDatabase.acquireAsset(%assetId); - - %module = %this.dirHandler.getModuleFromAddress(makeRelativePath(filePath(AssetDatabase.getAssetFilePath(%assetDef.getAssetId())))); + %module = $CurrentAssetBrowser.dirHandler.getModuleFromAddress(makeRelativePath(filePath(AssetDatabase.getAssetFilePath(%this.getAssetId())))); %previewPath = "tools/resources/previewCache/" @ %module.moduleId @ "/"; if(!IsDirectory(%previewPath)) { - %this.dirHandler.createFolder(%previewPath); + $CurrentAssetBrowser.dirHandler.createFolder(%previewPath); } %generatePreview = false; - %previewFilePath = %previewPath @ %assetDef.assetName @ "_Preview.dds"; + %previewFilePath = %previewPath @ %this.assetName @ "_Preview.dds"; if(!isFile(%previewFilePath)) { %generatePreview = true; } else { - if(isObject(%assetDef.materialDefinitionName)) + if(isObject(%this.materialDefinitionName)) { - if(compareFileTimes(%assetDef.materialDefinitionName.getDiffuseMap(), %previewFilePath) == 1 || - compareFileTimes(%assetDef.materialDefinitionName.getFilename(), %previewFilePath) == 1) + if(compareFileTimes(%this.materialDefinitionName.getDiffuseMap(), %previewFilePath) == 1 || + compareFileTimes(%this.materialDefinitionName.getFilename(), %previewFilePath) == 1) %generatePreview = true; } } - %previewAssetName = %module.moduleId @ "_" @ %assetDef.assetName @ "_PreviewImage"; + %previewAssetName = %module.moduleId @ "_" @ %this.assetName @ "_PreviewImage"; if(%generatePreview || %forceRegenerate) { - if(isObject(%assetDef.materialDefinitionName)) + if(isObject(%this.materialDefinitionName)) { %previewShapeDef = AssetDatabase.acquireAsset("ToolsModule:previewSphereShape"); %generatedFilePath = %previewShapeDef.generateCachedPreviewImage(256, DummyTerrMatPreview); @@ -178,7 +138,6 @@ function AssetBrowser::generateTerrainMaterialAssetPreviewImage(%this, %previewB pathCopy(%generatedFilePath, %previewFilePath); fileDelete(%generatedFilePath); - if(!AssetDatabase.isDeclaredAsset("ToolsModule:" @ %previewAssetName)) { %previewAsset = new ImageAsset() @@ -216,15 +175,4 @@ function AssetBrowser::generateTerrainMaterialAssetPreviewImage(%this, %previewB } return false; -} - -function GuiInspectorTypeTerrainMaterialAssetPtr::onClick( %this, %fieldName ) -{ - //Get our data - %obj = %this.getInspector().getInspectObject(0); -} - -function GuiInspectorTypeTerrainMaterialAssetPtr::onControlDropped( %this, %payload, %position ) -{ - } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/directoryHandling.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/directoryHandling.tscript index 0df04615a..92ecd22e8 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/directoryHandling.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/directoryHandling.tscript @@ -179,6 +179,39 @@ function directoryHandler::getModuleFromAddress(%this, %address) return ""; } +function getModuleFromAddress(%address) +{ + %moduleList = ModuleDatabase.findModules(); + + for(%i=0; %i < getWordCount(%moduleList); %i++) + { + %module = getWord(%moduleList, %i); + %modulePath = makeRelativePath(%module.ModulePath); + + //We don't want to add stuff directly to the root core or tools modules + //if(%modulePath $= "Core" || %modulePath $= "Tools") + // continue; + + if(startsWith(%address, %modulePath)) + { + return %module; + } + } + /*//break down the address + %folderCount = getTokenCount(%address, "/"); + + for(%f=0; %f < %folderCount; %f++) + { + %folderName = getToken(%address, "/", %f); + + %module = ModuleDatabase.findModule(%folderName); + if(%module !$= "") + return %module; + }*/ + + return ""; +} + function directoryHandler::getFolderTreeItemFromAddress(%this, %address) { //break down the address diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript index 226635dba..e46569169 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/editAsset.tscript @@ -1,67 +1,30 @@ function AssetBrowser_editAsset::saveAsset(%this) { - %file = AssetDatabase.getAssetFilePath(%this.editedAssetId); - %success = TamlWrite(AssetBrowser_editAsset.editedAsset, %file); + AssetBrowser_editAssetWindow.text = "Asset Properties"; - AssetBrowser.reloadAsset(%this.editedAssetId); - - AssetBrowser.refresh(); - - %assetType = AssetDatabase.getAssetType(%this.editedAssetId); - %assetDef = AssetDatabase.acquireAsset(%this.editedAssetId); - %assetDef.refreshAsset(); - - AssetBrowser.call("on" @ %assetType @ "Changed", %assetDef); - AssetDatabase.releaseAsset(%this.editedAssetId); + AssetBrowser.callAssetTypeFunc(%this.editedObjectType, "onSaveProperties", %this.editedObjectData); + AssetBrowser.callAssetTypeFunc(%this.editedObjectType, "onChanged", %this.editedObjectData); Canvas.popDialog(AssetBrowser_editAsset); } function AssetBrowser::editAsset(%this, %assetDef) { - //Find out what type it is - //If the passed-in definition param is blank, then we're likely called via a popup - if(%assetDef $= "") + if(%this.selectMode) { - if(AssetDatabase.isDeclaredAsset(EditAssetPopup.assetId)) - { - %assetDef = AssetDatabase.acquireAsset(EditAssetPopup.assetId); - } - else - { - //if it's not a valid asset at all, then it's probably a folder - %folder = strreplace(EditAssetPopup.assetId, ":", "/"); - if(isDirectory(%folder)) - { - AssetBrowser.navigateTo(%folder); - } - else - { - %object = getToken(EditAssetPopup.assetId, ":", 1); - if(isObject(%object)) - { - if(%object.isMemberOfClass("SimDatablock")) - { - DatablockEditorPlugin.openDatablock( %object ); - } - } - } - } + %this.selectAsset(%this.selectedAsset); } - else if(AssetDatabase.isDeclaredAsset(%assetDef)) + else { - //Turns out we were passed an assetid, not an asset definition. - //Grab the asset def from that - %assetDef = AssetDatabase.acquireAsset(%assetDef); - } - - if(%assetDef !$= "") - { - %assetType = %assetDef.getClassName(); - - //Build out the edit command - %buildCommand = %this @ ".edit" @ %assetType @ "(" @ %assetDef @ ");"; - eval(%buildCommand); + if(isObject(%assetDef)) + { + %assetType = AssetDatabase.getAssetType(%assetDef.getAssetId()); + %this.callAssetTypeFunc(%assetType, "onEdit", %assetDef); + } + else if(%this.popupMenu.objectType !$= "" && %this.popupMenu.objectData !$= "") + { + %this.callAssetTypeFunc(%this.popupMenu.objectType, "onEdit", %this.popupMenu.objectData); + } } } @@ -75,27 +38,9 @@ function AssetBrowser::appendSubLevel(%this) function AssetBrowser::editAssetInfo(%this) { - Canvas.pushDialog(AssetBrowser_editAsset); - - %assetDef = AssetDatabase.acquireAsset(EditAssetPopup.assetId); - - AssetBrowser.tempAsset = %assetDef.deepClone(); - - AssetEditInspector.inspect(AssetBrowser.tempAsset); - AssetBrowser_editAsset.editedAssetId = EditAssetPopup.assetId; - AssetBrowser_editAsset.editedAsset = AssetBrowser.tempAsset; - - //remove some of the groups we don't need: - for(%i=0; %i < AssetEditInspector.getCount(); %i++) + if(%this.popupMenu.objectType !$= "" && %this.popupMenu.objectData !$= "") { - %caption = AssetEditInspector.getObject(%i).caption; - - if(%caption $= "Ungrouped" || %caption $= "Object" || %caption $= "Editing" - || %caption $= "Persistence" || %caption $= "Dynamic Fields") - { - AssetEditInspector.remove(AssetEditInspector.getObject(%i)); - %i--; - } + %this.callAssetTypeFunc(%this.popupMenu.objectType, "onEditProperties", %this.popupMenu.objectData); } } @@ -127,17 +72,17 @@ function AssetBrowser::refreshAsset(%this, %assetId) //------------------------------------------------------------ function AssetBrowser::regeneratePreviewImage(%this) { - %assetDef = AssetDatabase.acquireAsset(EditAssetPopup.assetId); + %assetDef = AssetDatabase.acquireAsset(AssetBrowser.popupMenu.objectData); %dummyObj = new ScriptObject(); - %dummyObj.moduleName = AssetDatabase.getAssetModule(EditAssetPopup.assetId).moduleId; - %dummyObj.assetName = AssetDatabase.getAssetName(EditAssetPopup.assetId); - - %regenCommand = "AssetBrowser.generate" @ EditAssetPopup.assetType @ - "PreviewImage(" @ %dummyObj @ ", true);"; - eval(%regenCommand); + %dummyObj.moduleName = AssetDatabase.getAssetModule(AssetBrowser.popupMenu.objectData).moduleId; + %dummyObj.assetName = AssetDatabase.getAssetName(AssetBrowser.popupMenu.objectData); + + %assetType = AssetBrowser.popupMenu.objectType; + + AssetBrowser.callAssetTypeFunc(%assetType, "generatePreviewImage", %dummyObj, true); %dummyObj.delete(); - AssetDatabase.releaseAsset(EditAssetPopup.assetId); + AssetDatabase.releaseAsset(AssetBrowser.popupMenu.objectData); } //------------------------------------------------------------ @@ -319,8 +264,8 @@ function moveAssetLooseFile(%file, %destinationPath) function AssetBrowser::duplicateAsset(%this) { - %assetDef = AssetDatabase.acquireAsset(EditAssetPopup.assetId); - %assetType = AssetDatabase.getAssetType(EditAssetPopup.assetId); + %assetDef = AssetDatabase.acquireAsset(%this.popupMenu.objectData); + %assetType = %this.popupMenu.objectType; %trailingNum = getTrailingNumber(%assetDef.assetName); if(%trailingNum != -1) @@ -345,18 +290,16 @@ function AssetBrowser::duplicateAsset(%this) function AssetBrowser::doDuplicateAsset(%this) { - %assetDef = AssetDatabase.acquireAsset(EditAssetPopup.assetId); - %assetType = AssetDatabase.getAssetType(EditAssetPopup.assetId); + %assetType = %this.popupMenu.objectType; + %assetDef = AssetDatabase.acquireAsset(%this.popupMenu.objectData); if(AssetBrowser_assetNameEditTxt.text !$= "" && AssetBrowser_assetNameEditTxt.text !$= %assetDef.assetName) { - //this acts as a redirect based on asset type and will enact the appropriate function - //so for a GameObjectAsset, it'll become %this.duplicateGameObjectAsset(%assetDef, %targetModule); - //and call to the tools/assetBrowser/scripts/assetTypes/gameObject.tscript file for implementation - if(%this.isMethod("duplicate"@%assetType)) - eval(%this @ ".duplicate"@%assetType@"("@%assetDef@","@AssetBrowser_assetNameEditTxt.text@");"); - - AssetBrowser.refresh(); + AssetDatabase.releaseAsset(%this.popupMenu.objectData); + + %this.callAssetTypeFunc(%assetType, "onDuplicate", %this.popupMenu.objectData, AssetBrowser_assetNameEditTxt.text); + + %this.refresh(); } } @@ -415,31 +358,8 @@ function AssetBrowser::confirmDeleteAsset(%this) %currentSelectedItem = AssetBrowserFilterTree.getSelectedItem(); %currentItemParent = AssetBrowserFilterTree.getParentItem(%currentSelectedItem); - if(EditFolderPopup.visible) - { - if(EditFolderPopup.dirPath !$= "") - %folderPath = EditFolderPopup.dirPath; - else - %folderPath = AssetBrowserFilterTree.getItemValue(%currentSelectedItem) @ "/" @ AssetBrowserFilterTree.getItemText(%currentSelectedItem); - - if(%this.isMethod("deleteFolder")) - eval(%this @ ".deleteFolder(\""@%folderPath@"\");"); - } - else - { - %assetDef = AssetDatabase.acquireAsset(EditAssetPopup.assetId); - %assetType = AssetDatabase.getAssetType(EditAssetPopup.assetType); - - if(!isObject(%assetDef)) - return; - - //Do any cleanup required given the type - if(%this.isMethod("delete"@%assetType)) - eval(%this @ ".delete"@%assetType@"("@%assetDef@");"); - - AssetDatabase.deleteAsset(EditAssetPopup.assetId, true, false); - } - + %this.callAssetTypeFunc(%this.popupMenu.objectType, "onDelete", %this.popupMenu.objectData); + %this.refresh(); } @@ -497,21 +417,27 @@ function AssetBrowser::updateAssetReference(%this, %targetPath, %oldAssetId, %ne function AssetBrowser::openFileLocation(%this) { - %filePath = ""; - if(EditAssetPopup.assetId !$= "") + if(isFunction("systemCommand")) { - if(AssetDatabase.isDeclaredAsset(EditAssetPopup.assetId)) + warnf("AssetBrowser::openFileLocation() - systemCommand function disabled in this build. Unable to launch application to edit file."); + return; + } + + %filePath = ""; + if(%this.popuMenu.assetId !$= "") + { + if(AssetDatabase.isDeclaredAsset(%this.popuMenu.objectData)) { - %filePath = AssetDatabase.getAssetPath(EditAssetPopup.assetId); + %filePath = AssetDatabase.getAssetPath(%this.popuMenu.objectData); } else { //probably a file path - %pathSplit = strpos(EditAssetPopup.assetId, ":"); + %pathSplit = strpos(%this.popuMenu.objectData, ":"); if(%pathSplit != -1) { - %path = getSubStr(EditAssetPopup.assetId, 0, %pathSplit); - %file = getSubStr(EditAssetPopup.assetId, %pathSplit + 1); + %path = getSubStr(%this.popuMenu.objectData, 0, %pathSplit); + %file = getSubStr(%this.popuMenu.objectData, %pathSplit + 1); //datablocks pack the originator file in the parent path as-is, so check that if(fileExt(%path) !$= "") @@ -525,14 +451,6 @@ function AssetBrowser::openFileLocation(%this) } } } - else if(EditLevelAssetPopup.assetId !$= "") - { - %filePath = AssetDatabase.getAssetPath(EditAssetPopup.assetId); - } - else if(EditTerrainAssetPopup.assetId !$= "") - { - %filePath = AssetDatabase.getAssetPath(EditAssetPopup.assetId); - } if(isFile(%filePath) || isDirectory(%filePath)) { @@ -553,20 +471,27 @@ function AssetBrowser::openFileLocation(%this) } } -function AssetBrowser::openFolderLocation(%this) +function AssetBrowser::openFolderLocation(%this, %folderPath) { - %filePath = AssetBrowser.dirHandler.currentAddress; + if(!isFunction("systemCommand")) + { + warn("AssetBrowser::openFolderLocation() - systemCommand function disabled in this build. Unable to launch application to open this folder."); + return; + } - if(%filePath !$= "") + if(%folderPath $= "") + %folderPath = filePath(%this.dirHandler.currentAddress); + + if(%folderPath !$= "") { if($platform $= "windows") { - %cmd = "cd \"" @ makeFullPath(%filePath) @ "\" && start ."; + %cmd = "cd \"" @ makeFullPath(%folderPath) @ "\" && start ."; systemCommand(%cmd); } else { - %cmd = "open \"" @ makeFullPath(%filePath) @ "\""; + %cmd = "open \"" @ makeFullPath(%folderPath) @ "\""; systemCommand(%cmd); } } diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/newAsset.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/newAsset.tscript index c4fe9b537..ab22b4238 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/newAsset.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/newAsset.tscript @@ -119,11 +119,7 @@ function AssetBrowser::setupCreateNewAsset(%this, %assetType, %moduleName, %call NewAssetPropertiesInspector.addCallbackField("description", "Description", "Command", "Description of the new asset", "", "", "updateNewAssetField", %this.newAssetSettings); NewAssetPropertiesInspector.endGroup(); - if(%this.isMethod("setupCreateNew"@%assetType)) - { - %command = %this @ ".setupCreateNew"@%assetType @"();"; - eval(%command); - } + %this.callAssetTypeFunc(%assetType, "setupCreateNew"); } function NewAssetPropertiesInspector::updateNewAssetField(%this) @@ -253,7 +249,7 @@ function CreateNewAsset() return; } - %assetFilePath = eval(AssetBrowser @ ".create"@%assetType@"();"); + %assetFilePath = AssetBrowser.callAssetTypeFunc(%assetType, "onCreateNew"); Canvas.popDialog(AssetBrowser_newAsset); diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/popupMenus.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/popupMenus.tscript index 469e742ac..53ad8efff 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/popupMenus.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/popupMenus.tscript @@ -182,8 +182,6 @@ function AssetBrowser::buildPopupMenus(%this) item[12] = "View Loose Files" TAB "" TAB "AssetBrowser.importLooseFiles();"; Item[ 13 ] = "-"; item[ 14 ] = "Open Folder Location" TAB "" TAB "AssetBrowser.openFolderLocation();"; - - }; } @@ -265,13 +263,18 @@ function AssetBrowser::buildPopupMenus(%this) radioSelection = false; }; - AssetTypeListPopup.addItem(0, AssetFilterTypeList.getKey(0) TAB "" TAB "AssetBrowser.toggleAssetTypeFilter(" @ 0 @ ");"); + AssetTypeListPopup.addItem(0, "All" TAB "" TAB %this @ ".toggleAssetTypeFilter(0);"); AssetTypeListPopup.addItem(1, "-"); - for(%i=1; %i < AssetFilterTypeList.Count(); %i++) + %listIndex = 1; + for(%i=0; %i < ABAssetTypesList.Count(); %i++) { - %assetTypeName = AssetFilterTypeList.getKey(%i); - AssetTypeListPopup.addItem(%i+1, %assetTypeName TAB "" TAB "AssetBrowser.toggleAssetTypeFilter(" @ %i + 1 @ ");"); + %assetTypeData = ABAssetTypesList.getValue(%i); + if(getField(%assetTypeData, 3) != true) + continue; + + AssetTypeListPopup.addItem(%listIndex, getField(%assetTypeData,1) TAB "" TAB %this @ ".toggleAssetTypeFilter(" @ %listIndex @ ");"); + %listIndex++; } } @@ -314,44 +317,6 @@ function AssetBrowser::buildPopupMenus(%this) }; } - if( !isObject( EditGameObjectAssetPopup ) ) - { - new PopupMenu( EditGameObjectAssetPopup ) - { - superClass = "MenuBuilder"; - class = "EditorWorldMenu"; - //isPopup = true; - - item[ 0 ] = "Open GameObject Editor" TAB "" TAB "echo(\"Not yet implemented.\");"; - item[ 1 ] = "Edit GameObject Script" TAB "" TAB "AssetBrowser.editGameObjectAssetScript(AssetDatabase.acquireAsset(EditGameObjectAssetPopup.assetId));"; - item[ 2 ] = "-"; - item[ 3 ] = "Apply Instance to GameObject" TAB "" TAB "AssetBrowser.applyInstanceToGameObject(AssetDatabase.acquireAsset(EditGameObjectAssetPopup.assetId));"; - item[ 4 ] = "Reset Instance to GameObject" TAB "" TAB "echo(\"Not yet implemented.\");"; - item[ 5 ] = "-"; - item[ 6 ] = "Create Child GameObject" TAB "" TAB "echo(\"Not yet implemented.\");"; - }; - } - - //Asset Import Resolution menus - if( !isObject( ImportAssetResolutionsPopup ) ) - { - %this.ImportAssetResolutionsPopup = new PopupMenu( ImportAssetResolutionsPopup ) - { - superClass = "MenuBuilder"; - class = "EditorWorldMenu"; - - item[0] = "Use original Asset for duplicates" TAB "" TAB ""; - item[1] = "Override duplicate with new Asset" TAB "" TAB ""; - item[2] = "-"; - item[3] = "Rename Asset" TAB "" TAB ""; - item[4] = "-"; - item[5] = "Find missing file" TAB "" TAB "ImportAssetWindow.findMissingFile(ImportAssetResolutionsPopup.assetItem);"; - item[6] = "-"; - item[7] = "Edit Asset properties" TAB "" TAB ""; - - }; - } - // // Import Asset Actions // diff --git a/Templates/BaseGame/game/tools/gui/fieldTypes/listField.tscript b/Templates/BaseGame/game/tools/gui/fieldTypes/listField.tscript index c26f3638d..eb95ba633 100644 --- a/Templates/BaseGame/game/tools/gui/fieldTypes/listField.tscript +++ b/Templates/BaseGame/game/tools/gui/fieldTypes/listField.tscript @@ -87,11 +87,11 @@ function GuiInspectorVariableGroup::buildListField(%this, %fieldName, %fieldLabe eval(%setCommand); } - %listCount = getTokenCount(%fieldDataVals, ","); + %listCount = getTokenCount(%fieldDataVals, ",;"); for(%i=0; %i < %listCount; %i++) { - %entryText = getToken(%fieldDataVals, ",", %i); + %entryText = getToken(%fieldDataVals, ",;", %i); %editControl.add(%entryText); } From 5566f8a3962556915de44ef82f9d382f0f6966f6 Mon Sep 17 00:00:00 2001 From: JeffR Date: Sun, 30 Mar 2025 16:36:15 -0500 Subject: [PATCH 02/74] Updated handling of subscenes in assets to be it's own distinct definition to avoid parsing and detection issues, as well as fields to be handled distinctly between the types --- Engine/source/T3D/SubScene.cpp | 32 ++-- Engine/source/T3D/SubScene.h | 12 +- Engine/source/T3D/assets/LevelAsset.cpp | 15 +- Engine/source/T3D/assets/LevelAsset.h | 3 - Engine/source/T3D/assets/SubSceneAsset.cpp | 166 ++++++++++++++++++ Engine/source/T3D/assets/SubSceneAsset.h | 113 ++++++++++++ .../game/tools/assetBrowser/main.tscript | 1 + .../scripts/assetTypes/level.tscript | 18 +- .../scripts/assetTypes/subScene.tscript | 156 ++++++++++++++++ 9 files changed, 469 insertions(+), 47 deletions(-) create mode 100644 Engine/source/T3D/assets/SubSceneAsset.cpp create mode 100644 Engine/source/T3D/assets/SubSceneAsset.h create mode 100644 Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/subScene.tscript diff --git a/Engine/source/T3D/SubScene.cpp b/Engine/source/T3D/SubScene.cpp index ecab32ff0..2b773bfd6 100644 --- a/Engine/source/T3D/SubScene.cpp +++ b/Engine/source/T3D/SubScene.cpp @@ -24,7 +24,7 @@ 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()), + mSubSceneAssetId(StringTable->EmptyString()), mGameModesNames(StringTable->EmptyString()), mScopeDistance(-1), mLoaded(false), @@ -67,7 +67,7 @@ void SubScene::initPersistFields() { addGroup("SubScene"); addField("isGlobalLayer", TypeBool, Offset(mGlobalLayer, SubScene), ""); - INITPERSISTFIELD_LEVELASSET(Level, SubScene, "The level asset to load."); + INITPERSISTFIELD_SUBSCENEASSET(SubScene, SubScene, "The subscene asset to load."); addField("tickPeriodMS", TypeS32, Offset(mTickPeriodMS, SubScene), "evaluation rate (ms)"); addField("gameModes", TypeGameModeList, Offset(mGameModesNames, SubScene), "The game modes that this subscene is associated with."); endGroup("SubScene"); @@ -265,13 +265,13 @@ void SubScene::processTick(const Move* move) void SubScene::_onFileChanged(const Torque::Path& path) { - if(mLevelAsset.isNull() || Torque::Path(mLevelAsset->getLevelPath()) != path) + if(mSubSceneAsset.isNull() || Torque::Path(mSubSceneAsset->getLevelPath()) != path) return; if (mSaving) return; - AssertFatal(path == mLevelAsset->getLevelPath(), "Prefab::_onFileChanged - path does not match filename."); + AssertFatal(path == mSubSceneAsset->getLevelPath(), "SubScene::_onFileChanged - path does not match filename."); _closeFile(false); _loadFile(false); @@ -307,9 +307,9 @@ void SubScene::_closeFile(bool removeFileNotify) _removeContents(SimGroupIterator(this)); - if (removeFileNotify && mLevelAsset.notNull() && mLevelAsset->getLevelPath() != StringTable->EmptyString()) + if (removeFileNotify && mSubSceneAsset.notNull() && mSubSceneAsset->getLevelPath() != StringTable->EmptyString()) { - Torque::FS::RemoveChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged); + Torque::FS::RemoveChangeNotification(mSubSceneAsset->getLevelPath(), this, &SubScene::_onFileChanged); } mGameModesList.clear(); @@ -319,18 +319,18 @@ 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()) + if(mSubSceneAsset.isNull() || mSubSceneAsset->getLevelPath() == StringTable->EmptyString()) return; - String evalCmd = String::ToString("exec(\"%s\");", mLevelAsset->getLevelPath()); + String evalCmd = String::ToString("exec(\"%s\");", mSubSceneAsset->getLevelPath()); String instantGroup = Con::getVariable("InstantGroup"); Con::setIntVariable("InstantGroup", this->getId()); - Con::evaluate((const char*)evalCmd.c_str(), false, mLevelAsset->getLevelPath()); + Con::evaluate((const char*)evalCmd.c_str(), false, mSubSceneAsset->getLevelPath()); Con::setVariable("InstantGroup", instantGroup.c_str()); if (addFileNotify) - Torque::FS::AddChangeNotification(mLevelAsset->getLevelPath(), this, &SubScene::_onFileChanged); + Torque::FS::AddChangeNotification(mSubSceneAsset->getLevelPath(), this, &SubScene::_onFileChanged); } void SubScene::load() @@ -432,7 +432,7 @@ bool SubScene::save() if (size() == 0 || !isLoaded()) return false; - if (mLevelAsset.isNull()) + if (mSubSceneAsset.isNull()) return false; if (mSaving) @@ -446,7 +446,7 @@ bool SubScene::save() PersistenceManager prMger; - StringTableEntry levelPath = mLevelAsset->getLevelPath(); + StringTableEntry levelPath = mSubSceneAsset->getLevelPath(); FileStream fs; fs.open(levelPath, Torque::FS::File::Write); @@ -460,7 +460,7 @@ bool SubScene::save() { if ((*itr)->isMethod("onSaving")) { - Con::executef((*itr), "onSaving", mLevelAssetId); + Con::executef((*itr), "onSaving", mSubSceneAssetId); } if (childObj->getGroup() == this) @@ -481,14 +481,14 @@ bool SubScene::save() bool saveSuccess = false; //Get the level asset - if (mLevelAsset.isNull()) + if (mSubSceneAsset.isNull()) return saveSuccess; //update the gamemode list as well - mLevelAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames)); + mSubSceneAsset->setDataField(StringTable->insert("gameModesNames"), NULL, StringTable->insert(mGameModesNames)); //Finally, save - saveSuccess = mLevelAsset->saveAsset(); + saveSuccess = mSubSceneAsset->saveAsset(); mSaving = false; diff --git a/Engine/source/T3D/SubScene.h b/Engine/source/T3D/SubScene.h index e73133057..cfc3606ad 100644 --- a/Engine/source/T3D/SubScene.h +++ b/Engine/source/T3D/SubScene.h @@ -5,8 +5,8 @@ #ifndef SCENE_GROUP_H #include "SceneGroup.h" #endif -#ifndef LEVEL_ASSET_H -#include "assets/LevelAsset.h" +#ifndef SUBSCENE_ASSET_H +#include "assets/SubSceneAsset.h" #endif class GameMode; @@ -21,13 +21,13 @@ public: NextFreeMask = Parent::NextFreeMask << 0 }; - void onLevelChanged() {} + void onSubSceneChanged() {} protected: static bool smTransformChildren; private: - DECLARE_LEVELASSET(SubScene, Level, onLevelChanged); + DECLARE_SUBSCENEASSET(SubScene, SubScene, onSubSceneChanged); StringTableEntry mGameModesNames; Vector mGameModesList; @@ -61,7 +61,7 @@ public: static void initPersistFields(); static void consoleInit(); - StringTableEntry getTypeHint() const override { return (getLevelAsset()) ? getLevelAsset()->getAssetName() : StringTable->EmptyString(); } + StringTableEntry getTypeHint() const override { return (getSubSceneAsset()) ? getSubSceneAsset()->getAssetName() : StringTable->EmptyString(); } // SimObject bool onAdd() override; @@ -122,6 +122,6 @@ public: DECLARE_CALLBACK(void, onLoaded, ()); DECLARE_CALLBACK(void, onUnloaded, ()); - DECLARE_ASSET_SETGET(SubScene, Level); + DECLARE_ASSET_SETGET(SubScene, SubScene); }; #endif diff --git a/Engine/source/T3D/assets/LevelAsset.cpp b/Engine/source/T3D/assets/LevelAsset.cpp index e47a6f563..f04bfe058 100644 --- a/Engine/source/T3D/assets/LevelAsset.cpp +++ b/Engine/source/T3D/assets/LevelAsset.cpp @@ -101,7 +101,7 @@ ConsoleSetType(TypeLevelAssetId) } //----------------------------------------------------------------------------- -LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false) +LevelAsset::LevelAsset() : AssetBase() { mLevelName = StringTable->EmptyString(); mLevelFile = StringTable->EmptyString(); @@ -117,7 +117,6 @@ LevelAsset::LevelAsset() : AssetBase(), mIsSubLevel(false) mNavmeshPath = StringTable->EmptyString(); mGameModesNames = StringTable->EmptyString(); - mMainLevelAsset = StringTable->EmptyString(); mEditorFile = StringTable->EmptyString(); mBakedSceneFile = StringTable->EmptyString(); @@ -158,7 +157,6 @@ void LevelAsset::initPersistFields() addProtectedField("BakedSceneFile", TypeAssetLooseFilePath, Offset(mBakedSceneFile, LevelAsset), &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("gameModesNames", TypeString, Offset(mGameModesNames, LevelAsset), "Name of the Game Mode to be used with this level"); } @@ -480,15 +478,8 @@ GuiControl* GuiInspectorTypeLevelAssetPtr::constructEditControl() // Create "Open in Editor" button mEditButton = new GuiBitmapButtonCtrl(); - String setSubSceneValue = "$createLevelAssetIsSubScene = \"\";"; - if(dynamic_cast(mInspector->getInspectObject()) != NULL) - { - setSubSceneValue = "$createLevelAssetIsSubScene = true;"; - } - - dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; %s AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");", - getIdString(), - setSubSceneValue.c_str()); + dSprintf(szBuffer, sizeof(szBuffer), "$createAndAssignField = %s; AssetBrowser.setupCreateNewAsset(\"LevelAsset\", AssetBrowser.selectedModule, \"createAndAssignLevelAsset\");", + getIdString()); mEditButton->setField("Command", szBuffer); char bitmapName[512] = "ToolsModule:iconAdd_image"; diff --git a/Engine/source/T3D/assets/LevelAsset.h b/Engine/source/T3D/assets/LevelAsset.h index 8b02495ed..39339a41e 100644 --- a/Engine/source/T3D/assets/LevelAsset.h +++ b/Engine/source/T3D/assets/LevelAsset.h @@ -66,9 +66,6 @@ class LevelAsset : public AssetBase StringTableEntry mEditorFile; StringTableEntry mBakedSceneFile; - bool mIsSubLevel; - StringTableEntry mMainLevelAsset; - StringTableEntry mGameModesNames; Vector mAssetDependencies; diff --git a/Engine/source/T3D/assets/SubSceneAsset.cpp b/Engine/source/T3D/assets/SubSceneAsset.cpp new file mode 100644 index 000000000..a55af9cad --- /dev/null +++ b/Engine/source/T3D/assets/SubSceneAsset.cpp @@ -0,0 +1,166 @@ +#include "SubSceneAsset.h" + +#include "T3D/SubScene.h" +IMPLEMENT_CONOBJECT(SubSceneAsset); + + +ConsoleType(SubSceneAssetPtr, TypeSubSceneAssetPtr, const char*, "") + +//----------------------------------------------------------------------------- + +ConsoleGetType(TypeSubSceneAssetPtr) +{ + // Fetch asset Id. + return *((const char**)(dptr)); +} + +//----------------------------------------------------------------------------- + +ConsoleSetType(TypeSubSceneAssetPtr) +{ + // Was a single argument specified? + if (argc == 1) + { + // Yes, so fetch field value. + *((const char**)dptr) = StringTable->insert(argv[0]); + + return; + } + + // Warn. + Con::warnf("(TypeSubSceneAssetPtr) - Cannot set multiple args to a single asset."); +} + +//----------------------------------------------------------------------------- +ConsoleType(assetIdString, TypeSubSceneAssetId, const char*, "") + +ConsoleGetType(TypeSubSceneAssetId) +{ + // Fetch asset Id. + return *((const char**)(dptr)); +} + +ConsoleSetType(TypeSubSceneAssetId) +{ + // Was a single argument specified? + if (argc == 1) + { + *((const char**)dptr) = StringTable->insert(argv[0]); + + return; + } + + // Warn. + Con::warnf("(TypeSubSceneAssetId) - Cannot set multiple args to a single asset."); +} + +SubSceneAsset::SubSceneAsset() : LevelAsset() +{ +} + +SubSceneAsset::~SubSceneAsset() +{ +} + +void SubSceneAsset::initPersistFields() +{ + docsURL; + Parent::initPersistFields(); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeAssetId +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiInspectorTypeSubSceneAssetPtr); + +ConsoleDocClass(GuiInspectorTypeSubSceneAssetPtr, + "@brief Inspector field type for Shapes\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSubSceneAssetPtr::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeSubSceneAssetPtr)->setInspectorFieldType("GuiInspectorTypeSubSceneAssetPtr"); +} + +GuiControl* GuiInspectorTypeSubSceneAssetPtr::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(\"SubSceneAsset\", \"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(\"SubSceneAsset\", AssetBrowser.selectedModule);", + 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 GuiInspectorTypeSubSceneAssetPtr::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(GuiInspectorTypeSubSceneAssetId); + +ConsoleDocClass(GuiInspectorTypeSubSceneAssetId, + "@brief Inspector field type for SubScene\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSubSceneAssetId::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeSubSceneAssetId)->setInspectorFieldType("GuiInspectorTypeSubSceneAssetId"); +} diff --git a/Engine/source/T3D/assets/SubSceneAsset.h b/Engine/source/T3D/assets/SubSceneAsset.h new file mode 100644 index 000000000..444c4c23a --- /dev/null +++ b/Engine/source/T3D/assets/SubSceneAsset.h @@ -0,0 +1,113 @@ +#pragma once +#ifndef SUBSCENE_ASSET_H +#define SUBSCENE_ASSET_H + +#ifndef LEVEL_ASSET_H +#include "LevelAsset.h" +#endif + +#ifndef _ASSET_DEFINITION_H_ +#include "assets/assetDefinition.h" +#endif + +#ifndef _GUI_INSPECTOR_TYPES_H_ +#include "gui/editor/guiInspectorTypes.h" +#endif + +class SubSceneAsset : public LevelAsset +{ + typedef LevelAsset Parent; + +public: + SubSceneAsset(); + virtual ~SubSceneAsset(); + + /// Engine. + static void initPersistFields(); + + /// Declare Console Object. + DECLARE_CONOBJECT(SubSceneAsset); +}; + +#ifdef TORQUE_TOOLS +class GuiInspectorTypeSubSceneAssetPtr : public GuiInspectorTypeFileName +{ + typedef GuiInspectorTypeFileName Parent; +public: + + GuiBitmapButtonCtrl* mEditButton; + + DECLARE_CONOBJECT(GuiInspectorTypeSubSceneAssetPtr); + static void consoleInit(); + + GuiControl* constructEditControl() override; + bool updateRects() override; +}; + +class GuiInspectorTypeSubSceneAssetId : public GuiInspectorTypeSubSceneAssetPtr +{ + typedef GuiInspectorTypeSubSceneAssetPtr Parent; +public: + + DECLARE_CONOBJECT(GuiInspectorTypeSubSceneAssetId); + static void consoleInit(); +}; +#endif + +DefineConsoleType(TypeSubSceneAssetPtr, SubSceneAsset) +DefineConsoleType(TypeSubSceneAssetId, String) + +#pragma region Singular Asset Macros + +//Singular assets +/// +/// Declares an SubScene asset +/// This establishes the assetId, asset and legacy filepath fields, along with supplemental getter and setter functions +/// +#define DECLARE_SUBSCENEASSET(className, name, changeFunc) public: \ + StringTableEntry m##name##AssetId;\ + AssetPtr m##name##Asset;\ +public: \ + const AssetPtr & get##name##Asset() const { return m##name##Asset; }\ + void set##name##Asset(const AssetPtr &_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_SUBSCENEASSET(name, consoleClass, docs) \ + addProtectedField(assetText(name, Asset), TypeSubSceneAssetId, Offset(m##name##AssetId, consoleClass), _set##name##Data, &defaultProtectedGetFn, assetDoc(name, asset docs.)); + +#pragma endregion + +#endif // SUBSCENE_ASSET_H diff --git a/Templates/BaseGame/game/tools/assetBrowser/main.tscript b/Templates/BaseGame/game/tools/assetBrowser/main.tscript index 73d2a314a..2b3e0938c 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/main.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/main.tscript @@ -75,6 +75,7 @@ function initializeAssetBrowser() exec("./scripts/assetTypes/gui." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/image." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/level." @ $TorqueScriptFileExtension); + exec("./scripts/assetTypes/subScene." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/material." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/postFX." @ $TorqueScriptFileExtension); exec("./scripts/assetTypes/script." @ $TorqueScriptFileExtension); diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/level.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/level.tscript index d0e673628..015a798ae 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/level.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/level.tscript @@ -3,17 +3,9 @@ AssetBrowser::registerAssetType("LevelAsset", "Levels"); function LevelAsset::setupCreateNew() { NewAssetPropertiesInspector.startGroup("Level"); - NewAssetPropertiesInspector.addField("LevelName", "Level Name", "String", "Human-readable name of new level", "", "", $CurrentAssetBrowser.callAssetTypeFunc(%assetType, "setupCreateNew");.newAssetSettings); + NewAssetPropertiesInspector.addField("LevelName", "Level Name", "String", "Human-readable name of new level", "", "", $CurrentAssetBrowser.newAssetSettings); NewAssetPropertiesInspector.addField("levelPreviewImage", "Level Preview Image", "Image", "Preview Image for the level", "", "", $CurrentAssetBrowser.newAssetSettings); - //If we forcefully set it elsewhere, adhere - if($createLevelAssetIsSubScene == true) - %this.newAssetSettings.isSubScene = true; - else - %this.newAssetSettings.isSubScene = false; - - NewAssetPropertiesInspector.addField("isSubScene", "Is SubScene", "bool", "Is this levelAsset intended as a subScene", %this.newAssetSettings.isSubScene, "", %this.newAssetSettings); - NewAssetPropertiesInspector.endGroup(); } @@ -88,11 +80,17 @@ function LevelAsset::onCreateNew(%this) if(!%addSuccess) { - error("AssetBrowser::createLevelAsset() - failed to add declared asset: " @ %tamlpath @ " for module: " @ %moduleDef); + error("LevelAsset::onCreateNew() - failed to add declared asset: " @ %tamlpath @ " for module: " @ %moduleDef); } $CurrentAssetBrowser.refresh(); + if(%addSuccess && isObject($createAndAssignField)) + { + $createAndAssignField.apply(%moduleName @ ":" @ %assetName); + $createAndAssignField = ""; + } + return %tamlpath; } diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/subScene.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/subScene.tscript new file mode 100644 index 000000000..bd72e7080 --- /dev/null +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/subScene.tscript @@ -0,0 +1,156 @@ +AssetBrowser::registerAssetType("SubSceneAsset", "SubScenes"); + +function SubSceneAsset::setupCreateNew() +{ + NewAssetPropertiesInspector.startGroup("SubScene"); + NewAssetPropertiesInspector.addField("LevelName", "SubScene Name", "String", "Human-readable name of new subScene", "", "", $CurrentAssetBrowser.newAssetSettings); + + NewAssetPropertiesInspector.endGroup(); +} + +function SubSceneAsset::onCreateNew(%this) +{ + %moduleName = $CurrentAssetBrowser.newAssetSettings.moduleName; + %modulePath = "data/" @ %moduleName; + + %assetName = $CurrentAssetBrowser.newAssetSettings.assetName; + + %assetPath = NewAssetTargetAddress.getText() @ "/"; + + %misExtension = ".subMis"; + + %tamlpath = %assetPath @ %assetName @ ".asset.taml"; + %levelPath = %assetPath @ %assetName @ %misExtension; + + %asset = new SubSceneAsset() + { + AssetName = %assetName; + versionId = 1; + LevelFile = %assetName @ %misExtension; + LevelName = $CurrentAssetBrowser.newAssetSettings.levelName; + AssetDescription = $CurrentAssetBrowser.newAssetSettings.description; + }; + + TamlWrite(%asset, %tamlpath); + + %fileObj = new FileObject(); + if (!%fileObj.openForWrite(%levelPath)) + { + echo("Unable to write subScene file!"); + } + else + { + %fileObj.writeLine(""); + %fileObj.close(); + %fileObj.delete(); + } + + %moduleDef = ModuleDatabase.findModule(%moduleName, 1); + %addSuccess = AssetDatabase.addDeclaredAsset(%moduleDef, %tamlpath); + + if(!%addSuccess) + { + error("SubSceneAsset::onCreateNew() - failed to add declared asset: " @ %tamlpath @ " for module: " @ %moduleDef); + } + + $CurrentAssetBrowser.refresh(); + + if(%addSuccess && isObject($createAndAssignField)) + { + $createAndAssignField.apply(%moduleName @ ":" @ %assetName); + $createAndAssignField = ""; + } + + return %tamlpath; +} + +function SubSceneAsset::buildBrowserElement(%this, %previewData) +{ + %previewData.assetName = %this.assetName; + %previewData.assetPath = %this.getLevelPath(); + %previewData.doubleClickCommand = "AssetBrowser.selectAsset(" @ %this @ ");"; + + %levelPreviewImage = %this.PreviewImage; + + if(isFile(%levelPreviewImage)) + %previewData.previewImage = %levelPreviewImage; + else + %previewData.previewImage = "ToolsModule:levelIcon_image"; + + %previewData.assetFriendlyName = %this.assetName; + %previewData.assetDesc = %this.description; + %previewData.tooltip = "Asset Name: " @ %this.assetName @ "\n" @ + "Asset Type: SubScene Asset\n" @ + "Asset Definition ID: " @ %this @ "\n" @ + "SubScene File path: " @ %this.getLevelPath(); +} + +function EWorldEditor::createSelectedAsSubScene( %this, %object ) +{ + //create new level asset here + AssetBrowser.setupCreateNewAsset("SubSceneAsset", 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 SubSceneAsset::onWorldEditorDropped(%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 ); +} \ No newline at end of file From 0bf38aacf3e99761ec7850eb5748031900ca2cde Mon Sep 17 00:00:00 2001 From: JeffR Date: Tue, 1 Apr 2025 02:10:13 -0500 Subject: [PATCH 03/74] Made the Asset Properties inspector ctrl be a regular GuiInspector control Standardized the image asset action menu and editing callbacks while leaving it open for modification/expansion later as needed --- .../tools/assetBrowser/guis/editAsset.gui | 2 +- .../scripts/assetTypes/image.tscript | 42 ++++++------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/Templates/BaseGame/game/tools/assetBrowser/guis/editAsset.gui b/Templates/BaseGame/game/tools/assetBrowser/guis/editAsset.gui index 6d30fea61..9ac2e2ef2 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/guis/editAsset.gui +++ b/Templates/BaseGame/game/tools/assetBrowser/guis/editAsset.gui @@ -75,7 +75,7 @@ $guiContent = new GuiControl(AssetBrowser_editAsset) { canSave = "1"; canSaveDynamicFields = "0"; - new GuiVariableInspector(AssetEditInspector) { + new GuiInspector(AssetEditInspector) { dividerMargin = "5"; showCustomFields = "1"; stackingType = "Vertical"; diff --git a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript index 673652b70..98af1c509 100644 --- a/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript +++ b/Templates/BaseGame/game/tools/assetBrowser/scripts/assetTypes/image.tscript @@ -95,38 +95,20 @@ function ImageAsset::generatePreviewImage(%this, %previewButton, %forceRegenerat function ImageAsset::onShowActionMenu(%this) { - if( !isObject( EditImageAssetTypePopup ) ) - { - new PopupMenu( EditImageAssetTypePopup ) - { - superClass = "MenuBuilder"; - class = "EditorWorldMenu"; - - jumpFileName = ""; - jumpLineNumber = ""; - }; - } - - EditImageAssetTypePopup.objectData = %this; - EditImageAssetTypePopup.objectType = "ImageAsset"; - - //Regen the menu so we're fully up and current with options and references - EditImageAssetTypePopup.clearItems(); - - EditImageAssetTypePopup.item[ 0 ] = "Rename Folder" TAB "" TAB $CurrentAssetBrowser @ ".renameAsset();"; - EditImageAssetTypePopup.item[ 1 ] = "Duplicate Folder" TAB "" TAB $CurrentAssetBrowser @ ".duplicateAsset();"; - EditImageAssetTypePopup.item[ 2 ] = "-"; - EditImageAssetTypePopup.item[ 3 ] = "Open File Location" TAB "" TAB $CurrentAssetBrowser @ ".openFolderLocation(" @ filePath(%this.getFilename()) @ ");"; - EditImageAssetTypePopup.item[ 4 ] = "-"; - EditImageAssetTypePopup.item[ 5 ] = "Delete Folder" TAB "" TAB $CurrentAssetBrowser @ ".deleteAsset();"; - - EditImageAssetTypePopup.reloadItems(); - - EditImageAssetTypePopup.showPopup(Canvas); - - $CurrentAssetBrowser.popupMenu = EditImageAssetTypePopup; + GenericAsset::onShowActionMenu(%this); } +function ImageAsset::onEditProperties(%this) +{ + GenericAsset::onEditProperties(%this); +} + +//Called when the AssetType has it's properties saved from the onEditProperties process +function ImageAsset::onSaveProperties(%this) +{ + GenericAsset::onSaveProperties(%this); +} + function GuiInspectorTypeImageAssetPtr::onControlDropped( %this, %payload, %position ) { Canvas.popDialog(EditorDragAndDropLayer); From 8ef5760b5db937b3c09d31de672374bb9ed340ad Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 5 Apr 2025 16:38:27 -0500 Subject: [PATCH 04/74] fix make a mesh sometimes shifting position in level leverage the fact that when we create the backup prefab, that compresses down to one selected object from there we can get it's position and use that for the bake location --- .../scripts/menuHandlers.ed.tscript | 39 +++---------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.tscript index cc7eeaa62..f5cfe67ca 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.tscript @@ -721,32 +721,7 @@ function EditorExplodePrefab() } function makeSelectedAMesh(%assetId) -{ - - /*%dlg = new SaveFileDialog() - { - Filters = "Collada file (*.dae)|*.dae|"; - DefaultPath = $Pref::WorldEditor::LastPath; - DefaultFile = ""; - ChangePath = false; - OverwritePrompt = true; - }; - - %ret = %dlg.Execute(); - if ( %ret ) - { - $Pref::WorldEditor::LastPath = filePath( %dlg.FileName ); - %saveFile = %dlg.FileName; - } - - if( fileExt( %saveFile ) !$= ".dae" ) - %saveFile = %saveFile @ ".dae"; - - %dlg.delete(); - - if ( !%ret ) - return;*/ - +{ %assetDef = AssetDatabase.acquireAsset(%assetId); %assetPath = AssetDatabase.getAssetPath(%assetId); @@ -762,16 +737,12 @@ function makeSelectedAMesh(%assetId) AssetDatabase.refreshAsset(%assetId); if(%success) - { - //ok, cool it worked, so clear out the old - //First, get our center of the currently selected objects - %selectionCenter = EWorldEditor.getSelectionCentroid(); - + { //Next, for safety purposes(and convenience!) we'll make them a prefab aping off the filepath/name provided //TODO: Make this an editor option %prefabPath = %assetPath @ "/" @ %assetDef.AssetName @ ".prefab"; - EWorldEditor.makeSelectionPrefab(%prefabPath, true); - + EWorldEditor.makeSelectionPrefab(%prefabPath, false); + %selectionPos = EWorldEditor.getSelectedObject(0).getPosition(); //Next, nuke 'em EditorMenuEditDelete(); @@ -779,7 +750,7 @@ function makeSelectedAMesh(%assetId) %newStatic = new TSStatic() { shapeAsset = %assetId; - position = %selectionCenter; + position = %selectionPos; }; getRootScene().add(%newStatic); From 5cf54580e62ab112de3b80fedff924decfa89654 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 6 Apr 2025 16:09:14 -0500 Subject: [PATCH 05/74] Generic Damagemodel included in root/data/ as a module so theres a generic folks can riff off of additionally, includes a physcs based damage option inspector exposed augment for velicity based collisions, as well as utilities for applying damage to the object a given thing is mounted to further. also fixes a lack of vehicles being able to use thier mvTriggerCount4 and 5 for the additional 2 mountpoints allowed --- Engine/source/T3D/shapeBase.cpp | 8 + Engine/source/T3D/shapeBase.h | 2 + Engine/source/T3D/vehicles/vehicle.cpp | 2 + .../game/data/DamageModel/DamageModel.module | 11 + .../game/data/DamageModel/DamageModel.tscript | 47 ++ .../guis/damageGuiOverlay.asset.taml | 1 + .../DamageModel/guis/damageGuiOverlay.gui | 285 ++++++++ .../data/DamageModel/images/crosshair.png | Bin 0 -> 144 bytes .../DamageModel/images/crosshair_blue.png | Bin 0 -> 134 bytes .../images/crosshair_blue_image.asset.taml | 3 + .../data/DamageModel/images/damageBottom.png | Bin 0 -> 1898 bytes .../images/damageBottom_image.asset.taml | 3 + .../data/DamageModel/images/damageFront.png | Bin 0 -> 1914 bytes .../images/damageFront_image.asset.taml | 3 + .../data/DamageModel/images/damageLeft.png | Bin 0 -> 1888 bytes .../images/damageLeft_image.asset.taml | 3 + .../data/DamageModel/images/damageRight.png | Bin 0 -> 1837 bytes .../images/damageRight_image.asset.taml | 3 + .../data/DamageModel/images/damageTop.png | Bin 0 -> 1914 bytes .../images/damageTop_image.asset.taml | 3 + .../scripts/client/playGui.tscript | 53 ++ .../DamageModel/scripts/server/player.tscript | 42 ++ .../scripts/server/projectile.tscript | 46 ++ .../scripts/server/radiusDamage.tscript | 73 ++ .../scripts/server/shapeBase.tscript | 285 ++++++++ .../scripts/server/utility.tscript | 34 + .../DamageModel/scripts/server/weapon.tscript | 645 ++++++++++++++++++ 27 files changed, 1552 insertions(+) create mode 100644 Templates/BaseGame/game/data/DamageModel/DamageModel.module create mode 100644 Templates/BaseGame/game/data/DamageModel/DamageModel.tscript create mode 100644 Templates/BaseGame/game/data/DamageModel/guis/damageGuiOverlay.asset.taml create mode 100644 Templates/BaseGame/game/data/DamageModel/guis/damageGuiOverlay.gui create mode 100644 Templates/BaseGame/game/data/DamageModel/images/crosshair.png create mode 100644 Templates/BaseGame/game/data/DamageModel/images/crosshair_blue.png create mode 100644 Templates/BaseGame/game/data/DamageModel/images/crosshair_blue_image.asset.taml create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageBottom.png create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageBottom_image.asset.taml create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageFront.png create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageFront_image.asset.taml create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageLeft.png create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageLeft_image.asset.taml create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageRight.png create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageRight_image.asset.taml create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageTop.png create mode 100644 Templates/BaseGame/game/data/DamageModel/images/damageTop_image.asset.taml create mode 100644 Templates/BaseGame/game/data/DamageModel/scripts/client/playGui.tscript create mode 100644 Templates/BaseGame/game/data/DamageModel/scripts/server/player.tscript create mode 100644 Templates/BaseGame/game/data/DamageModel/scripts/server/projectile.tscript create mode 100644 Templates/BaseGame/game/data/DamageModel/scripts/server/radiusDamage.tscript create mode 100644 Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript create mode 100644 Templates/BaseGame/game/data/DamageModel/scripts/server/utility.tscript create mode 100644 Templates/BaseGame/game/data/DamageModel/scripts/server/weapon.tscript diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 968e8fb9b..7f2389bd8 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -172,6 +172,8 @@ ShapeBaseData::ShapeBaseData() density( 1.0f ), maxEnergy( 0.0f ), maxDamage( 1.0f ), + mCollisionMul(0.0f), + mImpactMul(0.0f), repairRate( 0.0033f ), disabledLevel( 1.0f ), destroyedLevel( 1.0f ), @@ -229,6 +231,8 @@ ShapeBaseData::ShapeBaseData(const ShapeBaseData& other, bool temp_clone) : Game density = other.density; maxEnergy = other.maxEnergy; maxDamage = other.maxDamage; + mCollisionMul = other.mCollisionMul; + mImpactMul = other.mImpactMul; repairRate = other.repairRate; disabledLevel = other.disabledLevel; destroyedLevel = other.destroyedLevel; @@ -585,6 +589,10 @@ void ShapeBaseData::initPersistFields() addField( "isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData), "Invincible flag; when invincible, the object cannot be damaged or " "repaired." ); + addFieldV("collisionMul", TypeRangedF32, Offset(mCollisionMul, ShapeBaseData), &CommonValidators::PositiveFloat, + "collision damage multiplier"); + addFieldV("impactMul", TypeRangedF32, Offset(mImpactMul, ShapeBaseData), &CommonValidators::PositiveFloat, + "impact damage multiplier"); endGroup( "Damage/Energy" ); addGroup( "Camera", "The settings used by the shape when it is the camera." ); diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 706f54b72..594b709bd 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -579,6 +579,8 @@ public: F32 density; F32 maxEnergy; F32 maxDamage; + F32 mCollisionMul; + F32 mImpactMul; F32 repairRate; ///< Rate per tick. F32 disabledLevel; diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index cfbd9a876..a0ba43208 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -726,6 +726,8 @@ void Vehicle::updateMove(const Move* move) if (mDamageState == Enabled) { setImageTriggerState(0,move->trigger[0]); setImageTriggerState(1,move->trigger[1]); + setImageTriggerState(2, move->trigger[4]); + setImageTriggerState(3, move->trigger[5]); } // Throttle diff --git a/Templates/BaseGame/game/data/DamageModel/DamageModel.module b/Templates/BaseGame/game/data/DamageModel/DamageModel.module new file mode 100644 index 000000000..0bce1ed70 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/DamageModel.module @@ -0,0 +1,11 @@ + + + diff --git a/Templates/BaseGame/game/data/DamageModel/DamageModel.tscript b/Templates/BaseGame/game/data/DamageModel/DamageModel.tscript new file mode 100644 index 000000000..7e29f7d9e --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/DamageModel.tscript @@ -0,0 +1,47 @@ +function DamageModel::onCreate(%this) +{ +} + +function DamageModel::onDestroy(%this) +{ +} + +//This is called when the server is initially set up by the game application +function DamageModel::initServer(%this) +{ +} + +//This is called when the server is created for an actual game/map to be played +function DamageModel::onCreateGameServer(%this) +{ + %this.registerDatablock("./scripts/managedData/managedParticleData"); + %this.registerDatablock("./scripts/managedData/managedParticleEmitterData"); + %this.queueExec("./scripts/server/utility"); + %this.queueExec("./scripts/server/radiusDamage"); + %this.queueExec("./scripts/server/projectile"); + %this.queueExec("./scripts/server/weapon"); + %this.queueExec("./scripts/server/shapeBase"); + %this.queueExec("./scripts/server/player"); +} + +//This is called when the server is shut down due to the game/map being exited +function DamageModel::onDestroyGameServer(%this) +{ +} + +//This is called when the client is initially set up by the game application +function DamageModel::initClient(%this) +{ + %this.queueExec("./guis/damageGuiOverlay.gui"); + %this.queueExec("./scripts/client/playGui"); +} + +//This is called when a client connects to a server +function DamageModel::onCreateClientConnection(%this) +{ +} + +//This is called when a client disconnects from a server +function DamageModel::onDestroyClientConnection(%this) +{ +} diff --git a/Templates/BaseGame/game/data/DamageModel/guis/damageGuiOverlay.asset.taml b/Templates/BaseGame/game/data/DamageModel/guis/damageGuiOverlay.asset.taml new file mode 100644 index 000000000..ab0935549 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/guis/damageGuiOverlay.asset.taml @@ -0,0 +1 @@ + diff --git a/Templates/BaseGame/game/data/DamageModel/guis/damageGuiOverlay.gui b/Templates/BaseGame/game/data/DamageModel/guis/damageGuiOverlay.gui new file mode 100644 index 000000000..2c8ee1fe6 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/guis/damageGuiOverlay.gui @@ -0,0 +1,285 @@ +//--- OBJECT WRITE BEGIN --- +$guiContent = new GuiContainer(DamageGuiOverlay) { + isContainer = "1"; + Profile = "GuiContentProfile"; + HorizSizing = "relative"; + VertSizing = "relative"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + Enabled = "1"; + helpTag = "0"; + noCursor = "1"; + new GuiShapeNameHud() { + fillColor = "0 0 0 0.25"; + frameColor = "0 1 0 1"; + textColor = "0 1 0 1"; + showFill = "0"; + showFrame = "0"; + verticalOffset = "0.2"; + distanceFade = "0.1"; + isContainer = "0"; + Profile = "GuiModelessDialogProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiCrossHairHud(Reticle) { + damageFillColor = "0 1 0 1"; + damageFrameColor = "1 0.6 0 1"; + damageRect = "50 4"; + damageOffset = "0 10"; + bitmapAsset = "FPSEquipment:blank_image"; + wrap = "0"; + isContainer = "0"; + Profile = "GuiModelessDialogProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "496 368"; + Extent = "32 32"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiCrossHairHud(ZoomReticle) { + damageFillColor = "0 1 0 1"; + damageFrameColor = "1 0.6 0 1"; + damageRect = "50 4"; + damageOffset = "0 10"; + bitmapAsset = "DamageModel:bino_image"; + wrap = "0"; + isContainer = "0"; + Profile = "GuiModelessDialogProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapBorderCtrl(WeaponHUD) { + isContainer = "0"; + Profile = "ChatHudBorderProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "78 693"; + Extent = "124 72"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "UI:hudfill_image"; + wrap = "0"; + isContainer = "0"; + Profile = "GuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "8 8"; + Extent = "108 56"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl(PreviewImage) { + bitmapAsset = "UI:hudfill_image"; + wrap = "0"; + isContainer = "0"; + Profile = "GuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "8 8"; + Extent = "108 56"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(AmmoAmount) { + maxLength = "255"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "HudTextItalicProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "40 8"; + Extent = "120 16"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiHealthTextHud() { + fillColor = "0 0 0 0.65"; + frameColor = "0 0 0 1"; + textColor = "1 1 1 1"; + warningColor = "1 0 0 1"; + showFill = "1"; + showFrame = "1"; + showTrueValue = "0"; + showEnergy = "0"; + warnThreshold = "25"; + pulseThreshold = "15"; + pulseRate = "750"; + position = "5 693"; + extent = "72 72"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiBigTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl(DamageHUD) { + position = "384 256"; + extent = "256 256"; + minExtent = "8 2"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl(DamageFront) { + bitmapAsset = "DamageModel:damageFront_image"; + wrap = "0"; + position = "0 0"; + extent = "256 32"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "Damage[Front]"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl(DamageTop) { + bitmapAsset = "DamageModel:damageTop_image"; + wrap = "0"; + position = "0 0"; + extent = "256 32"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "Damage[Top]"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl(DamageBottom) { + bitmapAsset = "DamageModel:damageBottom_image"; + wrap = "0"; + position = "0 224"; + extent = "256 32"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "Damage[Bottom]"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl(DamageLeft) { + bitmapAsset = "DamageModel:damageLeft_image"; + wrap = "0"; + position = "0 0"; + extent = "32 256"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "Damage[Left]"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl(DamageRight) { + bitmapAsset = "DamageModel:damageRight_image"; + wrap = "0"; + position = "224 0"; + extent = "32 256"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "Damage[Right]"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/DamageModel/images/crosshair.png b/Templates/BaseGame/game/data/DamageModel/images/crosshair.png new file mode 100644 index 0000000000000000000000000000000000000000..06bcf5c6ca7dcd8481c5660c7fc875dd303464e9 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1|*LJg}1B{;D1osdy8@szBYwCIG4*or`-fKIy=Ke83- pnwCvmC@_EH`j4KABv=I!7}iW+?Rl-@XAjiP;OXk;vd$@?2>{A;EA9XQ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/DamageModel/images/crosshair_blue.png b/Templates/BaseGame/game/data/DamageModel/images/crosshair_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..d5b0485d7ad5ebd7ba72f05901210a275312e90c GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1|*LJg}1rL4Grq=Drhv!Qa&;Uf*~0#p9*AO0_0BRSz` f&z6nC3I`Z69e7L?*M3t3>S6G7^>bP0l+XkKiajbo literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/DamageModel/images/crosshair_blue_image.asset.taml b/Templates/BaseGame/game/data/DamageModel/images/crosshair_blue_image.asset.taml new file mode 100644 index 000000000..0962854e3 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/images/crosshair_blue_image.asset.taml @@ -0,0 +1,3 @@ + diff --git a/Templates/BaseGame/game/data/DamageModel/images/damageBottom.png b/Templates/BaseGame/game/data/DamageModel/images/damageBottom.png new file mode 100644 index 0000000000000000000000000000000000000000..883935ba8a837f92e61c7d0ecaf72b7c426a9fa3 GIT binary patch literal 1898 zcmV-w2bK7VP)toKuthQ$2^-KHXK-)z#J2O-rR9pT8=VDJ_-C zQo7R(e*e2t`5F2f_;XOD!q@l&2l~eWlm^wQ+yw1`_M%_ylz)Z(0{-Ye{0&rnS`O_fR7Z=}8H6+(EJ09*p%=iL z;2tofXnl?T)Sm&-8rD3z{3b_7x=1L#U?@))0ie#!SUU<;TM?8+er5aI-|4gnt5jRR}IB+!<_?nILt!~yifS$`;) zMtL*z9P|+MIWV$8uZ?Q6-~fVDu#2F3flGqDU@qk`DaeyWGvXah$^mr%j6)}$0H5hG zf}NCm!sTyLq1DnXggO<0z6w2|HvruP?S@8dpcaM};BV4e|9*7ftu*T!H(CoO7NC34 zg)2FLU^~EK1pY2Kuf+!oDNhJ*pgj75%m*XyJE2R!7nF7Rw^N=S%fk}rK@z^(g+~$i z`7{G4{98)U{Ze-k8IJ|?k@;@uchLRN)zI5Y&6u)vKE(#+LAQakD8biIje;)Wu(I)^ z=>0{e?f^XmeLWfd6RA7{8hIi`SAeVaPs^_>2>k%K=W8HnC729iw;<<=p)p6k)1fbc zc!z~>p^xyzh0l~ zTYC}Y%T^rucpHi_6eXDjHh{yRx+U~9<)bI%ZDktxQ<2vS@DaEGKH>QqsJlLq z%~xLaxOUG`6q7MBCaxn$m_|5ac<($U*AkSWe zJ^+RT&hW%1IM4?Upg4ov6#$Q+I2(bfp>7wcT&qKVWJMcSV;5SeJMXws=F+Y4v@(4^4`8*i{mCZEc*TC424gQD?S{I(e=scr!sF^AU*l&D;sA>1{R5*xH}DoB zy#m*y&Qu;SG3``iFhgSi!Wdj33k{@R6TC5|@-p}k98CuG49{I=J(*_vo1u}t*C2h4 zYHQ>GO6kmH=GizD*O`AMsP08;434-=P{EzufWgJm8UyVyNzIReyDfTA=Xkyy zYBJWnl)dh)y++Z*p9Kf9<$yC(n(fD;z-}383>^Gs8LHr>rMgeO4H!$91qy!{i9UK5 z=Mk_BybTWyfs??y8g%i!#mPZ$W~O&GxXg;6UbgBRZ>uK)l56AdVinjbZolQinPg{ zkx8^~4%DmHbl(lw85p%MdObhz+}m}%XKxwsvQ}q#x9%dCmzfsEi<*W5$vbd=+W}M! zB)TN zgPIv>gkBFu17;h|zjt=myYWI+@7a5bvfFiLSGp_Wxr=b^n?@6(Ax*&n28V&fHsIRV zfufgSW}5@Ztwt}fFz?_eXVhdI9B_IKK6e1Rq;S?f#cv&)?|`lSgdRzCP+OOETDQ9! z&gLGSc7WRk+v5Mlum|e-36I)N?s{n8>{AT*rqHrzM6NjCOv&K!8Y>5pmz^<`8px0|~VCq3QNL^a=2i1YCw{ej=UAsHLJrw2Myk3fN3hzyD z`%Z)9tlkDp)s4ZPD=mx0=8gjlGUEwmCTbvB9e8Q*`M=s!F`#Wm>T!SGJcrA5r}}-D z?-T>7KX*)z1_uZ7#DU}m=n2$}JoPu)8XO1?{09eWF#y5H;6QMofE@TgV^F~EMwfyE k!GWuBKw}WxDK7{90mfO3Ix@<;ApigX07*qoM6N<$f^#^AVE_OC literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/DamageModel/images/damageBottom_image.asset.taml b/Templates/BaseGame/game/data/DamageModel/images/damageBottom_image.asset.taml new file mode 100644 index 000000000..044292120 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/images/damageBottom_image.asset.taml @@ -0,0 +1,3 @@ + diff --git a/Templates/BaseGame/game/data/DamageModel/images/damageFront.png b/Templates/BaseGame/game/data/DamageModel/images/damageFront.png new file mode 100644 index 0000000000000000000000000000000000000000..ff759fc7c3c7c704eb204a46770c8f8594dc01fa GIT binary patch literal 1914 zcmV-=2Zi{FP)Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;u(kfL#Cp2GdDI zK~#8N?VX8_R7DVnhlS;E*Toe@5Kw{#hZqh|@B$J+jDm_7HKO4#azr#JLFG`COSx|Z z5d;C@k5s>J(qlG;-7GsZJMSoxU#jQrOuz1`uIlRQ>ZVeOf2HNXKb6X@(sn*N9vmnm z2UPmrP)2@4pMwL1=RjXdAI6~Y;*SoNl>-A&{Qv)^th|eU2M03Gfxj!2A)pPVZG#Sl z4il86?O;efo$&Xmo_6ZAzFPgh%Xf;p-ce8c;U_Rw# z(6!KSz)zHqLH`72(v1EW^u`^+-`q^Uzeb%WtsCs5yaBob`U;pvd5mqcZK;j6wXGDw ziB1Lw8p(k)tE%XuP@=n`_d#cX#o#kOtArPNDE9TF>{p-5@_Mio%mokd%syxw>{A$pMh=#!EB_pDrUcIF zD#{qjk3g4#jg${TRdAugseme^N_~>Q?S_61{Sf*Zc#87lU?LdNDDEWX9iQr+1ziMI z^1cnwUC`q|Z{ye0Mvali;U<4S4!#0hBm5=QBQCnHyl7QMgWyyEL0cz z9m-q4=`^FNK+f`h)(C6^t0=z=eN+X?T4!lf2kn~-UeX|eFDU;EPNlx+HQeC2UdeXw z9-rqx^%6#Fut3BUvL@JQPyHNVU^~m}20QybM0pX|NcjZR+1;7_95}|aAEB=2U0+Xx z=6aS?!Rf>NWE6H%J_7V$TvA-;`B|_TEa9`>iw2=Hyo373UDVJ*4)joZXZh(s7kCG# zt)F$5kMQ|hpa<|gc(8>{9+X$pM+1gW1NpHP9vlQNAF9g+p6yBA!EE^fTpH!t2o4Hk z<0aYP0E5-F?KlSW6G`c}K@HA1z;%cB@%K&6czOe%v~I6`2V=nt@MRs?1B@r=5nbi^ z-ee5sLMMO@4Qn!``Dt&K*CK&@~ zd*cEh0GA6#lQFo==Uw0}Pzxls#{ditw3Gu3Mgvz97^KgDRg@1wb@AOZ{{gIZb^~tZ z#mv=DX=xzgna04GUt{1lrk=qS$_7T)Lk);d2IfSBLG34R<7;(tAPr6fdd~Ll&2Is2 z&FQx8=b6Dh19-0eW9`}ie(NSb{N@t}wE7GCO|WI|m$GX^_oe zz%8fzeNuYY_0CZ5^W49U3O+W61Ignu`|)k~?J~u>XC||A4 z_+T{99W~)*KQO3w7&zOT6yq{rSoyfqnjR;axU4d!vZf{zu5x{^0ay*5<9T;sI-JvvAOmv-W+M-cvzd_^XT37x1TO-&9m6BYseA{$ z*-7y`lj48eL-8G4y7}%;cMf{P;)Q*3AT1B3r{#f%qKp$5jP?A49)a105iE>x)aN?i zBd=~VIphBV%)j3YRvKt>kOlv;%>iWONkn%C-hdjL(Q7kSU;xnsq>|3+=51`HyZ{=@f?|og@7`$n5xi0315jN7-b$nJs|R!_d#Q%5;1q*Lg=au?u*w1C zZBo}HWWE@8g_Fg22eN%VM*Z20?ND67X1oHEOv^?~>HBWWw4V;0l0h996 zi)iV896*3mfhYAYBE(bRP2lxZaY8S~$!$5n`0)QZ@Z{kFP&?Y~El#F=$Nd9&7&i(E zC0_{4*>}&^2)_q;yiF!%{X7V<#cgDq7#o-dz634_j2kTnkpkXg+4T8(1m#V;E1;*7 z8|O)}hr@bjS!&sA8)L>i5icn979uYpxd1hXU|cp^T=G^SAn(HWf2{GM2-j~0aXiKw zfxeQDje&Mwj}dssqsd|WQh?Aa+NTb*&aD0JU!k^ diff --git a/Templates/BaseGame/game/data/DamageModel/images/damageLeft.png b/Templates/BaseGame/game/data/DamageModel/images/damageLeft.png new file mode 100644 index 0000000000000000000000000000000000000000..9562db1dd826b1f10491bc8e01157ca0111dad5d GIT binary patch literal 1888 zcma)-`#;kQ1INF!*`19@2s5SQq&0^kBA3nFbI`?oBvQi3%4H72WW{kg$0L_K)nTsZ z(ZtD}VJ@8#QFy{k4aapDW|1DR=MQ*Z&rhHC=f}_Im-ho#7m6HG4G92%oV^{%?I3Xg z00Q990~&rtyLS+f5G!XZ0I1DE?FGmlbX6MpD$PA2m=+yy=Q`jT5E@P+xR5}A&+w!Ry}j0N&iBC{=jfBK`RQVGe3LF#1L z8a&lUzCiZ#U%-|B!tdoQij5jxsUPa#8wA+7%*~FRi%KwzUVSqe>hAvaibjRJ2doPG zA!*U_?o(6O|6PZYItmNtluOd$H>RDPcOSwT@kZnK8SOL>wt=ygJqML=XFMkZ4Dp zv@rdhVLeKVa)zVMBY0N>uEVw(T-PZu0$~RsY29B4ADwo9blj^YcV`4w4w^`(du5qC3esaQ<;TPmWGtG2MFYZmu6HOG5J>!EGN zH8#!Y$Dn!cuSOd?tN+Sgg-AMKRF6WSL_J1zSS-Nh|7NS%riEFU=d!nbKfY&;p%<#N z{h%<1%>>0#f1rG7qOvVwXZs3{j|mP++$}E_2{61ch|MtM$P8{7qJrNzWp}V)xK^M; z$Gj{p^Tf(059KE!9}A=7-m9yM+f+akQghP}3Gfg`w}SL?&~Ggb-iW5QX_RgdDt{as zbJkRI7(xoQ5T#OeoblL1f$3J@hoZwNBK*q9&{0^dp}B~~S*m)}-lJyo ze1H}{q6QK}{Y{9wCul5hb ze?HW>@6X&r-u7+=V;hnv0vckYGbdrHT;gAjV$k3+{ETJeM8NvIQq1B^==`nYN7uOT zibZ#`>5B@6T)n^fgU3xotr6UtEc1JGG$D^O-JK@Q{$@{aWCCLh0wf>2>YxHt8VJk< zn$)Ofz|LH{nb}9a;oJtWU`jKEORBxhHJCm(SWpzqH+@^@J|G<4iAm*enI@x z2Nt;4B%fPw{~HBFu!Rld18fMyHH2o}QHhcQR%#iY!#viTteSI)SQgYIYgcQaAyaGo zNtZpCyMtau{l%WGHKI* z{AvERn=bTYzLvb?pK>aM5ia<{8Ae!;2+>>C0#x?VdXv@b>qcZPm#x@6p_(2w`r6zZ zHw27eNOd|u$|fOudKcGHZt0&uSW4R*Rguv z49qEE0S*XLOe4iE<5@g&W-nNq&7$v`hr`IDnuUIUG^KpoYe5)A| z_f_ClOpqD27bxOex#1U8fZ9JxdQ+C*SG}#d#Qrf}moTar|KwRQe>!Oo(tj#2J%*~C za!KqLG(TNx6ZSB;itq0T+3ylyP-Pg`; zcxSr&CvQOr*dxw~LHmJ|Fa6>66$B{Q{_<#SWz)nTl}wf=wy47EQU{00D924ND#}?v zVxNB>XzCRpk>T)`0D{xWnJ%OLfn4-}RAn;TYi?n5U{FBSe_>f*@C_0_{<4)9!WrsG z`=thBvGV%oIZ)-5)^O^@-dvwwS7msYeZmch=ZkQa`^j0ePq7 zS|e4JGoOBWW_CX*{Rk=#Xw!Q46Q(2mv0l9n^@7mizL(q`acX4OKQ`1j$HIaifEGeq zo^Y!sw{X#3wsXQ3rh9gg)h_9~XfxQe>q!I;!dM=wh&&snCuVh9_a&@ z@-Ilwzv&}R*L#k>!F?`rbqvYHZ8bfKlSol5+R>n@d^Gt(#5N(VgJ!S$t?Focd% zOUqeH^CF8g3W95~#zuLtorVWq?!GLdIVRBf<>i&RY3DT72iBTynb}lQzH%n4x`Cb! zztOKFzBQ&!&j3pw1@!1Y=ly?*`LBTXUneC37*3AGzuy&BA1D&AC%ceptf|TW0Xxle Ap8x;= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/DamageModel/images/damageLeft_image.asset.taml b/Templates/BaseGame/game/data/DamageModel/images/damageLeft_image.asset.taml new file mode 100644 index 000000000..66e90be28 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/images/damageLeft_image.asset.taml @@ -0,0 +1,3 @@ + diff --git a/Templates/BaseGame/game/data/DamageModel/images/damageRight.png b/Templates/BaseGame/game/data/DamageModel/images/damageRight.png new file mode 100644 index 0000000000000000000000000000000000000000..3a9a626cc82f5468a7a2f4d213fdd7c906a64e38 GIT binary patch literal 1837 zcmajg`#;l*9|!P{VZ$0*)@n>vXEVpTn^JD6Y1;w7o4A-ulM8i!|TWQ)j)qwn9^}2003ZkFI>=} zNC1!l6u^gRvYtviM2_g<=K=t2*P#0`@`tw;)%_whI3B}^)k4q`wt282i{y#oGBHrGHDGV5NC@dB@yZGt=V*_@7_T-~v@Kv~{? zFr|vGJs=1~z*X|8*0#^Ze3C}75-HMLP81Rr@|VX5@32w^-VOLuy)H{Aw=n8(&|wM;DKK+wQt|)iJ{}o%W<~e6YhjMCKAolLYB=3pAEOA#Y?ZtQ z6U^p&71w=b$|I^NmDp(p73tSNw@8qp5)!6&E`n0;tp&Y;vMyC`A0ozn|9g7%qAfSX=PvI93|RO`op7$U5y}|5*H;fih$t99DyTU86j*6KRN6_ z>O+tP>5Wnb$aFbLTqrBmcsRTER@J^*vzEqIvMaTu93~1R*Dx4?h z8x_Kmj*Nu#0Uz}{dIpxuk~IsN`jPtm_sf!7Cnb2Ajj6Te*7jb1obWBs1)Yu=@q2S3 zUZHjDp)O)34|-utsq3idO8@fB8xQ>QdET|bXX5Q=VbO*n^_z;Z4Y6}iId6X^&7p)W z1mu@uscFlt$ssM!eUp9VT@$l25!f~GkVMza%F*<32PY%FVE+Ok)qNc~j%E+NiOHOLQ4qn(kS%K|Hif%;NHm6?UK92R*r}ULa~-VzkZl`A=oC zoVi}|yO$`+Y2wDG)hozcMS4%{X)lxWVfqr>TMJet@>5qZe3);SIA``fwgyr*>gC#N zEOz;A+Q*DE7==K^m`5r2?eQjucyrU=%Trl&D2j~NSn=lm^I-F)G0-hb&>SV&GxOpZLP79 zx7YyVJ|=wwg(Kj#4D71=KVGor_s}wJR!H_Zjw{FQ3VK7{2!<8!ai^;cRoa`!io8 zaJdfD1-Hx7TQRi(XFnJsEmyl{cF24f+Zjle_cYWgh;qHcv5}0I{GMYle4r1q(N4TG zI=;>DBsETb&6&@QCB3fY+if!LwVHleL!Y5f3a#g4G;KDKmJ3jTdPNb@!(t=ao za(O#+d}hzX@}McX4j6g&?bt;r_Vi%dZ5?CCSl-{VL4+L0+T6bG diff --git a/Templates/BaseGame/game/data/DamageModel/images/damageTop.png b/Templates/BaseGame/game/data/DamageModel/images/damageTop.png new file mode 100644 index 0000000000000000000000000000000000000000..ff759fc7c3c7c704eb204a46770c8f8594dc01fa GIT binary patch literal 1914 zcmV-=2Zi{FP)Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;u(kfL#Cp2GdDI zK~#8N?VX8_R7DVnhlS;E*Toe@5Kw{#hZqh|@B$J+jDm_7HKO4#azr#JLFG`COSx|Z z5d;C@k5s>J(qlG;-7GsZJMSoxU#jQrOuz1`uIlRQ>ZVeOf2HNXKb6X@(sn*N9vmnm z2UPmrP)2@4pMwL1=RjXdAI6~Y;*SoNl>-A&{Qv)^th|eU2M03Gfxj!2A)pPVZG#Sl z4il86?O;efo$&Xmo_6ZAzFPgh%Xf;p-ce8c;U_Rw# z(6!KSz)zHqLH`72(v1EW^u`^+-`q^Uzeb%WtsCs5yaBob`U;pvd5mqcZK;j6wXGDw ziB1Lw8p(k)tE%XuP@=n`_d#cX#o#kOtArPNDE9TF>{p-5@_Mio%mokd%syxw>{A$pMh=#!EB_pDrUcIF zD#{qjk3g4#jg${TRdAugseme^N_~>Q?S_61{Sf*Zc#87lU?LdNDDEWX9iQr+1ziMI z^1cnwUC`q|Z{ye0Mvali;U<4S4!#0hBm5=QBQCnHyl7QMgWyyEL0cz z9m-q4=`^FNK+f`h)(C6^t0=z=eN+X?T4!lf2kn~-UeX|eFDU;EPNlx+HQeC2UdeXw z9-rqx^%6#Fut3BUvL@JQPyHNVU^~m}20QybM0pX|NcjZR+1;7_95}|aAEB=2U0+Xx z=6aS?!Rf>NWE6H%J_7V$TvA-;`B|_TEa9`>iw2=Hyo373UDVJ*4)joZXZh(s7kCG# zt)F$5kMQ|hpa<|gc(8>{9+X$pM+1gW1NpHP9vlQNAF9g+p6yBA!EE^fTpH!t2o4Hk z<0aYP0E5-F?KlSW6G`c}K@HA1z;%cB@%K&6czOe%v~I6`2V=nt@MRs?1B@r=5nbi^ z-ee5sLMMO@4Qn!``Dt&K*CK&@~ zd*cEh0GA6#lQFo==Uw0}Pzxls#{ditw3Gu3Mgvz97^KgDRg@1wb@AOZ{{gIZb^~tZ z#mv=DX=xzgna04GUt{1lrk=qS$_7T)Lk);d2IfSBLG34R<7;(tAPr6fdd~Ll&2Is2 z&FQx8=b6Dh19-0eW9`}ie(NSb{N@t}wE7GCO|WI|m$GX^_oe zz%8fzeNuYY_0CZ5^W49U3O+W61Ignu`|)k~?J~u>XC||A4 z_+T{99W~)*KQO3w7&zOT6yq{rSoyfqnjR;axU4d!vZf{zu5x{^0ay*5<9T;sI-JvvAOmv-W+M-cvzd_^XT37x1TO-&9m6BYseA{$ z*-7y`lj48eL-8G4y7}%;cMf{P;)Q*3AT1B3r{#f%qKp$5jP?A49)a105iE>x)aN?i zBd=~VIphBV%)j3YRvKt>kOlv;%>iWONkn%C-hdjL(Q7kSU;xnsq>|3+=51`HyZ{=@f?|og@7`$n5xi0315jN7-b$nJs|R!_d#Q%5;1q*Lg=au?u*w1C zZBo}HWWE@8g_Fg22eN%VM*Z20?ND67X1oHEOv^?~>HBWWw4V;0l0h996 zi)iV896*3mfhYAYBE(bRP2lxZaY8S~$!$5n`0)QZ@Z{kFP&?Y~El#F=$Nd9&7&i(E zC0_{4*>}&^2)_q;yiF!%{X7V<#cgDq7#o-dz634_j2kTnkpkXg+4T8(1m#V;E1;*7 z8|O)}hr@bjS!&sA8)L>i5icn979uYpxd1hXU|cp^T=G^SAn(HWf2{GM2-j~0aXiKw zfxeQDje&Mwj}dssqsd|WQh?Aa+NTb*&aD0JU!k^ diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/client/playGui.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/client/playGui.tscript new file mode 100644 index 000000000..77f533cce --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/scripts/client/playGui.tscript @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// PlayGui is the main TSControl through which the game is viewed. +// The PlayGui also contains the hud controls. +//----------------------------------------------------------------------------- + +function DamageModel::Playgui_onWake(%this) +{ + Canvas.pushDialog(DamageGuiOverlay); +} + +function DamageModel::Playgui_onSleep(%this) +{ + Canvas.popDialog(DamageGuiOverlay); +} + +function DamageModel::Playgui_clearHud( %this ) +{ + Canvas.popDialog(DamageGuiOverlay); +} + +function clientCmdSetDamageDirection(%direction) +{ + %ctrl = DamageHUD.findObjectByInternalName("damage[" @ %direction@"]"); + if (isObject(%ctrl)) + { + // Show the indicator, and schedule an event to hide it again + cancelAll(%ctrl); + %ctrl.setVisible(true); + %ctrl.schedule(1500, setVisible, false); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/player.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/player.tscript new file mode 100644 index 000000000..817ae85c2 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/player.tscript @@ -0,0 +1,42 @@ +function PlayerData::damage(%this, %obj, %sourceObject, %position, %damage, %damageType) +{ + if (!isObject(%obj) || %obj.getDamageState() !$= "Enabled" || !%damage) + return; + + %rootObj = %obj; + if (%obj.healthFromMount) + %rootObj = findRootObject(%obj); + + %rootObj.applyDamage(%damage); + %this.onDamage(%rootObj, %damage); + + %this.setDamageDirection(%obj, %sourceObject, %position); + + // Deal with client callbacks here because we don't have this + // information in the onDamage or onDisable methods + %client = %rootObj.client; + %sourceClient = %sourceObject ? %sourceObject.client : 0; + + %location = "Body"; + if (isObject(%client)) + { + if (%rootObj.getDamageState() !$= "Enabled") + { + callGamemodeFunction("onDeath", %client, %sourceObject, %sourceClient, %damageType, %location); + } + } +} + +function PlayerData::onDamage(%this, %obj, %delta) +{ + Parent::onDamage(%this, %obj, %delta); + + // This method is invoked by the ShapeBase code whenever the + // object's damage level changes. + if (%delta > 0 && %obj.getDamageState() !$= "Destroyed") + { + // If the pain is excessive, let's hear about it. + if (%delta > 10) + %obj.playPain(); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/projectile.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/projectile.tscript new file mode 100644 index 000000000..1af454f61 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/projectile.tscript @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// "Universal" script methods for projectile damage handling. You can easily +// override these support functions with an equivalent namespace method if your +// weapon needs a unique solution for applying damage. + +function ProjectileData::onCollision(%data, %proj, %col, %fade, %pos, %normal) +{ + //echo("ProjectileData::onCollision("@%data.getName()@", "@%proj@", "@%col.getClassName()@", "@%fade@", "@%pos@", "@%normal@")"); + + // Apply damage to the object all shape base objects + if (%data.directDamage > 0) + { + if (%col.getType() & ($TypeMasks::ShapeBaseObjectType)) + %col.damage(%proj, %pos, %data.directDamage, %data.damageType); + } +} + +function ProjectileData::onExplode(%data, %proj, %position, %mod) +{ + //echo("ProjectileData::onExplode("@%data.getName()@", "@%proj@", "@%position@", "@%mod@")"); + + // Damage objects within the projectiles damage radius + if (%data.damageRadius > 0) + radiusDamage(%proj, %position, %data.damageRadius, %data.radiusDamage, %data.damageType, %data.areaImpulse); +} diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/radiusDamage.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/radiusDamage.tscript new file mode 100644 index 000000000..91255f25c --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/radiusDamage.tscript @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Support function which applies damage to objects within the radius of +// some effect, usually an explosion. This function will also optionally +// apply an impulse to each object. + +function radiusDamage(%sourceObject, %position, %radius, %damage, %damageType, %impulse) +{ + // Use the container system to iterate through all the objects + // within our explosion radius. We'll apply damage to all ShapeBase + // objects. + InitContainerRadiusSearch(%position, %radius, $TypeMasks::ShapeBaseObjectType | $TypeMasks::DynamicShapeObjectType); + + %halfRadius = %radius / 2; + while ((%targetObject = containerSearchNext()) != 0) + { + // Calculate how much exposure the current object has to + // the explosive force. The object types listed are objects + // that will block an explosion. If the object is totally blocked, + // then no damage is applied. + %coverage = calcExplosionCoverage(%position, %targetObject, + $TypeMasks::InteriorObjectType | + $TypeMasks::TerrainObjectType | + $TypeMasks::ForceFieldObjectType | + $TypeMasks::StaticShapeObjectType | + $TypeMasks::VehicleObjectType); + if (%coverage == 0) + continue; + + // Radius distance subtracts out the length of smallest bounding + // box axis to return an appriximate distance to the edge of the + // object's bounds, as opposed to the distance to it's center. + %dist = containerSearchCurrRadiusDist(); + + // Calculate a distance scale for the damage and the impulse. + // Full damage is applied to anything less than half the radius away, + // linear scale from there. + %distScale = (%dist < %halfRadius)? 1.0 : 1.0 - ((%dist - %halfRadius) / %halfRadius); + %distScale = mClamp(%distScale,0.0,1.0); + + // Apply the damage + %targetObject.damage(%sourceObject, %position, %damage * %coverage * %distScale, %damageType); + + // Apply the impulse + if (%impulse) + { + %impulseVec = VectorSub(%targetObject.getWorldBoxCenter(), %position); + %impulseVec = VectorNormalize(%impulseVec); + %impulseVec = VectorScale(%impulseVec, %impulse * %distScale); + %targetObject.applyImpulse(%position, %impulseVec); + } + } +} diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript new file mode 100644 index 000000000..b1aa67448 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript @@ -0,0 +1,285 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// This file contains ShapeBase methods used by all the derived classes +$DeathDuration = 10000; +$CorpseTimeoutValue = 20000; +//----------------------------------------------------------------------------- +// ShapeBase object +//----------------------------------------------------------------------------- + +// A raycast helper function to keep from having to duplicate code everytime +// that a raycast is needed. +// %this = the object doing the cast, usually a player +// %range = range to search +// %mask = what to look for + +function ShapeBase::doRaycast(%this, %range, %mask) +{ + // get the eye vector and eye transform of the player + %eyeVec = %this.getEyeVector(); + %eyeTrans = %this.getEyeTransform(); + + // extract the position of the player's camera from the eye transform (first 3 words) + %eyePos = getWord(%eyeTrans, 0) SPC getWord(%eyeTrans, 1) SPC getWord(%eyeTrans, 2); + + // normalize the eye vector + %nEyeVec = VectorNormalize(%eyeVec); + + // scale (lengthen) the normalized eye vector according to the search range + %scEyeVec = VectorScale(%nEyeVec, %range); + + // add the scaled & normalized eye vector to the position of the camera + %eyeEnd = VectorAdd(%eyePos, %scEyeVec); + + // see if anything gets hit + %searchResult = containerRayCast(%eyePos, %eyeEnd, %mask, %this); + + return %searchResult; +} + +//----------------------------------------------------------------------------- + +function ShapeBase::damage(%this, %sourceObject, %position, %damage, %damageType) +{ + // All damage applied by one object to another should go through this method. + // This function is provided to allow objects some chance of overriding or + // processing damage values and types. As opposed to having weapons call + // ShapeBase::applyDamage directly. Damage is redirected to the datablock, + // this is standard procedure for many built in callbacks. + + if (isObject(%this)) + %this.getDataBlock().damage(%this, %sourceObject, %position, %damage, %damageType); +} + +//----------------------------------------------------------------------------- + +function ShapeBase::setDamageDt(%this, %damageAmount, %damageType) +{ + // This function is used to apply damage over time. The damage is applied + // at a fixed rate (50 ms). Damage could be applied over time using the + // built in ShapBase C++ repair functions (using a neg. repair), but this + // has the advantage of going through the normal script channels. + + if (%this.getState() !$= "Dead") + { + %this.damage(0, "0 0 0", %damageAmount, %damageType); + %this.damageSchedule = %this.schedule(50, "setDamageDt", %damageAmount, %damageType); + } + else + %this.damageSchedule = ""; +} + +function ShapeBase::clearDamageDt(%this) +{ + if (%this.damageSchedule !$= "") + { + cancel(%this.damageSchedule); + %this.damageSchedule = ""; + } +} + +function GameBase::damage(%this, %sourceObject, %position, %damage, %damageType) +{ + // All damage applied by one object to another should go through this method. + // This function is provided to allow objects some chance of overriding or + // processing damage values and types. As opposed to having weapons call + // ShapeBase::applyDamage directly. Damage is redirected to the datablock, + // this is standard procedure for many built in callbacks. + + %datablock = %this.getDataBlock(); + if ( isObject( %datablock ) ) + %datablock.damage(%this, %sourceObject, %position, %damage, %damageType); +} + +//----------------------------------------------------------------------------- +// ShapeBase datablock +//----------------------------------------------------------------------------- + +function GameBaseData::damage(%this, %obj, %source, %position, %amount, %damageType) +{ + // Ignore damage by default. This empty method is here to + // avoid console warnings. +} + +function ShapeBaseData::onAdd(%this, %obj) +{ + %obj.setDamageState("Enabled"); +} + +function ShapeBaseData::setDamageDirection(%this, %obj, %sourceObject, %damagePos) +{ + if (!%obj.client) return; + if (%damagePos $= "" && isObject(%sourceObject)) + { + if (%sourceObject.isField(initialPosition)) + { + // Projectiles have this field set to the muzzle point of + // the firing weapon at the time the projectile was created. + // This gives a damage direction towards the firing object, + // turret, vehicle, etc. Bullets and weapon fired grenades + // are examples of projectiles. + %damagePos = %sourceObject.initialPosition; + } + else + { + // Other objects that cause damage, such as mines, use their own + // location as the damage position. This gives a damage direction + // towards the explosive origin rather than the person that lay the + // explosives. + %damagePos = %sourceObject.getPosition(); + } + } + + // Rotate damage vector into object space + %damageVec = VectorSub(%damagePos, %obj.getWorldBoxCenter()); + %damageVec = VectorNormalize(%damageVec); + %damageVec = MatrixMulVector(%obj.client.getCameraObject().getInverseTransform(), %damageVec); + + // Determine largest component of damage vector to get direction + %vecComponents = -%damageVec.x SPC %damageVec.x SPC -%damageVec.y SPC %damageVec.y SPC -%damageVec.z SPC %damageVec.z; + %vecDirections = "Left" SPC "Right" SPC "Bottom" SPC "Front" SPC "Bottom" SPC "Top"; + + %max = -1; + for (%i = 0; %i < 6; %i++) + { + %value = getWord(%vecComponents, %i); + if (%value > %max) + { + %max = %value; + %damageDir = getWord(%vecDirections, %i); + } + } + commandToClient(%obj.client, 'setDamageDirection', %damageDir); +} + + +function ShapeBaseData::onCollision(%this, %obj, %collObj, %vec, %len ) +{ + if ((!isObject(%obj) || %obj.getDamageState() !$= "Enabled")) + return; + + //echo(%this SPC %obj SPC %collObj SPC %vec SPC %len ); + %dmgPos = VectorSub(%obj.getPosition(), %vec); + %dmgAmt = %len/%this.minImpactSpeed * %this.collisionMul; + + %this.damage(%obj, %collObj, %dmgPos, %dmgAmt, "impact"); +} + +function ShapeBaseData::onImpact(%this, %obj, %collObj, %vec, %len ) +{ + if ((!isObject(%obj) || %obj.getDamageState() !$= "Enabled")) + return; + + //echo(%this SPC %obj SPC %collObj SPC %vec SPC %len ); + %dmgPos = VectorSub(%obj.getPosition(), %vec); + %dmgAmt = %len/%this.minImpactSpeed * %this.impactMul; + + %this.damage(%obj, %collObj, %dmgPos, %dmgAmt, "impact"); +} + +//---------------------------------------------------------------------------- + +function ShapeBaseData::damage(%this, %obj, %sourceObject, %position, %damage, %damageType) +{ + if (!isObject(%obj) || %obj.getDamageState() !$= "Enabled" || !%damage) + return; + + %rootObj = %obj; + if (%obj.healthFromMount) + %rootObj = findRootObject(%obj); + + %rootObj.applyDamage(%damage); + %this.onDamage(%rootObj, %damage); + + %this.setDamageDirection(%obj, %sourceObject, %position); + + // Deal with client callbacks here because we don't have this + // information in the onDamage or onDisable methods + %client = %rootObj.client; + %sourceClient = %sourceObject ? %sourceObject.client : 0; + + if (isObject(%client)) + { + if (%rootObj.getDamageState() !$= "Enabled") + { + callGamemodeFunction("onDeath", %client, %sourceObject, %sourceClient, %damageType, ""); + } + } +} + +function ShapeBaseData::onDamage(%this, %obj, %delta) +{ + // This method is invoked by the ShapeBase code whenever the + // object's damage level changes. + if (%delta > 0 && %obj.getDamageState() $= "Enabled") + { + // Apply a damage flash + %obj.setDamageFlash(1); + + //total raw damage allowed to be stored + if (%this.maxDamage> 1.0 && %obj.getDamageLevel() >= %this.maxDamage) + %obj.setDamageState("Destroyed"); + //damage before we are considered destroyed (can animate via "damage" thread) + else if (%this.destroyedLevel> 1.0 && %obj.getDamageLevel() >= %this.destroyedLevel) + %obj.setDamageState("Destroyed"); + //optional additional disabled level + else if (%this.disabledLevel> 1.0 && %obj.getDamageLevel() >= %this.disabledLevel) + %obj.setDamageState("Disabled"); + } +} + +function ShapeBaseData::onDisabled(%this, %obj, %state) +{ + // Release the weapon triggers + for (%slot = 0; %slot<4; %slot++) + { + if (%obj.getMountedImage(%slot)) + %obj.setImageTrigger(%slot, false); + } +} + +function ShapeBaseData::onDestroyed(%this, %obj, %state) +{ + // Release the weapon triggers + for (%slot = 0; %slot<4; %slot++) + { + if (%obj.getMountedImage(%slot)) + %obj.setImageTrigger(%slot, false); + } + + if (%obj.client) + { + %obj.client.player = 0; + %obj.client.schedule($DeathDuration, "spawnControlObject"); + } + // Schedule corpse removal. Just keeping the place clean. + %obj.schedule($CorpseTimeoutValue - 1000, "startFade", 1000, 0, true); + %obj.schedule($CorpseTimeoutValue, "delete"); + %obj.schedule($CorpseTimeoutValue,"blowUp"); +} + +function ShapeBaseData::onRemove(%this, %obj) +{ + Parent::onRemove(%this, %obj); + deleteMountchain(%obj); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/utility.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/utility.tscript new file mode 100644 index 000000000..c74f8ee50 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/utility.tscript @@ -0,0 +1,34 @@ +function findRootObject(%obj) +{ if (!isObject(%obj)) return -1; + %ret = %obj; + if (isObject(%obj.getObjectMount())) + %ret = findRootObject(%obj.getObjectMount()); + return %ret; +} + +function deleteMountchain(%obj) +{ + if (!isObject(%obj)) return; + %count = %obj.getMountedObjectCount(); + for (%i=%count; %i>=0; %i--) + { + if (isObject(%obj.getMountedObject(%i))) + deleteMountchain(%obj.getMountedObject(%i)); + } + if (%obj.isMounted()) + %obj.delete(); +} + + +function setMountChainDamage(%obj,%damagePercent) +{ + if (!isObject(%obj)) return; + %count = %obj.getMountedObjectCount(); + for (%i=0; %i<%count; %i++) + { + if (isObject(%obj.getMountedObject(%i))) + setMountChainDamage(%obj.getMountedObject(%i),%damagePercent); + } + + %obj.setDamageLevel(%obj.getMaxDamage()*%damagePercent); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/weapon.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/weapon.tscript new file mode 100644 index 000000000..8cb476614 --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/weapon.tscript @@ -0,0 +1,645 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// This file contains Weapon and Ammo Class/"namespace" helper methods as well +// as hooks into the inventory system. These functions are not attached to a +// specific C++ class or datablock, but define a set of methods which are part +// of dynamic namespaces "class". The Items include these namespaces into their +// scope using the ItemData and ItemImageData "className" variable. +// ---------------------------------------------------------------------------- + +// All ShapeBase images are mounted into one of 8 slots on a shape. This weapon +// system assumes all primary weapons are mounted into this specified slot: +$WeaponSlot = 0; + +//----------------------------------------------------------------------------- +// Weapon Class +//----------------------------------------------------------------------------- + +function Weapon::onUse(%data, %obj) +{ + // Default behavior for all weapons is to mount it into the object's weapon + // slot, which is currently assumed to be slot 0 + if (%obj.getMountedImage($WeaponSlot) != %data.image.getId()) + { + serverPlay3D(WeaponUseSound, %obj.getTransform()); + + %obj.mountImage(%data.image, $WeaponSlot); + if (%obj.client) + { + if (%data.description !$= "") + messageClient(%obj.client, 'MsgWeaponUsed', '\c0%1 selected.', %data.description); + else + messageClient(%obj.client, 'MsgWeaponUsed', '\c0Weapon selected'); + } + + // If this is a Player class object then allow the weapon to modify allowed poses + if (%obj.isInNamespaceHierarchy("Player")) + { + // Start by allowing everything + %obj.allowAllPoses(); + + // Now see what isn't allowed by the weapon + + %image = %data.image; + + if (%image.jumpingDisallowed) + %obj.allowJumping(false); + + if (%image.jetJumpingDisallowed) + %obj.allowJetJumping(false); + + if (%image.sprintDisallowed) + %obj.allowSprinting(false); + + if (%image.crouchDisallowed) + %obj.allowCrouching(false); + + if (%image.proneDisallowed) + %obj.allowProne(false); + + if (%image.swimmingDisallowed) + %obj.allowSwimming(false); + } + } +} + +function Weapon::onPickup(%this, %obj, %shape, %amount) +{ + // The parent Item method performs the actual pickup. + // For player's we automatically use the weapon if the + // player does not already have one in hand. + if (Parent::onPickup(%this, %obj, %shape, %amount)) + { + serverPlay3D(WeaponPickupSound, %shape.getTransform()); + if (%shape.getClassName() $= "Player" && %shape.getMountedImage($WeaponSlot) == 0) + %shape.use(%this); + } +} + +function Weapon::onInventory(%this, %obj, %amount) +{ + // Weapon inventory has changed, make sure there are no weapons + // of this type mounted if there are none left in inventory. + if (!%amount && (%slot = %obj.getMountSlot(%this.image)) != -1) + %obj.unmountImage(%slot); +} + +//----------------------------------------------------------------------------- +// Weapon Image Class +//----------------------------------------------------------------------------- + +function WeaponImage::onMount(%this, %obj, %slot) +{ + // Images assume a false ammo state on load. We need to + // set the state according to the current inventory. + if(%this.isField("clip")) + { + // Use the clip system for this weapon. Check if the player already has + // some ammo in a clip. + if (%obj.getInventory(%this.ammo)) + { + %obj.setImageAmmo(%slot, true); + %currentAmmo = %obj.getInventory(%this.ammo); + } + else if(%obj.getInventory(%this.clip) > 0) + { + // Fill the weapon up from the first clip + %obj.setInventory(%this.ammo, %this.ammo.maxInventory); + %obj.setImageAmmo(%slot, true); + + // Add any spare ammo that may be "in the player's pocket" + %currentAmmo = %this.ammo.maxInventory; + %amountInClips += %obj.getFieldValue( "remaining" @ %this.ammo.getName()); + } + else + { + %currentAmmo = 0 + %obj.getFieldValue( "remaining" @ %this.ammo.getName()); + } + + %amountInClips = %obj.getInventory(%this.clip); + %amountInClips *= %this.ammo.maxInventory; + + if (%obj.client !$= "" && !%obj.isAiControlled) + %obj.client.RefreshWeaponHud(%currentAmmo, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle, %amountInClips); + } + else if(%this.ammo !$= "") + { + // Use the ammo pool system for this weapon + if (%obj.getInventory(%this.ammo)) + { + %obj.setImageAmmo(%slot, true); + %currentAmmo = %obj.getInventory(%this.ammo); + } + else + %currentAmmo = 0; + + if (%obj.client !$= "" && !%obj.isAiControlled) + %obj.client.RefreshWeaponHud( 1, %this.item.previewImage, %this.item.reticle, %this.item.zoomReticle, %currentAmmo ); + } +} + +function WeaponImage::onUnmount(%this, %obj, %slot) +{ + if (%obj.client !$= "" && !%obj.isAiControlled) + %obj.client.RefreshWeaponHud(0, "", ""); +} + +// ---------------------------------------------------------------------------- +// A "generic" weaponimage onFire handler for most weapons. Can be overridden +// with an appropriate namespace method for any weapon that requires a custom +// firing solution. + +// projectileSpread is a dynamic property declared in the weaponImage datablock +// for those weapons in which bullet skew is desired. Must be greater than 0, +// otherwise the projectile goes straight ahead as normal. lower values give +// greater accuracy, higher values increase the spread pattern. +// ---------------------------------------------------------------------------- + +function WeaponImage::onFire(%this, %obj, %slot) +{ + //echo("\c4WeaponImage::onFire( "@%this.getName()@", "@%obj.client.nameBase@", "@%slot@" )"); + + // Make sure we have valid data + if (!isObject(%this.projectile)) + { + error("WeaponImage::onFire() - Invalid projectile datablock"); + return; + } + + // Decrement inventory ammo. The image's ammo state is updated + // automatically by the ammo inventory hooks. + if ( !%this.infiniteAmmo ) + %obj.decInventory(%this.ammo, 1); + + // Get the player's velocity, we'll then add it to that of the projectile + %objectVelocity = %obj.getVelocity(); + + %numProjectiles = %this.projectileNum; + if (%numProjectiles == 0) + %numProjectiles = 1; + + for (%i = 0; %i < %numProjectiles; %i++) + { + if (%this.projectileSpread) + { + // We'll need to "skew" this projectile a little bit. We start by + // getting the straight ahead aiming point of the gun + %vec = %obj.getMuzzleVector(%slot); + + // Then we'll create a spread matrix by randomly generating x, y, and z + // points in a circle + %matrix = ""; + for(%j = 0; %j < 3; %j++) + %matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.projectileSpread @ " "; + %mat = MatrixCreateFromEuler(%matrix); + + // Which we'll use to alter the projectile's initial vector with + %muzzleVector = MatrixMulVector(%mat, %vec); + %muzzleVector = VectorScale(VectorNormalize(%muzzleVector), %this.projectileSpread *2); + %muzzleVector = VectorAdd(%muzzleVector, %vec); + } + else + { + // Weapon projectile doesn't have a spread factor so we fire it using + // the straight ahead aiming point of the gun + %muzzleVector = %obj.getMuzzleVector(%slot); + } + + // Add player's velocity + %muzzleVelocity = VectorAdd( + VectorScale(%muzzleVector, %this.projectile.muzzleVelocity), + VectorScale(%objectVelocity, %this.projectile.velInheritFactor)); + + // Create the projectile object + %p = new (%this.projectileType)() + { + dataBlock = %this.projectile; + initialVelocity = %muzzleVelocity; + initialPosition = %obj.getMuzzlePoint(%slot); + sourceObject = %obj; + sourceSlot = %slot; + client = %obj.client; + sourceClass = %obj.getClassName(); + }; + MissionCleanup.add(%p); + } +} + +// ---------------------------------------------------------------------------- +// A "generic" weaponimage onAltFire handler for most weapons. Can be +// overridden with an appropriate namespace method for any weapon that requires +// a custom firing solution. +// ---------------------------------------------------------------------------- + +function WeaponImage::onAltFire(%this, %obj, %slot) +{ + //echo("\c4WeaponImage::onAltFire("@%this.getName()@", "@%obj.client.nameBase@", "@%slot@")"); + + // Decrement inventory ammo. The image's ammo state is updated + // automatically by the ammo inventory hooks. + %obj.decInventory(%this.ammo, 1); + + // Get the player's velocity, we'll then add it to that of the projectile + %objectVelocity = %obj.getVelocity(); + + %numProjectiles = %this.altProjectileNum; + if (%numProjectiles == 0) + %numProjectiles = 1; + + for (%i = 0; %i < %numProjectiles; %i++) + { + if (%this.altProjectileSpread) + { + // We'll need to "skew" this projectile a little bit. We start by + // getting the straight ahead aiming point of the gun + %vec = %obj.getMuzzleVector(%slot); + + // Then we'll create a spread matrix by randomly generating x, y, and z + // points in a circle + %matrix = ""; + for(%i = 0; %i < 3; %i++) + %matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.altProjectileSpread @ " "; + %mat = MatrixCreateFromEuler(%matrix); + + // Which we'll use to alter the projectile's initial vector with + %muzzleVector = MatrixMulVector(%mat, %vec); + } + else + { + // Weapon projectile doesn't have a spread factor so we fire it using + // the straight ahead aiming point of the gun. + %muzzleVector = %obj.getMuzzleVector(%slot); + } + + // Add player's velocity + %muzzleVelocity = VectorAdd( + VectorScale(%muzzleVector, %this.altProjectile.muzzleVelocity), + VectorScale(%objectVelocity, %this.altProjectile.velInheritFactor)); + + // Create the projectile object + %p = new (%this.projectileType)() + { + dataBlock = %this.altProjectile; + initialVelocity = %muzzleVelocity; + initialPosition = %obj.getMuzzlePoint(%slot); + sourceObject = %obj; + sourceSlot = %slot; + client = %obj.client; + }; + MissionCleanup.add(%p); + } +} + +// ---------------------------------------------------------------------------- +// A "generic" weaponimage onWetFire handler for most weapons. Can be +// overridden with an appropriate namespace method for any weapon that requires +// a custom firing solution. +// ---------------------------------------------------------------------------- + +function WeaponImage::onWetFire(%this, %obj, %slot) +{ + //echo("\c4WeaponImage::onWetFire("@%this.getName()@", "@%obj.client.nameBase@", "@%slot@")"); + + // Decrement inventory ammo. The image's ammo state is updated + // automatically by the ammo inventory hooks. + %obj.decInventory(%this.ammo, 1); + + // Get the player's velocity, we'll then add it to that of the projectile + %objectVelocity = %obj.getVelocity(); + + %numProjectiles = %this.projectileNum; + if (%numProjectiles == 0) + %numProjectiles = 1; + + for (%i = 0; %i < %numProjectiles; %i++) + { + if (%this.wetProjectileSpread) + { + // We'll need to "skew" this projectile a little bit. We start by + // getting the straight ahead aiming point of the gun + %vec = %obj.getMuzzleVector(%slot); + + // Then we'll create a spread matrix by randomly generating x, y, and z + // points in a circle + %matrix = ""; + for(%j = 0; %j < 3; %j++) + %matrix = %matrix @ (getRandom() - 0.5) * 2 * 3.1415926 * %this.wetProjectileSpread @ " "; + %mat = MatrixCreateFromEuler(%matrix); + + // Which we'll use to alter the projectile's initial vector with + %muzzleVector = MatrixMulVector(%mat, %vec); + } + else + { + // Weapon projectile doesn't have a spread factor so we fire it using + // the straight ahead aiming point of the gun. + %muzzleVector = %obj.getMuzzleVector(%slot); + } + + // Add player's velocity + %muzzleVelocity = VectorAdd( + VectorScale(%muzzleVector, %this.wetProjectile.muzzleVelocity), + VectorScale(%objectVelocity, %this.wetProjectile.velInheritFactor)); + + // Create the projectile object + %p = new (%this.projectileType)() + { + dataBlock = %this.wetProjectile; + initialVelocity = %muzzleVelocity; + initialPosition = %obj.getMuzzlePoint(%slot); + sourceObject = %obj; + sourceSlot = %slot; + client = %obj.client; + }; + MissionCleanup.add(%p); + } +} + +//----------------------------------------------------------------------------- +// Clip Management +//----------------------------------------------------------------------------- + +function WeaponImage::onClipEmpty(%this, %obj, %slot) +{ + //echo("WeaponImage::onClipEmpty: " SPC %this SPC %obj SPC %slot); + + // Attempt to automatically reload. Schedule this so it occurs + // outside of the current state that called this method + %this.schedule(0, "reloadAmmoClip", %obj, %slot); +} + +function WeaponImage::reloadAmmoClip(%this, %obj, %slot) +{ + //echo("WeaponImage::reloadAmmoClip: " SPC %this SPC %obj SPC %slot); + + // Make sure we're indeed the currect image on the given slot + if (%this != %obj.getMountedImage(%slot)) + return; + + if ( %this.isField("clip") ) + { + if (%obj.getInventory(%this.clip) > 0) + { + %obj.decInventory(%this.clip, 1); + %obj.setInventory(%this.ammo, %this.ammo.maxInventory); + %obj.setImageAmmo(%slot, true); + } + else + { + %amountInPocket = %obj.getFieldValue( "remaining" @ %this.ammo.getName()); + if ( %amountInPocket ) + { + %obj.setFieldValue( "remaining" @ %this.ammo.getName(), 0); + %obj.setInventory( %this.ammo, %amountInPocket ); + %obj.setImageAmmo( %slot, true ); + } + } + + } +} + +function WeaponImage::clearAmmoClip( %this, %obj, %slot ) +{ + //echo("WeaponImage::clearAmmoClip: " SPC %this SPC %obj SPC %slot); + + // if we're not empty put the remaining bullets from the current clip + // in to the player's "pocket". + + if ( %this.isField( "clip" ) ) + { + // Commenting out this line will use a "hard clip" system, where + // A player will lose any ammo currently in the gun when reloading. + %pocketAmount = %this.stashSpareAmmo( %obj ); + + if ( %obj.getInventory( %this.clip ) > 0 || %pocketAmount != 0 ) + %obj.setImageAmmo(%slot, false); + } +} +function WeaponImage::stashSpareAmmo( %this, %player ) +{ + // If the amount in our pocket plus what we are about to add from the clip + // Is over a clip, add a clip to inventory and keep the remainder + // on the player + if (%player.getInventory( %this.ammo ) < %this.ammo.maxInventory ) + { + %nameOfAmmoField = "remaining" @ %this.ammo.getName(); + + %amountInPocket = %player.getFieldValue( %nameOfAmmoField ); + + %amountInGun = %player.getInventory( %this.ammo ); + + %combinedAmmo = %amountInGun + %amountInPocket; + + // Give the player another clip if the amount in our pocket + the + // Amount in our gun is over the size of a clip. + if ( %combinedAmmo >= %this.ammo.maxInventory ) + { + %player.setFieldValue( %nameOfAmmoField, %combinedAmmo - %this.ammo.maxInventory ); + %player.incInventory( %this.clip, 1 ); + } + else if ( %player.getInventory(%this.clip) > 0 )// Only put it back in our pocket if we have clips. + %player.setFieldValue( %nameOfAmmoField, %combinedAmmo ); + + return %player.getFieldValue( %nameOfAmmoField ); + + } + + return 0; + +} + +//----------------------------------------------------------------------------- +// Clip Class +//----------------------------------------------------------------------------- + +function AmmoClip::onPickup(%this, %obj, %shape, %amount) +{ + // The parent Item method performs the actual pickup. + if (Parent::onPickup(%this, %obj, %shape, %amount)) + serverPlay3D(AmmoPickupSound, %shape.getTransform()); + + // The clip inventory state has changed, we need to update the + // current mounted image using this clip to reflect the new state. + if ((%image = %shape.getMountedImage($WeaponSlot)) > 0) + { + // Check if this weapon uses the clip we just picked up and if + // there is no ammo. + if (%image.isField("clip") && %image.clip.getId() == %this.getId()) + { + %outOfAmmo = !%shape.getImageAmmo($WeaponSlot); + + %currentAmmo = %shape.getInventory(%image.ammo); + + if ( isObject( %image.clip ) ) + %amountInClips = %shape.getInventory(%image.clip); + + %amountInClips *= %image.ammo.maxInventory; + %amountInClips += %obj.getFieldValue( "remaining" @ %this.ammo.getName() ); + + %shape.client.setAmmoAmountHud(%currentAmmo, %amountInClips ); + + if (%outOfAmmo) + { + %image.onClipEmpty(%shape, $WeaponSlot); + } + } + } +} + +//----------------------------------------------------------------------------- +// Ammmo Class +//----------------------------------------------------------------------------- + +function Ammo::onPickup(%this, %obj, %shape, %amount) +{ + // The parent Item method performs the actual pickup. + if (Parent::onPickup(%this, %obj, %shape, %amount)) + serverPlay3D(AmmoPickupSound, %shape.getTransform()); +} + +function Ammo::onInventory(%this, %obj, %amount) +{ + // The ammo inventory state has changed, we need to update any + // mounted images using this ammo to reflect the new state. + for (%i = 0; %i < 8; %i++) + { + if ((%image = %obj.getMountedImage(%i)) > 0) + if (isObject(%image.ammo) && %image.ammo.getId() == %this.getId()) + { + %obj.setImageAmmo(%i, %amount != 0); + %currentAmmo = %obj.getInventory(%this); + + if (%obj.getClassname() $= "Player") + { + if ( isObject( %this.clip ) ) + { + %amountInClips = %obj.getInventory(%this.clip); + %amountInClips *= %this.maxInventory; + %amountInClips += %obj.getFieldValue( "remaining" @ %this.getName() ); + } + else //Is a single fire weapon, like the grenade launcher. + { + %amountInClips = %currentAmmo; + %currentAmmo = 1; + } + + if (%obj.client !$= "" && !%obj.isAiControlled) + %obj.client.setAmmoAmountHud(%currentAmmo, %amountInClips); + } + } + } +} + +// ---------------------------------------------------------------------------- +// Weapon cycling +// ---------------------------------------------------------------------------- + +function ShapeBase::clearWeaponCycle(%this) +{ + %this.totalCycledWeapons = 0; +} + +function ShapeBase::addToWeaponCycle(%this, %weapon) +{ + %this.cycleWeapon[%this.totalCycledWeapons++ - 1] = %weapon; +} + +function ShapeBase::cycleWeapon(%this, %direction) +{ + // Can't cycle what we don't have + if (%this.totalCycledWeapons == 0) + return; + + // Find out the index of the current weapon, if any (not all + // available weapons may be part of the cycle) + %currentIndex = -1; + if (%this.getMountedImage($WeaponSlot) != 0) + { + %curWeapon = %this.getMountedImage($WeaponSlot).item.getName(); + for (%i=0; %i<%this.totalCycledWeapons; %i++) + { + if (%this.cycleWeapon[%i] $= %curWeapon) + { + %currentIndex = %i; + break; + } + } + } + + // Get the next weapon index + %nextIndex = 0; + %dir = 1; + if (%currentIndex != -1) + { + if (%direction $= "prev") + { + %dir = -1; + %nextIndex = %currentIndex - 1; + if (%nextIndex < 0) + { + // Wrap around to the end + %nextIndex = %this.totalCycledWeapons - 1; + } + } + else + { + %nextIndex = %currentIndex + 1; + if (%nextIndex >= %this.totalCycledWeapons) + { + // Wrap back to the beginning + %nextIndex = 0; + } + } + } + + // We now need to check if the next index is a valid weapon. If not, + // then continue to cycle to the next weapon, in the appropriate direction, + // until one is found. If nothing is found, then do nothing. + %found = false; + for (%i=0; %i<%this.totalCycledWeapons; %i++) + { + %weapon = %this.cycleWeapon[%nextIndex]; + if (%weapon !$= "" && %this.hasInventory(%weapon) && %this.hasAmmo(%weapon)) + { + // We've found out weapon + %found = true; + break; + } + + %nextIndex = %nextIndex + %dir; + if (%nextIndex < 0) + { + %nextIndex = %this.totalCycledWeapons - 1; + } + else if (%nextIndex >= %this.totalCycledWeapons) + { + %nextIndex = 0; + } + } + + if (%found) + { + %this.use(%this.cycleWeapon[%nextIndex]); + } +} From 802f90bf696d244aca12c4361107d1277bc27742 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Tue, 8 Apr 2025 08:16:09 +0100 Subject: [PATCH 06/74] init commit initial commit of templated ParseProperty function TypePoint and TypeRect consoleSetTypes now use the new templated function --- Engine/source/console/propertyParsing.h | 29 +++++ Engine/source/math/mathTypes.cpp | 143 ++++++++++++++++-------- 2 files changed, 128 insertions(+), 44 deletions(-) diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h index f782df2b4..eab09118b 100644 --- a/Engine/source/console/propertyParsing.h +++ b/Engine/source/console/propertyParsing.h @@ -162,6 +162,35 @@ namespace PropertyInfo bool hex_print(String & string, const S8 & hex); bool default_print(String & result, SimObjectType * const & data); + + template + inline bool ParseProperty(char* str, T(&out)[count]) + { + + size_t index = 0; + char* tok = dStrtok(str, " \t"); + + while (tok && index < count) + { + if constexpr (std::is_same_v || std::is_same_v) + { + out[index++] = dAtoi(tok); + } + else if constexpr (std::is_same_v || std::is_same_v) + { + out[index++] = dAtof(tok); + } + else + { + AssertFatal(sizeof(T) == 0, "Unsupported type in PropertyInfo::ParseProperty"); + } + + tok = dStrtok(nullptr, " \t"); + } + + return index == count; + } + } // Default Scan/print definition diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index ef7616340..600bd7944 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -24,6 +24,7 @@ #include "console/consoleTypes.h" #include "console/console.h" #include "console/engineAPI.h" +#include "console/propertyParsing.h" #include "math/mPoint2.h" #include "math/mPoint3.h" #include "math/mMatrix.h" @@ -178,12 +179,20 @@ ConsoleGetType( TypePoint2I ) ConsoleSetType( TypePoint2I ) { - if(argc == 1) - dSscanf(argv[0], "%d %d", &((Point2I *) dptr)->x, &((Point2I *) dptr)->y); - else if(argc == 2) - *((Point2I *) dptr) = Point2I(dAtoi(argv[0]), dAtoi(argv[1])); - else - Con::printf("Point2I must be set as { x, y } or \"x y\""); + if (argc >= 1) { + S32 parsed[2]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point2I*)dptr) = Point2I(parsed[0], parsed[1]); + return; + } + } + + Con::printf("Point2I must be set as { x, y } or \"x y\""); } //----------------------------------------------------------------------------- @@ -203,12 +212,20 @@ ConsoleGetType( TypePoint2F ) ConsoleSetType( TypePoint2F ) { - if(argc == 1) - dSscanf(argv[0], "%g %g", &((Point2F *) dptr)->x, &((Point2F *) dptr)->y); - else if(argc == 2) - *((Point2F *) dptr) = Point2F(dAtof(argv[0]), dAtof(argv[1])); - else - Con::printf("Point2F must be set as { x, y } or \"x y\""); + if (argc >= 1) { + F32 parsed[2]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point2F*)dptr) = Point2F(parsed[0], parsed[1]); + return; + } + } + + Con::printf("Point2F must be set as { x, y } or \"x y\""); } //----------------------------------------------------------------------------- @@ -228,12 +245,20 @@ ConsoleGetType( TypePoint3I ) ConsoleSetType( TypePoint3I ) { - if(argc == 1) - dSscanf(argv[0], "%d %d %d", &((Point3I *) dptr)->x, &((Point3I *) dptr)->y, &((Point3I *) dptr)->z); - else if(argc == 3) - *((Point3I *) dptr) = Point3I(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2])); - else - Con::printf("Point3I must be set as { x, y, z } or \"x y z\""); + if (argc >= 1) { + S32 parsed[3]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point3I*)dptr) = Point3I(parsed[0], parsed[1], parsed[2]); + return; + } + } + + Con::printf("Point3I must be set as { x, y, z } or \"x y z\""); } //----------------------------------------------------------------------------- @@ -253,12 +278,20 @@ ConsoleGetType( TypePoint3F ) ConsoleSetType( TypePoint3F ) { - if(argc == 1) - dSscanf(argv[0], "%g %g %g", &((Point3F *) dptr)->x, &((Point3F *) dptr)->y, &((Point3F *) dptr)->z); - else if(argc == 3) - *((Point3F *) dptr) = Point3F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2])); - else - Con::printf("Point3F must be set as { x, y, z } or \"x y z\""); + if (argc >= 1) { + F32 parsed[3]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point3F*)dptr) = Point3F(parsed[0], parsed[1], parsed[2]); + return; + } + } + + Con::printf("Point3F must be set as { x, y, z } or \"x y z\""); } //----------------------------------------------------------------------------- @@ -278,12 +311,20 @@ ConsoleGetType( TypePoint4F ) ConsoleSetType( TypePoint4F ) { - if(argc == 1) - dSscanf(argv[0], "%g %g %g %g", &((Point4F *) dptr)->x, &((Point4F *) dptr)->y, &((Point4F *) dptr)->z, &((Point4F *) dptr)->w); - else if(argc == 4) - *((Point4F *) dptr) = Point4F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3])); - else - Con::printf("Point4F must be set as { x, y, z, w } or \"x y z w\""); + if (argc >= 1) { + F32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((Point4F*)dptr) = Point4F(parsed[0], parsed[1], parsed[2], parsed[3]); + return; + } + } + + Con::printf("Point4F must be set as { x, y, z, w } or \"x y z w\""); } //----------------------------------------------------------------------------- @@ -304,13 +345,20 @@ ConsoleGetType( TypeRectI ) ConsoleSetType( TypeRectI ) { - if(argc == 1) - dSscanf(argv[0], "%d %d %d %d", &((RectI *) dptr)->point.x, &((RectI *) dptr)->point.y, - &((RectI *) dptr)->extent.x, &((RectI *) dptr)->extent.y); - else if(argc == 4) - *((RectI *) dptr) = RectI(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2]), dAtoi(argv[3])); - else - Con::printf("RectI must be set as { x, y, w, h } or \"x y w h\""); + if (argc >= 1) { + S32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((RectI*)dptr) = RectI(parsed[0], parsed[1], parsed[2], parsed[3]); + return; + } + } + + Con::printf("RectI must be set as { x, y, w, h } or \"x y w h\""); } //----------------------------------------------------------------------------- @@ -331,13 +379,20 @@ ConsoleGetType( TypeRectF ) ConsoleSetType( TypeRectF ) { - if(argc == 1) - dSscanf(argv[0], "%g %g %g %g", &((RectF *) dptr)->point.x, &((RectF *) dptr)->point.y, - &((RectF *) dptr)->extent.x, &((RectF *) dptr)->extent.y); - else if(argc == 4) - *((RectF *) dptr) = RectF(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3])); - else - Con::printf("RectF must be set as { x, y, w, h } or \"x y w h\""); + if (argc >= 1) { + F32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + *((RectF*)dptr) = RectF(parsed[0], parsed[1], parsed[2], parsed[3]); + return; + } + } + + Con::printf("RectF must be set as { x, y, w, h } or \"x y w h\""); } //----------------------------------------------------------------------------- From 1cb2109d6ed8371557eedc1efe62ca171e91f7dd Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Tue, 8 Apr 2025 09:51:38 +0100 Subject: [PATCH 07/74] Update mathTypes.cpp update matrix/ang types to use the new function change all print's to warn's --- Engine/source/math/mathTypes.cpp | 202 +++++++++++++++++-------------- 1 file changed, 108 insertions(+), 94 deletions(-) diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index 600bd7944..5f01c444a 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -179,7 +179,8 @@ ConsoleGetType( TypePoint2I ) ConsoleSetType( TypePoint2I ) { - if (argc >= 1) { + if (argc >= 1) + { S32 parsed[2]; // Combine argv into a single space-separated string if argc > 1 char buffer[256] = { 0 }; @@ -192,7 +193,7 @@ ConsoleSetType( TypePoint2I ) } } - Con::printf("Point2I must be set as { x, y } or \"x y\""); + Con::warnf("Point2I must be set as { x, y } or \"x y\""); } //----------------------------------------------------------------------------- @@ -212,7 +213,8 @@ ConsoleGetType( TypePoint2F ) ConsoleSetType( TypePoint2F ) { - if (argc >= 1) { + if (argc >= 1) + { F32 parsed[2]; // Combine argv into a single space-separated string if argc > 1 char buffer[256] = { 0 }; @@ -225,7 +227,7 @@ ConsoleSetType( TypePoint2F ) } } - Con::printf("Point2F must be set as { x, y } or \"x y\""); + Con::warnf("Point2F must be set as { x, y } or \"x y\""); } //----------------------------------------------------------------------------- @@ -245,7 +247,8 @@ ConsoleGetType( TypePoint3I ) ConsoleSetType( TypePoint3I ) { - if (argc >= 1) { + if (argc >= 1) + { S32 parsed[3]; // Combine argv into a single space-separated string if argc > 1 char buffer[256] = { 0 }; @@ -258,7 +261,7 @@ ConsoleSetType( TypePoint3I ) } } - Con::printf("Point3I must be set as { x, y, z } or \"x y z\""); + Con::warnf("Point3I must be set as { x, y, z } or \"x y z\""); } //----------------------------------------------------------------------------- @@ -278,7 +281,8 @@ ConsoleGetType( TypePoint3F ) ConsoleSetType( TypePoint3F ) { - if (argc >= 1) { + if (argc >= 1) + { F32 parsed[3]; // Combine argv into a single space-separated string if argc > 1 char buffer[256] = { 0 }; @@ -291,7 +295,7 @@ ConsoleSetType( TypePoint3F ) } } - Con::printf("Point3F must be set as { x, y, z } or \"x y z\""); + Con::warnf("Point3F must be set as { x, y, z } or \"x y z\""); } //----------------------------------------------------------------------------- @@ -311,7 +315,8 @@ ConsoleGetType( TypePoint4F ) ConsoleSetType( TypePoint4F ) { - if (argc >= 1) { + if (argc >= 1) + { F32 parsed[4]; // Combine argv into a single space-separated string if argc > 1 char buffer[256] = { 0 }; @@ -324,7 +329,7 @@ ConsoleSetType( TypePoint4F ) } } - Con::printf("Point4F must be set as { x, y, z, w } or \"x y z w\""); + Con::warnf("Point4F must be set as { x, y, z, w } or \"x y z w\""); } //----------------------------------------------------------------------------- @@ -345,7 +350,8 @@ ConsoleGetType( TypeRectI ) ConsoleSetType( TypeRectI ) { - if (argc >= 1) { + if (argc >= 1) + { S32 parsed[4]; // Combine argv into a single space-separated string if argc > 1 char buffer[256] = { 0 }; @@ -358,7 +364,7 @@ ConsoleSetType( TypeRectI ) } } - Con::printf("RectI must be set as { x, y, w, h } or \"x y w h\""); + Con::warnf("RectI must be set as { x, y, w, h } or \"x y w h\""); } //----------------------------------------------------------------------------- @@ -379,7 +385,8 @@ ConsoleGetType( TypeRectF ) ConsoleSetType( TypeRectF ) { - if (argc >= 1) { + if (argc >= 1) + { F32 parsed[4]; // Combine argv into a single space-separated string if argc > 1 char buffer[256] = { 0 }; @@ -392,7 +399,7 @@ ConsoleSetType( TypeRectF ) } } - Con::printf("RectF must be set as { x, y, w, h } or \"x y w h\""); + Con::warnf("RectF must be set as { x, y, w, h } or \"x y w h\""); } //----------------------------------------------------------------------------- @@ -421,21 +428,24 @@ ConsoleGetType( TypeMatrixF ) ConsoleSetType( TypeMatrixF ) { - if( argc != 1 ) + if (argc == 1) { - Con::errorf( "MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\"" ); - return; - } - - Point3F col0, col1, col2; - dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g %g %g", - &col0.x, &col0.y, &col0.z, &col1.x, &col1.y, &col1.z, &col2.x, &col2.y, &col2.z ); + F32 parsed[9]; - MatrixF* mat = ( MatrixF* ) dptr; - - mat->setColumn( 0, col0 ); - mat->setColumn( 1, col1 ); - mat->setColumn( 2, col2 ); + char* buffer = new char[dStrlen(argv[0])]; + dStrcpy(buffer, argv[0], sizeof(buffer)); + + if (PropertyInfo::ParseProperty(buffer, parsed)) { + MatrixF* mat = (MatrixF*)dptr; + + mat->setColumn(0, Point3F(parsed[0], parsed[1], parsed[2])); + mat->setColumn(1, Point3F(parsed[3], parsed[4], parsed[5])); + mat->setColumn(2, Point3F(parsed[6], parsed[7], parsed[8])); + return; + } + } + + Con::warnf("MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\""); } //----------------------------------------------------------------------------- @@ -457,20 +467,25 @@ ConsoleGetType( TypeMatrixPosition ) ConsoleSetType( TypeMatrixPosition ) { - F32 *col = ((F32 *) dptr) + 3; - if (argc == 1) + if (argc >= 1) { - col[0] = col[4] = col[8] = 0.f; - col[12] = 1.f; - dSscanf(argv[0], "%g %g %g %g", &col[0], &col[4], &col[8], &col[12]); + F32 parsed[4] = { 1.0f }; // default all to 1.0f + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); + // we dont want to hard fail based on the count. + // this will allow any number of properties to be set. + PropertyInfo::ParseProperty(buffer, parsed); + { + Point4F temp(parsed[0], parsed[1], parsed[2], parsed[3]); + MatrixF* mat = (MatrixF*)dptr; + mat->setColumn(3, temp); + return; + } } - else if (argc <= 4) - { - for (S32 i = 0; i < argc; i++) - col[i << 2] = dAtof(argv[i]); - } - else - Con::printf("Matrix position must be set as { x, y, z, w } or \"x y z w\""); + + Con::warnf("Matrix position must be set as { x, y, z, w } or \"x y z w\""); + } //----------------------------------------------------------------------------- @@ -490,32 +505,29 @@ ConsoleGetType( TypeMatrixRotation ) ConsoleSetType( TypeMatrixRotation ) { - // DMM: Note that this will ONLY SET the ULeft 3x3 submatrix. - // - AngAxisF aa(Point3F(0,0,0),0); - if (argc == 1) + if (argc >= 1) { - dSscanf(argv[0], "%g %g %g %g", &aa.axis.x, &aa.axis.y, &aa.axis.z, &aa.angle); - aa.angle = mDegToRad(aa.angle); - } - else if (argc == 4) - { - for (S32 i = 0; i < argc; i++) - ((F32*)&aa)[i] = dAtof(argv[i]); - aa.angle = mDegToRad(aa.angle); - } - else - Con::printf("Matrix rotation must be set as { x, y, z, angle } or \"x y z angle\""); + F32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); - // - MatrixF temp; - aa.setMatrix(&temp); + if (PropertyInfo::ParseProperty(buffer, parsed)) + { + AngAxisF aa(Point3F(parsed[0], parsed[1], parsed[2]), mDegToRad(parsed[3])); + MatrixF temp; + aa.setMatrix(&temp); - F32* pDst = *(MatrixF *)dptr; - const F32* pSrc = temp; - for (U32 i = 0; i < 3; i++) - for (U32 j = 0; j < 3; j++) - pDst[i*4 + j] = pSrc[i*4 + j]; + F32* pDst = *(MatrixF*)dptr; + const F32* pSrc = temp; + for (U32 i = 0; i < 3; i++) + for (U32 j = 0; j < 3; j++) + pDst[i * 4 + j] = pSrc[i * 4 + j]; + return; + } + } + + Con::warnf("Matrix rotation must be set as { x, y, z, angle } or \"x y z angle\""); } //----------------------------------------------------------------------------- @@ -535,22 +547,22 @@ ConsoleGetType( TypeAngAxisF ) ConsoleSetType( TypeAngAxisF ) { - // DMM: Note that this will ONLY SET the ULeft 3x3 submatrix. - // - AngAxisF* aa = ( AngAxisF* ) dptr; - if (argc == 1) + if (argc >= 1) { - dSscanf(argv[0], "%g %g %g %g", &aa->axis.x, &aa->axis.y, &aa->axis.z, &aa->angle); - aa->angle = mDegToRad(aa->angle); + F32 parsed[4]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); + + if(PropertyInfo::ParseProperty(buffer, parsed)) + { + AngAxisF* aa = (AngAxisF*)dptr; + aa->set(Point3F(parsed[0], parsed[1], parsed[2]), mDegToRad(parsed[3])); + return; + } } - else if (argc == 4) - { - for (S32 i = 0; i < argc; i++) - ((F32*)&aa)[i] = dAtof(argv[i]); - aa->angle = mDegToRad(aa->angle); - } - else - Con::printf("AngAxisF must be set as { x, y, z, angle } or \"x y z angle\""); + + Con::warnf("AngAxisF must be set as { x, y, z, angle } or \"x y z angle\""); } @@ -576,27 +588,29 @@ ConsoleGetType( TypeTransformF ) ConsoleSetType( TypeTransformF ) { - TransformF* aa = ( TransformF* ) dptr; - if( argc == 1 ) + if(argc >= 1) { - U32 count = dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g", - &aa->mPosition.x, &aa->mPosition.y, &aa->mPosition.z, - &aa->mOrientation.axis.x, &aa->mOrientation.axis.y, &aa->mOrientation.axis.z, &aa->mOrientation.angle ); + F32 parsed[7]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); - aa->mHasRotation = ( count == 7 ); + if (PropertyInfo::ParseProperty(buffer, parsed)) + { + TransformF* aa = (TransformF*)dptr; + aa->mPosition.x = parsed[0]; + aa->mPosition.y = parsed[1]; + aa->mPosition.z = parsed[2]; + aa->mOrientation.axis.x = parsed[3]; + aa->mOrientation.axis.y = parsed[4]; + aa->mOrientation.axis.z = parsed[5]; + aa->mOrientation.angle = parsed[6]; + aa->mHasRotation = true; + return; + } } - else if( argc == 7 ) - { - aa->mPosition.x = dAtof( argv[ 0 ] ); - aa->mPosition.y = dAtof( argv[ 1 ] ); - aa->mPosition.z = dAtof( argv[ 2 ] ); - aa->mOrientation.axis.x = dAtof( argv[ 3 ] ); - aa->mOrientation.axis.y = dAtof( argv[ 4 ] ); - aa->mOrientation.axis.z = dAtof( argv[ 5 ] ); - aa->mOrientation.angle = dAtof( argv[ 6 ] ); - } - else - Con::errorf( "TransformF must be set as { px, py, pz, x, y, z, angle } or \"px py pz x y z angle\""); + + Con::warnf("TransformF must be set as { px, py, pz, x, y, z, angle } or \"px py pz x y z angle\""); } From f30ff6734e589772412636cf076ea8dd1f648104 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Tue, 8 Apr 2025 10:13:48 +0100 Subject: [PATCH 08/74] Update mathTypes.cpp mathtypes.cpp ConsoleSetTypes now converted --- Engine/source/math/mathTypes.cpp | 103 ++++++++++++++++++------------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index 5f01c444a..584a011c9 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -636,19 +636,27 @@ ConsoleGetType( TypeBox3F ) ConsoleSetType( TypeBox3F ) { - Box3F* pDst = (Box3F*)dptr; + if (argc >= 1) + { + F32 parsed[6]; + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); - if (argc == 1) - { - U32 args = dSscanf(argv[0], "%g %g %g %g %g %g", - &pDst->minExtents.x, &pDst->minExtents.y, &pDst->minExtents.z, - &pDst->maxExtents.x, &pDst->maxExtents.y, &pDst->maxExtents.z); - AssertWarn(args == 6, "Warning, box probably not read properly"); - } - else - { - Con::printf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\""); + if (PropertyInfo::ParseProperty(buffer, parsed)) + { + Box3F* pDst = (Box3F*)dptr; + pDst->minExtents.x = parsed[0]; + pDst->minExtents.y = parsed[1]; + pDst->minExtents.z = parsed[2]; + pDst->maxExtents.x = parsed[3]; + pDst->maxExtents.y = parsed[4]; + pDst->maxExtents.z = parsed[5]; + return; + } } + + Con::warnf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\""); } @@ -672,19 +680,26 @@ ConsoleGetType( TypeEaseF ) ConsoleSetType( TypeEaseF ) { - EaseF* pDst = (EaseF*)dptr; + if (argc >= 1) + { + F32 parsed[4]; + parsed[2] = -1.0f; + parsed[3] = -1.0f; - // defaults... - pDst->mParam[0] = -1.0f; - pDst->mParam[1] = -1.0f; - if (argc == 1) { - U32 args = dSscanf(argv[0], "%d %d %f %f", // the two params are optional and assumed -1 if not present... - &pDst->mDir, &pDst->mType, &pDst->mParam[0],&pDst->mParam[1]); - if( args < 2 ) - Con::warnf( "Warning, EaseF probably not read properly" ); - } else { - Con::printf("EaseF must be set as \"dir type [param0 param1]\""); + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + + dStrncpy(buffer, *argv, sizeof(buffer)); + + // same as matrix do not hard fail based on count! + PropertyInfo::ParseProperty(buffer, parsed); + { + ((EaseF*)dptr)->set(mRound(parsed[0]), mRound(parsed[1]), parsed[2], parsed[3]); + return; + } } + + Con::warnf("EaseF must be set as \"dir type [param0 param1]\""); } //----------------------------------------------------------------------------- @@ -715,34 +730,36 @@ ConsoleGetType(TypeRotationF) ConsoleSetType(TypeRotationF) { - if (argc == 1) + if (argc >= 1) { - U32 elements = StringUnit::getUnitCount(argv[0], " \t\n"); + // Combine argv into a single space-separated string if argc > 1 + char buffer[256] = { 0 }; + dStrncpy(buffer, *argv, sizeof(buffer)); + + U32 elements = StringUnit::getUnitCount(buffer, " \t\n"); if (elements == 3) { - EulerF in; - dSscanf(argv[0], "%g %g %g", &in.x, &in.y, &in.z); - ((RotationF *)dptr)->set(in, RotationF::Degrees); + F32 parsed[3]; + if(PropertyInfo::ParseProperty(buffer, parsed)) + { + EulerF in(parsed[0], parsed[1], parsed[2]); + ((RotationF*)dptr)->set(in, RotationF::Degrees); + return; + } } - else + else if (elements == 4) { - AngAxisF in; - dSscanf(argv[0], "%g %g %g %g", &in.axis.x, &in.axis.y, &in.axis.z, &in.angle); - ((RotationF *)dptr)->set(in, RotationF::Degrees); + F32 parsed[4]; + if (PropertyInfo::ParseProperty(buffer, parsed)) + { + AngAxisF in(Point3F(parsed[0], parsed[1], parsed[2]), parsed[3]); + ((RotationF*)dptr)->set(in, RotationF::Degrees); + return; + } } } - else if (argc == 3) - { - EulerF in(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2])); - ((RotationF *)dptr)->set(in, RotationF::Degrees); - } - else if (argc == 4) - { - AngAxisF in(Point3F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2])), dAtof(argv[3])); - ((RotationF *)dptr)->set(in, RotationF::Degrees); - } - else - Con::printf("RotationF must be set as { x, y, z, w } or \"x y z w\""); + + Con::warnf("RotationF must be set as { x, y, z, w } or \"x y z w\""); } //----------------------------------------------------------------------------- From b886cbb527effa2631603faae3054fb5b6fea817 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 8 Apr 2025 16:09:54 -0500 Subject: [PATCH 09/74] on balance we'll want to round instead of truncate --- Engine/source/console/propertyParsing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h index eab09118b..8bffe59b8 100644 --- a/Engine/source/console/propertyParsing.h +++ b/Engine/source/console/propertyParsing.h @@ -174,7 +174,7 @@ namespace PropertyInfo { if constexpr (std::is_same_v || std::is_same_v) { - out[index++] = dAtoi(tok); + out[index++] = mRound(dAtof(tok)); } else if constexpr (std::is_same_v || std::is_same_v) { From d0d2e1f3438fd272a72bc21cd3b60115480c91d0 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 8 Apr 2025 18:30:06 -0500 Subject: [PATCH 10/74] handle relfecting the health of the controlled object players and vehicles can both be the controlobject, or you can control a vehicle *through* a player. set the health bar/text controls to reflect that state --- Engine/source/T3D/fps/guiHealthBarHud.cpp | 5 +++++ Engine/source/T3D/fps/guiHealthTextHud.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/fps/guiHealthBarHud.cpp b/Engine/source/T3D/fps/guiHealthBarHud.cpp index 602d242b1..79b420e3a 100644 --- a/Engine/source/T3D/fps/guiHealthBarHud.cpp +++ b/Engine/source/T3D/fps/guiHealthBarHud.cpp @@ -147,6 +147,11 @@ void GuiHealthBarHud::onRender(Point2I offset, const RectI &updateRect) if (!conn) return; ShapeBase* control = dynamic_cast(conn->getControlObject()); + + //cover the case of a connection controling an object in turn controlling another + if (control && control->getControlObject()) + control = control->getControlObject(); + if (!control || !(control->getTypeMask() & (PlayerObjectType | VehicleObjectType))) return; diff --git a/Engine/source/T3D/fps/guiHealthTextHud.cpp b/Engine/source/T3D/fps/guiHealthTextHud.cpp index 1c202906c..faa798253 100644 --- a/Engine/source/T3D/fps/guiHealthTextHud.cpp +++ b/Engine/source/T3D/fps/guiHealthTextHud.cpp @@ -148,7 +148,12 @@ void GuiHealthTextHud::onRender(Point2I offset, const RectI &updateRect) GameConnection* conn = GameConnection::getConnectionToServer(); if (!conn) return; - ShapeBase* control = dynamic_cast(conn->getControlObject()); + ShapeBase* control = dynamic_cast(conn->getControlObject()); + + //cover the case of a connection controling an object in turn controlling another + if (control && control->getControlObject()) + control = control->getControlObject(); + if (!control || !(control->getTypeMask() & (PlayerObjectType | VehicleObjectType))) return; From 9007aa953250866b786b7ec85c4f38e14eaf1b52 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 8 Apr 2025 19:50:47 -0500 Subject: [PATCH 11/74] basic vehicle eject onDestroyed code leverages Parent to also call the rest of the deletions also fix setdamagedirection for players driving vehicles instead of either on thier own --- .../game/data/DamageModel/DamageModel.tscript | 1 + .../DamageModel/scripts/server/player.tscript | 2 +- .../scripts/server/shapeBase.tscript | 12 +- .../scripts/server/vehicle.tscript | 134 ++++++++++++++++++ 4 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 Templates/BaseGame/game/data/DamageModel/scripts/server/vehicle.tscript diff --git a/Templates/BaseGame/game/data/DamageModel/DamageModel.tscript b/Templates/BaseGame/game/data/DamageModel/DamageModel.tscript index 7e29f7d9e..3268a5b9e 100644 --- a/Templates/BaseGame/game/data/DamageModel/DamageModel.tscript +++ b/Templates/BaseGame/game/data/DamageModel/DamageModel.tscript @@ -21,6 +21,7 @@ function DamageModel::onCreateGameServer(%this) %this.queueExec("./scripts/server/projectile"); %this.queueExec("./scripts/server/weapon"); %this.queueExec("./scripts/server/shapeBase"); + %this.queueExec("./scripts/server/vehicle"); %this.queueExec("./scripts/server/player"); } diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/player.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/player.tscript index 817ae85c2..b874c9f5c 100644 --- a/Templates/BaseGame/game/data/DamageModel/scripts/server/player.tscript +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/player.tscript @@ -10,7 +10,7 @@ function PlayerData::damage(%this, %obj, %sourceObject, %position, %damage, %dam %rootObj.applyDamage(%damage); %this.onDamage(%rootObj, %damage); - %this.setDamageDirection(%obj, %sourceObject, %position); + %this.setDamageDirection(%rootObj, %sourceObject, %position); // Deal with client callbacks here because we don't have this // information in the onDamage or onDisable methods diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript index b1aa67448..336f41861 100644 --- a/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript @@ -128,7 +128,9 @@ function ShapeBaseData::onAdd(%this, %obj) function ShapeBaseData::setDamageDirection(%this, %obj, %sourceObject, %damagePos) { - if (!%obj.client) return; + %client = (%obj.client) ? %obj.client : %obj.getControllingClient(); + if (!%client) return; + if (%damagePos $= "" && isObject(%sourceObject)) { if (%sourceObject.isField(initialPosition)) @@ -153,7 +155,7 @@ function ShapeBaseData::setDamageDirection(%this, %obj, %sourceObject, %damagePo // Rotate damage vector into object space %damageVec = VectorSub(%damagePos, %obj.getWorldBoxCenter()); %damageVec = VectorNormalize(%damageVec); - %damageVec = MatrixMulVector(%obj.client.getCameraObject().getInverseTransform(), %damageVec); + %damageVec = MatrixMulVector(%client.getCameraObject().getInverseTransform(), %damageVec); // Determine largest component of damage vector to get direction %vecComponents = -%damageVec.x SPC %damageVec.x SPC -%damageVec.y SPC %damageVec.y SPC -%damageVec.z SPC %damageVec.z; @@ -169,7 +171,7 @@ function ShapeBaseData::setDamageDirection(%this, %obj, %sourceObject, %damagePo %damageDir = getWord(%vecDirections, %i); } } - commandToClient(%obj.client, 'setDamageDirection', %damageDir); + commandToClient(%client, 'setDamageDirection', %damageDir); } @@ -280,6 +282,8 @@ function ShapeBaseData::onDestroyed(%this, %obj, %state) function ShapeBaseData::onRemove(%this, %obj) { - Parent::onRemove(%this, %obj); + if (isMethod(Parent, "onRemove")) + Parent::onRemove(%this, %obj); + deleteMountchain(%obj); } \ No newline at end of file diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/vehicle.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/vehicle.tscript new file mode 100644 index 000000000..fec8c9ecd --- /dev/null +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/vehicle.tscript @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Parenting is in place for WheeledVehicleData to VehicleData. This should +// make it easier for people to simply drop in new (generic) vehicles. All that +// the user needs to create is a set of datablocks for the new wheeled vehicle +// to use. This means that no (or little) scripting should be necessary. + +// Special, or unique vehicles however will still require some scripting. They +// may need to override the onAdd() function in order to mount weapons, +// differing tires/springs, etc., almost everything else is taken care of in the +// WheeledVehicleData and VehicleData methods. This helps us by not having to +// duplicate the same code for every new vehicle. + +// In theory this would work for HoverVehicles and FlyingVehicles also, but +// hasn't been tested or fully implemented for those classes -- yet. + +function VehicleData::onAdd(%this, %obj) +{ + %obj.setRechargeRate(%this.rechargeRate); + %obj.setEnergyLevel(%this.MaxEnergy); + %obj.setRepairRate(0); + + if (%obj.mountable || %obj.mountable $= "") + %this.isMountable(%obj, true); + else + %this.isMountable(%obj, false); + + if (%this.nameTag !$= "") + %obj.setShapeName(%this.nameTag); +} + +function VehicleData::onDestroyed(%this, %obj, %state) +{ + //echo("\c4VehicleData::onRemove("@ %this.getName() @", "@ %obj.getClassName() @")"); + // if there are passengers/driver, kick them out + if (!%this.killPassengers) + { + for(%i = 0; %i < %obj.getMountedObjectCount(); %i++) + { + if (%obj.getMountedObject(%i)) + { + %passenger = %obj.getMountedObject(%i); + if (%passenger.isMemberOfClass("Player")) + %passenger.getDataBlock().doDismount(%passenger, true); + } + } + } + Parent::onDestroyed(%this, %obj, %state); +} + +// ---------------------------------------------------------------------------- +// Vehicle player mounting and dismounting +// ---------------------------------------------------------------------------- + +function VehicleData::isMountable(%this, %obj, %val) +{ + %obj.mountable = %val; +} + +function VehicleData::mountPlayer(%this, %vehicle, %player) +{ + //echo("\c4VehicleData::mountPlayer("@ %this.getName() @", "@ %vehicle @", "@ %player.client.nameBase @")"); + + if (isObject(%vehicle) && %vehicle.getDamageState() !$= "Destroyed") + { + %player.startFade(1000, 0, true); + %this.schedule(1000, "setMountVehicle", %vehicle, %player); + %player.schedule(1500, "startFade", 1000, 0, false); + } +} + +function VehicleData::setMountVehicle(%this, %vehicle, %player) +{ + //echo("\c4VehicleData::setMountVehicle("@ %this.getName() @", "@ %vehicle @", "@ %player.client.nameBase @")"); + + if (isObject(%vehicle) && %vehicle.getDamageState() !$= "Destroyed") + { + %node = %this.findEmptySeat(%vehicle, %player); + if (%node >= 0) + { + //echo("\c4Mount Node: "@ %node); + %vehicle.mountObject(%player, %node); + //%player.playAudio(0, MountVehicleSound); + %player.mVehicle = %vehicle; + } + } +} + +function VehicleData::findEmptySeat(%this, %vehicle, %player) +{ + //echo("\c4This vehicle has "@ %this.numMountPoints @" mount points."); + + for (%i = 0; %i < %this.numMountPoints; %i++) + { + %node = %vehicle.getMountNodeObject(%i); + if (%node == 0) + return %i; + } + return -1; +} + +function VehicleData::switchSeats(%this, %vehicle, %player) +{ + for (%i = 0; %i < %this.numMountPoints; %i++) + { + %node = %vehicle.getMountNodeObject(%i); + if (%node == %player || %node > 0) + continue; + + if (%node == 0) + return %i; + } + return -1; +} From 7c3fbfc9d8b34005c24315154c608acdcfbedba9 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Wed, 9 Apr 2025 15:14:46 +0100 Subject: [PATCH 12/74] add getter for console get types templated function for getting data from a field --- Engine/source/console/propertyParsing.h | 35 ++++++ Engine/source/math/mathTypes.cpp | 138 +++++++++--------------- 2 files changed, 85 insertions(+), 88 deletions(-) diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h index 8bffe59b8..2608077c6 100644 --- a/Engine/source/console/propertyParsing.h +++ b/Engine/source/console/propertyParsing.h @@ -163,6 +163,41 @@ namespace PropertyInfo bool default_print(String & result, SimObjectType * const & data); + template + const char* FormatProperty(const void* dataPtr) + { + static const U32 bufSize = 256; + char* buffer = Con::getReturnBuffer(bufSize); + FormatProperty(dataPtr, buffer, bufSize); + return buffer; + } + + template + char* FormatProperty(const void* dataPtr, char* buffer, U32 bufSize) + { + const T* values = reinterpret_cast(dataPtr); + char* ptr = buffer; + + for (size_t i = 0; i < count; ++i) + { + S32 written = 0; + + if constexpr (std::is_same_v || std::is_same_v) + written = dSprintf(ptr, bufSize - (ptr - buffer), "%d", values[i]); + else if constexpr (std::is_same_v || std::is_same_v) + written = dSprintf(ptr, bufSize - (ptr - buffer), "%g", values[i]); + else + AssertFatal(sizeof(T) == 0, "Unsupported type in FormatProperty"); + + ptr += written; + if (i < count - 1) + *ptr++ = ' '; + } + + *ptr = '\0'; + return ptr; // return end of written string for chaining + } + template inline bool ParseProperty(char* str, T(&out)[count]) { diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index 584a011c9..c9b36a0e1 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -170,11 +170,8 @@ ImplementConsoleTypeCasters( TypePoint2I, Point2I ) ConsoleGetType( TypePoint2I ) { - Point2I *pt = (Point2I *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%d %d", pt->x, pt->y); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint2I ) @@ -204,11 +201,8 @@ ImplementConsoleTypeCasters( TypePoint2F, Point2F ) ConsoleGetType( TypePoint2F ) { - Point2F *pt = (Point2F *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g", pt->x, pt->y); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint2F ) @@ -238,11 +232,8 @@ ImplementConsoleTypeCasters(TypePoint3I, Point3I) ConsoleGetType( TypePoint3I ) { - Point3I *pt = (Point3I *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%d %d %d", pt->x, pt->y, pt->z); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint3I ) @@ -272,11 +263,8 @@ ImplementConsoleTypeCasters(TypePoint3F, Point3F) ConsoleGetType( TypePoint3F ) { - Point3F *pt = (Point3F *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g %g", pt->x, pt->y, pt->z); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint3F ) @@ -306,11 +294,8 @@ ImplementConsoleTypeCasters( TypePoint4F, Point4F ) ConsoleGetType( TypePoint4F ) { - Point4F *pt = (Point4F *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g %g %g", pt->x, pt->y, pt->z, pt->w); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypePoint4F ) @@ -340,12 +325,8 @@ ImplementConsoleTypeCasters( TypeRectI, RectI ) ConsoleGetType( TypeRectI ) { - RectI *rect = (RectI *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%d %d %d %d", rect->point.x, rect->point.y, - rect->extent.x, rect->extent.y); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypeRectI ) @@ -375,12 +356,8 @@ ImplementConsoleTypeCasters( TypeRectF, RectF ) ConsoleGetType( TypeRectF ) { - RectF *rect = (RectF *) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g %g %g", rect->point.x, rect->point.y, - rect->extent.x, rect->extent.y); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypeRectF ) @@ -413,17 +390,18 @@ ImplementConsoleTypeCasters( TypeMatrixF, MatrixF ) ConsoleGetType( TypeMatrixF ) { - MatrixF* mat = ( MatrixF* ) dptr; - - Point3F col0, col1, col2; - mat->getColumn(0, &col0); - mat->getColumn(1, &col1); - mat->getColumn(2, &col2); static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer,bufSize,"%g %g %g %g %g %g %g %g %g", - col0.x, col0.y, col0.z, col1.x, col1.y, col1.z, col2.x, col2.y, col2.z); - return returnBuffer; + char* buffer = Con::getReturnBuffer(bufSize); + + F32* mat = (F32*)dptr; + buffer = PropertyInfo::FormatProperty(mat + 0, buffer, bufSize); + *buffer++ = ' '; + buffer = PropertyInfo::FormatProperty(mat + 4, buffer, bufSize); + *buffer++ = ' '; + buffer = PropertyInfo::FormatProperty(mat + 8, buffer, bufSize); + *buffer = '\0'; // null-terminate just in case + + return buffer; } ConsoleSetType( TypeMatrixF ) @@ -455,14 +433,9 @@ ConsoleMappedType(MatrixPosition, TypeMatrixPosition, Point3F, MatrixF, "") ConsoleGetType( TypeMatrixPosition ) { - F32 *col = (F32 *) dptr + 3; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - if(col[12] == 1.f) - dSprintf(returnBuffer, bufSize, "%g %g %g", col[0], col[4], col[8]); - else - dSprintf(returnBuffer, bufSize, "%g %g %g %g", col[0], col[4], col[8], col[12]); - return returnBuffer; + F32* mat = (F32*)dptr; + const char* buff = PropertyInfo::FormatProperty(mat + 8); + return buff; } ConsoleSetType( TypeMatrixPosition ) @@ -495,12 +468,11 @@ ConsoleMappedType(MatrixRotation, TypeMatrixRotation, AngAxisF, MatrixF, "") ConsoleGetType( TypeMatrixRotation ) { - AngAxisF aa(*(MatrixF *) dptr); + AngAxisF aa(*(MatrixF*)dptr); aa.axis.normalize(); - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa.axis.x,aa.axis.y,aa.axis.z,mRadToDeg(aa.angle)); - return returnBuffer; + aa.angle = mRadToDeg(aa.angle); + const char* buff = PropertyInfo::FormatProperty(&aa); + return buff; } ConsoleSetType( TypeMatrixRotation ) @@ -539,10 +511,9 @@ ImplementConsoleTypeCasters( TypeAngAxisF, AngAxisF ) ConsoleGetType( TypeAngAxisF ) { AngAxisF* aa = ( AngAxisF* ) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa->axis.x,aa->axis.y,aa->axis.z,mRadToDeg(aa->angle)); - return returnBuffer; + aa->angle = mRadToDeg(aa->angle); + const char* buff = PropertyInfo::FormatProperty(aa); + return buff; } ConsoleSetType( TypeAngAxisF ) @@ -577,13 +548,8 @@ ImplementConsoleTypeCasters( TypeTransformF, TransformF ) ConsoleGetType( TypeTransformF ) { - TransformF* aa = ( TransformF* ) dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf( returnBuffer, bufSize, "%g %g %g %g %g %g %g", - aa->mPosition.x, aa->mPosition.y, aa->mPosition.z, - aa->mOrientation.axis.x, aa->mOrientation.axis.y, aa->mOrientation.axis.z, aa->mOrientation.angle ); - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypeTransformF ) @@ -623,15 +589,8 @@ ImplementConsoleTypeCasters( TypeBox3F, Box3F ) ConsoleGetType( TypeBox3F ) { - const Box3F* pBox = (const Box3F*)dptr; - - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%g %g %g %g %g %g", - pBox->minExtents.x, pBox->minExtents.y, pBox->minExtents.z, - pBox->maxExtents.x, pBox->maxExtents.y, pBox->maxExtents.z); - - return returnBuffer; + const char* buff = PropertyInfo::FormatProperty(dptr); + return buff; } ConsoleSetType( TypeBox3F ) @@ -668,14 +627,16 @@ ImplementConsoleTypeCasters( TypeEaseF, EaseF ) ConsoleGetType( TypeEaseF ) { - const EaseF* pEase = (const EaseF*)dptr; - static const U32 bufSize = 256; - char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, bufSize, "%d %d %g %g", - pEase->mDir, pEase->mType, pEase->mParam[0], pEase->mParam[1]); + char* buffer = Con::getReturnBuffer(bufSize); - return returnBuffer; + EaseF* pEase = (EaseF*)dptr; + buffer = PropertyInfo::FormatProperty(pEase + 0, buffer, bufSize); + *buffer++ = ' '; + buffer = PropertyInfo::FormatProperty(pEase + 2, buffer, bufSize); + *buffer = '\0'; // null-terminate just in case + + return buffer; } ConsoleSetType( TypeEaseF ) @@ -717,13 +678,14 @@ ConsoleGetType(TypeRotationF) if (pt->mRotationType == RotationF::Euler) { EulerF out = pt->asEulerF(RotationF::Degrees); - dSprintf(returnBuffer, bufSize, "%g %g %g", out.x, out.y, out.z); + returnBuffer = PropertyInfo::FormatProperty(out, returnBuffer, bufSize); } else if (pt->mRotationType == RotationF::AxisAngle) { AngAxisF out = pt->asAxisAngle(RotationF::Degrees); - dSprintf(returnBuffer, bufSize, "%g %g %g %g", out.axis.x, out.axis.y, out.axis.z, out.angle); + returnBuffer = PropertyInfo::FormatProperty(&out, returnBuffer, bufSize); } + *returnBuffer = '\0'; // null-terminate just in case return returnBuffer; } From cd7666bf2af104fb4b472060307e71a77652194c Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Wed, 9 Apr 2025 16:05:22 +0100 Subject: [PATCH 13/74] rename overloaded function so linux and mac stop bitching --- Engine/source/console/propertyParsing.h | 4 ++-- Engine/source/math/mathTypes.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h index 2608077c6..dc446d8f1 100644 --- a/Engine/source/console/propertyParsing.h +++ b/Engine/source/console/propertyParsing.h @@ -168,12 +168,12 @@ namespace PropertyInfo { static const U32 bufSize = 256; char* buffer = Con::getReturnBuffer(bufSize); - FormatProperty(dataPtr, buffer, bufSize); + FormatPropertyBuffer(dataPtr, buffer, bufSize); return buffer; } template - char* FormatProperty(const void* dataPtr, char* buffer, U32 bufSize) + char* FormatPropertyBuffer(const void* dataPtr, char* buffer, U32 bufSize) { const T* values = reinterpret_cast(dataPtr); char* ptr = buffer; diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index c9b36a0e1..c1292ec7d 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -394,11 +394,11 @@ ConsoleGetType( TypeMatrixF ) char* buffer = Con::getReturnBuffer(bufSize); F32* mat = (F32*)dptr; - buffer = PropertyInfo::FormatProperty(mat + 0, buffer, bufSize); + buffer = PropertyInfo::FormatPropertyBuffer(mat + 0, buffer, bufSize); *buffer++ = ' '; - buffer = PropertyInfo::FormatProperty(mat + 4, buffer, bufSize); + buffer = PropertyInfo::FormatPropertyBuffer(mat + 4, buffer, bufSize); *buffer++ = ' '; - buffer = PropertyInfo::FormatProperty(mat + 8, buffer, bufSize); + buffer = PropertyInfo::FormatPropertyBuffer(mat + 8, buffer, bufSize); *buffer = '\0'; // null-terminate just in case return buffer; @@ -631,9 +631,9 @@ ConsoleGetType( TypeEaseF ) char* buffer = Con::getReturnBuffer(bufSize); EaseF* pEase = (EaseF*)dptr; - buffer = PropertyInfo::FormatProperty(pEase + 0, buffer, bufSize); + buffer = PropertyInfo::FormatPropertyBuffer(pEase + 0, buffer, bufSize); *buffer++ = ' '; - buffer = PropertyInfo::FormatProperty(pEase + 2, buffer, bufSize); + buffer = PropertyInfo::FormatPropertyBuffer(pEase + 2, buffer, bufSize); *buffer = '\0'; // null-terminate just in case return buffer; @@ -678,12 +678,12 @@ ConsoleGetType(TypeRotationF) if (pt->mRotationType == RotationF::Euler) { EulerF out = pt->asEulerF(RotationF::Degrees); - returnBuffer = PropertyInfo::FormatProperty(out, returnBuffer, bufSize); + returnBuffer = PropertyInfo::FormatPropertyBuffer(out, returnBuffer, bufSize); } else if (pt->mRotationType == RotationF::AxisAngle) { AngAxisF out = pt->asAxisAngle(RotationF::Degrees); - returnBuffer = PropertyInfo::FormatProperty(&out, returnBuffer, bufSize); + returnBuffer = PropertyInfo::FormatPropertyBuffer(&out, returnBuffer, bufSize); } *returnBuffer = '\0'; // null-terminate just in case From ee4acc98fee2c91bd4fc84e992bfa179bfc61f93 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Wed, 9 Apr 2025 16:29:03 +0100 Subject: [PATCH 14/74] Update propertyParsing.h bahhh humbug --- Engine/source/console/propertyParsing.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h index dc446d8f1..62b582191 100644 --- a/Engine/source/console/propertyParsing.h +++ b/Engine/source/console/propertyParsing.h @@ -163,15 +163,6 @@ namespace PropertyInfo bool default_print(String & result, SimObjectType * const & data); - template - const char* FormatProperty(const void* dataPtr) - { - static const U32 bufSize = 256; - char* buffer = Con::getReturnBuffer(bufSize); - FormatPropertyBuffer(dataPtr, buffer, bufSize); - return buffer; - } - template char* FormatPropertyBuffer(const void* dataPtr, char* buffer, U32 bufSize) { @@ -198,6 +189,15 @@ namespace PropertyInfo return ptr; // return end of written string for chaining } + template + const char* FormatProperty(const void* dataPtr) + { + static const U32 bufSize = 256; + char* buffer = Con::getReturnBuffer(bufSize); + FormatPropertyBuffer(dataPtr, buffer, bufSize); + return buffer; + } + template inline bool ParseProperty(char* str, T(&out)[count]) { From 2e64d36382db8b0f9444659fd019c67740d47c5b Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 10 Apr 2025 08:13:10 +0100 Subject: [PATCH 15/74] Update mathTypes.cpp fix matrix types, should of been using columns not the rows --- Engine/source/math/mathTypes.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index c1292ec7d..6558786d6 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -390,15 +390,20 @@ ImplementConsoleTypeCasters( TypeMatrixF, MatrixF ) ConsoleGetType( TypeMatrixF ) { + MatrixF* mat = (MatrixF*)dptr; + + Point3F col0, col1, col2; + mat->getColumn(0, &col0); + mat->getColumn(1, &col1); + mat->getColumn(2, &col2); static const U32 bufSize = 256; char* buffer = Con::getReturnBuffer(bufSize); - F32* mat = (F32*)dptr; - buffer = PropertyInfo::FormatPropertyBuffer(mat + 0, buffer, bufSize); + buffer = PropertyInfo::FormatPropertyBuffer(col0, buffer, bufSize); *buffer++ = ' '; - buffer = PropertyInfo::FormatPropertyBuffer(mat + 4, buffer, bufSize); + buffer = PropertyInfo::FormatPropertyBuffer(col1, buffer, bufSize); *buffer++ = ' '; - buffer = PropertyInfo::FormatPropertyBuffer(mat + 8, buffer, bufSize); + buffer = PropertyInfo::FormatPropertyBuffer(col2, buffer, bufSize); *buffer = '\0'; // null-terminate just in case return buffer; @@ -433,16 +438,24 @@ ConsoleMappedType(MatrixPosition, TypeMatrixPosition, Point3F, MatrixF, "") ConsoleGetType( TypeMatrixPosition ) { - F32* mat = (F32*)dptr; - const char* buff = PropertyInfo::FormatProperty(mat + 8); - return buff; + F32* col = (F32*)dptr + 3; + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + Point4F pos(col[0], col[4], col[8], col[12]); + + if (col[12] == 1.0f) + returnBuffer = PropertyInfo::FormatPropertyBuffer(&pos, returnBuffer, bufSize); + else + returnBuffer = PropertyInfo::FormatPropertyBuffer(&pos, returnBuffer, bufSize); + + return returnBuffer; } ConsoleSetType( TypeMatrixPosition ) { if (argc >= 1) { - F32 parsed[4] = { 1.0f }; // default all to 1.0f + F32 parsed[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // Combine argv into a single space-separated string if argc > 1 char buffer[256] = { 0 }; dStrncpy(buffer, *argv, sizeof(buffer)); From fae5ebb9e817ff6f3776438f6eef3ccc7f7b58cb Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 10 Apr 2025 08:55:42 +0100 Subject: [PATCH 16/74] null termination was wiping buffer --- Engine/source/console/propertyParsing.h | 1 - Engine/source/math/mathTypes.cpp | 19 +++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h index 62b582191..69bdc09f6 100644 --- a/Engine/source/console/propertyParsing.h +++ b/Engine/source/console/propertyParsing.h @@ -185,7 +185,6 @@ namespace PropertyInfo *ptr++ = ' '; } - *ptr = '\0'; return ptr; // return end of written string for chaining } diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index 6558786d6..60779b212 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -399,12 +399,11 @@ ConsoleGetType( TypeMatrixF ) static const U32 bufSize = 256; char* buffer = Con::getReturnBuffer(bufSize); - buffer = PropertyInfo::FormatPropertyBuffer(col0, buffer, bufSize); + PropertyInfo::FormatPropertyBuffer(col0, buffer, bufSize); *buffer++ = ' '; - buffer = PropertyInfo::FormatPropertyBuffer(col1, buffer, bufSize); + PropertyInfo::FormatPropertyBuffer(col1, buffer, bufSize); *buffer++ = ' '; - buffer = PropertyInfo::FormatPropertyBuffer(col2, buffer, bufSize); - *buffer = '\0'; // null-terminate just in case + PropertyInfo::FormatPropertyBuffer(col2, buffer, bufSize); return buffer; } @@ -444,9 +443,9 @@ ConsoleGetType( TypeMatrixPosition ) Point4F pos(col[0], col[4], col[8], col[12]); if (col[12] == 1.0f) - returnBuffer = PropertyInfo::FormatPropertyBuffer(&pos, returnBuffer, bufSize); + PropertyInfo::FormatPropertyBuffer(&pos, returnBuffer, bufSize); else - returnBuffer = PropertyInfo::FormatPropertyBuffer(&pos, returnBuffer, bufSize); + PropertyInfo::FormatPropertyBuffer(&pos, returnBuffer, bufSize); return returnBuffer; } @@ -644,9 +643,9 @@ ConsoleGetType( TypeEaseF ) char* buffer = Con::getReturnBuffer(bufSize); EaseF* pEase = (EaseF*)dptr; - buffer = PropertyInfo::FormatPropertyBuffer(pEase + 0, buffer, bufSize); + PropertyInfo::FormatPropertyBuffer(pEase + 0, buffer, bufSize); *buffer++ = ' '; - buffer = PropertyInfo::FormatPropertyBuffer(pEase + 2, buffer, bufSize); + PropertyInfo::FormatPropertyBuffer(pEase + 2, buffer, bufSize); *buffer = '\0'; // null-terminate just in case return buffer; @@ -691,12 +690,12 @@ ConsoleGetType(TypeRotationF) if (pt->mRotationType == RotationF::Euler) { EulerF out = pt->asEulerF(RotationF::Degrees); - returnBuffer = PropertyInfo::FormatPropertyBuffer(out, returnBuffer, bufSize); + PropertyInfo::FormatPropertyBuffer(out, returnBuffer, bufSize); } else if (pt->mRotationType == RotationF::AxisAngle) { AngAxisF out = pt->asAxisAngle(RotationF::Degrees); - returnBuffer = PropertyInfo::FormatPropertyBuffer(&out, returnBuffer, bufSize); + PropertyInfo::FormatPropertyBuffer(&out, returnBuffer, bufSize); } *returnBuffer = '\0'; // null-terminate just in case From 77a7847eed26d3086009722ea2fa6e1a17a16c69 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 10 Apr 2025 08:56:20 +0100 Subject: [PATCH 17/74] Update mathTypes.cpp --- Engine/source/math/mathTypes.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index 60779b212..ff6a3e6dd 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -646,7 +646,6 @@ ConsoleGetType( TypeEaseF ) PropertyInfo::FormatPropertyBuffer(pEase + 0, buffer, bufSize); *buffer++ = ' '; PropertyInfo::FormatPropertyBuffer(pEase + 2, buffer, bufSize); - *buffer = '\0'; // null-terminate just in case return buffer; } From 0ff636e9ca438454474a261b202925e3d7e43a9f Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Thu, 10 Apr 2025 08:56:40 +0100 Subject: [PATCH 18/74] Update mathTypes.cpp --- Engine/source/math/mathTypes.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index ff6a3e6dd..18526267c 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -696,7 +696,6 @@ ConsoleGetType(TypeRotationF) AngAxisF out = pt->asAxisAngle(RotationF::Degrees); PropertyInfo::FormatPropertyBuffer(&out, returnBuffer, bufSize); } - *returnBuffer = '\0'; // null-terminate just in case return returnBuffer; } From 860ac41181b643bc96dbfe2fe39700a955501762 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 10 Apr 2025 13:08:43 -0500 Subject: [PATCH 19/74] handle enabled->disabled->destroyed filters properly --- .../data/DamageModel/scripts/server/shapeBase.tscript | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript b/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript index 336f41861..2a1b01fae 100644 --- a/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript +++ b/Templates/BaseGame/game/data/DamageModel/scripts/server/shapeBase.tscript @@ -177,7 +177,7 @@ function ShapeBaseData::setDamageDirection(%this, %obj, %sourceObject, %damagePo function ShapeBaseData::onCollision(%this, %obj, %collObj, %vec, %len ) { - if ((!isObject(%obj) || %obj.getDamageState() !$= "Enabled")) + if (!isObject(%obj) || %obj.getDamageState() $= "Destroyed") return; //echo(%this SPC %obj SPC %collObj SPC %vec SPC %len ); @@ -189,7 +189,7 @@ function ShapeBaseData::onCollision(%this, %obj, %collObj, %vec, %len ) function ShapeBaseData::onImpact(%this, %obj, %collObj, %vec, %len ) { - if ((!isObject(%obj) || %obj.getDamageState() !$= "Enabled")) + if (!isObject(%obj) || %obj.getDamageState() $= "Destroyed") return; //echo(%this SPC %obj SPC %collObj SPC %vec SPC %len ); @@ -203,7 +203,7 @@ function ShapeBaseData::onImpact(%this, %obj, %collObj, %vec, %len ) function ShapeBaseData::damage(%this, %obj, %sourceObject, %position, %damage, %damageType) { - if (!isObject(%obj) || %obj.getDamageState() !$= "Enabled" || !%damage) + if (!isObject(%obj) || %obj.getDamageState() $= "Destroyed" || !%damage) return; %rootObj = %obj; @@ -222,7 +222,7 @@ function ShapeBaseData::damage(%this, %obj, %sourceObject, %position, %damage, % if (isObject(%client)) { - if (%rootObj.getDamageState() !$= "Enabled") + if (%obj.getDamageState() $= "Destroyed") { callGamemodeFunction("onDeath", %client, %sourceObject, %sourceClient, %damageType, ""); } @@ -233,7 +233,7 @@ function ShapeBaseData::onDamage(%this, %obj, %delta) { // This method is invoked by the ShapeBase code whenever the // object's damage level changes. - if (%delta > 0 && %obj.getDamageState() $= "Enabled") + if (%delta > 0 && %obj.getDamageState() !$= "Destroyed") { // Apply a damage flash %obj.setDamageFlash(1); From 5b2c0c77039ff62ac19a25fc03d18d643ec62952 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 13 Apr 2025 14:51:30 -0500 Subject: [PATCH 20/74] add a controlmap entry for players, vehicles, and turret datablocks add a client command to setMovemap(%object.getDatablock().controlMap) --- Engine/source/T3D/player.cpp | 5 ++++- Engine/source/T3D/player.h | 2 +- Engine/source/T3D/turret/turretShape.cpp | 3 +++ Engine/source/T3D/turret/turretShape.h | 1 + Engine/source/T3D/vehicles/vehicle.cpp | 6 ++++++ Engine/source/T3D/vehicles/vehicle.h | 1 + .../scripts/client/connectionToServer.tscript | 13 +++++++++++++ .../scripts/server/connectionToClient.tscript | 4 +++- 8 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 801df58df..2e2b9fc34 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -460,6 +460,7 @@ PlayerData::PlayerData() jumpTowardsNormal = true; physicsPlayerType = StringTable->EmptyString(); + mControlMap = StringTable->EmptyString(); dMemset( actionList, 0, sizeof(actionList) ); } @@ -739,7 +740,9 @@ void PlayerData::initPersistFields() endGroup( "Camera" ); addGroup( "Movement" ); - + addField("controlMap", TypeString, Offset(mControlMap, PlayerData), + "@brief movemap used by these types of objects.\n\n"); + addFieldV( "maxStepHeight", TypeRangedF32, Offset(maxStepHeight, PlayerData), &CommonValidators::PositiveFloat, "@brief Maximum height the player can step up.\n\n" "The player will automatically step onto changes in ground height less " diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index b80b3f0e5..4c1c27fbe 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -346,7 +346,7 @@ struct PlayerData: public ShapeBaseData { // Jump off surfaces at their normal rather than straight up bool jumpTowardsNormal; - + StringTableEntry mControlMap; // For use if/when mPhysicsPlayer is created StringTableEntry physicsPlayerType; diff --git a/Engine/source/T3D/turret/turretShape.cpp b/Engine/source/T3D/turret/turretShape.cpp index 8c523588e..5594ce3c0 100644 --- a/Engine/source/T3D/turret/turretShape.cpp +++ b/Engine/source/T3D/turret/turretShape.cpp @@ -127,6 +127,7 @@ TurretShapeData::TurretShapeData() recoilSequence[i] = -1; pitchSequence = -1; headingSequence = -1; + mControlMap = StringTable->EmptyString(); } void TurretShapeData::initPersistFields() @@ -134,6 +135,8 @@ void TurretShapeData::initPersistFields() docsURL; Parent::initPersistFields(); addGroup("Steering"); + addField("controlMap", TypeString, Offset(mControlMap, TurretShapeData), + "@brief movemap used by these types of objects.\n\n"); addField("zRotOnly", TypeBool, Offset(zRotOnly, TurretShapeData), "@brief Should the turret allow only z rotations.\n\n" "True indicates that the turret may only be rotated on its z axis, just like the Item class. " diff --git a/Engine/source/T3D/turret/turretShape.h b/Engine/source/T3D/turret/turretShape.h index b31024e07..a086d79d7 100644 --- a/Engine/source/T3D/turret/turretShape.h +++ b/Engine/source/T3D/turret/turretShape.h @@ -80,6 +80,7 @@ public: bool startLoaded; ///< Should the turret's mounted weapon(s) start in a loaded state? bool zRotOnly; ///< Should the turret allow only z rotations (like an item)? + StringTableEntry mControlMap; public: TurretShapeData(); diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index cfbd9a876..aa191be29 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -141,6 +141,7 @@ VehicleData::VehicleData() dMemset( damageEmitterOffset, 0, sizeof( damageEmitterOffset ) ); dMemset( damageEmitterIDList, 0, sizeof( damageEmitterIDList ) ); dMemset( damageLevelTolerance, 0, sizeof( damageLevelTolerance ) ); + mControlMap = StringTable->EmptyString(); numDmgEmitterAreas = 0; @@ -321,6 +322,8 @@ void VehicleData::initPersistFields() endGroup("Collision"); addGroup("Steering"); + addField("controlMap", TypeString, Offset(mControlMap, VehicleData), + "@brief movemap used by these types of objects.\n\n"); addFieldV( "jetForce", TypeRangedF32, Offset(jetForce, VehicleData), &CommonValidators::PositiveFloat, "@brief Additional force applied to the vehicle when it is jetting.\n\n" "For WheeledVehicles, the force is applied in the forward direction. For " @@ -726,6 +729,9 @@ void Vehicle::updateMove(const Move* move) if (mDamageState == Enabled) { setImageTriggerState(0,move->trigger[0]); setImageTriggerState(1,move->trigger[1]); + //legacy code has trigger 2 and 3 reserved + setImageTriggerState(2, move->trigger[4]); + setImageTriggerState(3, move->trigger[5]); } // Throttle diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 402a3109a..746e99a16 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -69,6 +69,7 @@ struct VehicleData : public RigidShapeData F32 damageLevelTolerance[ VC_NUM_DAMAGE_LEVELS ]; F32 numDmgEmitterAreas; + StringTableEntry mControlMap; bool enablePhysicsRep; // diff --git a/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript b/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript index cbd4b46cc..82d00105c 100644 --- a/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript +++ b/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript @@ -158,3 +158,16 @@ function disconnectedCleanup() moduleExec("onDestroyClientConnection", "Game"); } + +function clientCmdsetMoveMap(%movemap) +{ + if (!isObject(%movemap)) return; + if(isObject(ServerConnection) && isObject(ServerConnection.curMoveMap)) + ServerConnection.curMoveMap.pop(); + + // clear movement + $mvForwardAction = 0; + $mvBackwardAction = 0; + %movemap.push(); + ServerConnection.curMoveMap = %movemap; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript b/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript index b804ac046..0e2a11e28 100644 --- a/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript +++ b/Templates/BaseGame/game/core/clientServer/scripts/server/connectionToClient.tscript @@ -275,7 +275,9 @@ function GameConnection::onPostSpawn( %this ) if (%this.numModsNeedingLoaded) callOnObjectList("onPostSpawn", %modulesIdList, %this); else - %this.listener.onPostSpawnComplete(%this); + %this.listener.onPostSpawnComplete(%this); + if (isObject(%this.player.getDatablock().controlMap)) + commandToClient(%this, 'setMoveMap', %this.player.getDatablock().controlMap); } function GameConnectionListener::onPostSpawnComplete(%this, %client) From 8c663a19a516a20da52c415c27f32625f92fbf20 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 15:12:27 -0500 Subject: [PATCH 21/74] generalized ai subsystem wipwork --- Engine/source/CMakeLists.txt | 2 +- Engine/source/T3D/AI/AIAimTarget.cpp | 77 +++++ Engine/source/T3D/AI/AIAimTarget.h | 39 +++ Engine/source/T3D/AI/AIController.cpp | 430 ++++++++++++++++++++++++++ Engine/source/T3D/AI/AIController.h | 155 ++++++++++ Engine/source/T3D/AI/AICover.cpp | 21 ++ Engine/source/T3D/AI/AICover.h | 37 +++ Engine/source/T3D/AI/AIGoal.cpp | 24 ++ Engine/source/T3D/AI/AIGoal.h | 34 ++ Engine/source/T3D/AI/AIInfo.cpp | 59 ++++ Engine/source/T3D/AI/AIInfo.h | 46 +++ Engine/source/T3D/AI/AINavigation.cpp | 259 ++++++++++++++++ Engine/source/T3D/AI/AINavigation.h | 91 ++++++ Engine/source/T3D/player.cpp | 35 ++- Engine/source/T3D/player.h | 13 +- Engine/source/T3D/shapeBase.h | 4 +- 16 files changed, 1320 insertions(+), 6 deletions(-) create mode 100644 Engine/source/T3D/AI/AIAimTarget.cpp create mode 100644 Engine/source/T3D/AI/AIAimTarget.h create mode 100644 Engine/source/T3D/AI/AIController.cpp create mode 100644 Engine/source/T3D/AI/AIController.h create mode 100644 Engine/source/T3D/AI/AICover.cpp create mode 100644 Engine/source/T3D/AI/AICover.h create mode 100644 Engine/source/T3D/AI/AIGoal.cpp create mode 100644 Engine/source/T3D/AI/AIGoal.h create mode 100644 Engine/source/T3D/AI/AIInfo.cpp create mode 100644 Engine/source/T3D/AI/AIInfo.h create mode 100644 Engine/source/T3D/AI/AINavigation.cpp create mode 100644 Engine/source/T3D/AI/AINavigation.h diff --git a/Engine/source/CMakeLists.txt b/Engine/source/CMakeLists.txt index 35d9d81d7..34991cce7 100644 --- a/Engine/source/CMakeLists.txt +++ b/Engine/source/CMakeLists.txt @@ -58,7 +58,7 @@ torqueAddSourceDirectories("platform" "platform/threads" "platform/async" torqueAddSourceDirectories("platform/nativeDialogs") # Handle T3D -torqueAddSourceDirectories( "T3D" "T3D/assets" "T3D/decal" "T3D/examples" "T3D/fps" "T3D/fx" +torqueAddSourceDirectories( "T3D" "T3D/AI" "T3D/assets" "T3D/decal" "T3D/examples" "T3D/fps" "T3D/fx" "T3D/gameBase" "T3D/gameBase/std" "T3D/lighting" "T3D/physics" diff --git a/Engine/source/T3D/AI/AIAimTarget.cpp b/Engine/source/T3D/AI/AIAimTarget.cpp new file mode 100644 index 000000000..907b2da3d --- /dev/null +++ b/Engine/source/T3D/AI/AIAimTarget.cpp @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "AIAimTarget.h" +#include "AIController.h" + +static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType; + +bool AIAimTarget::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) +{ + ShapeBase* sbo = dynamic_cast(getCtrl()->getAIInfo()->mObj.getPointer()); + if (!target) + { + target = dynamic_cast(mObj.getPointer()); + if (!target) + return false; + } + if (_checkEnabled) + { + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase* shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false; + } + else + return false; + } + + RayInfo ri; + + sbo->disableCollision(); + + S32 mountCount = target->getMountedObjectCount(); + for (S32 i = 0; i < mountCount; i++) + { + target->getMountedObject(i)->disableCollision(); + } + + Point3F checkPoint; + if (_useMuzzle) + sbo->getMuzzlePoint(0, &checkPoint); + else + { + MatrixF eyeMat; + sbo->getEyeTransform(&eyeMat); + eyeMat.getColumn(3, &checkPoint); + } + + bool hit = !gServerContainer.castRay(checkPoint, target->getBoxCenter(), sAILoSMask, &ri); + sbo->enableCollision(); + + for (S32 i = 0; i < mountCount; i++) + { + target->getMountedObject(i)->enableCollision(); + } + return hit; +} diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h new file mode 100644 index 000000000..650a4f9b8 --- /dev/null +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _AIAIMTARGET_H_ +#define _AIAIMTARGET_H_ + +#include "AIInfo.h" +struct AIAimTarget : AIInfo +{ + typedef AIInfo Parent; + Point3F mAimOffset; + bool mTargetInLOS; // Is target object visible? + Point3F getPosition() { return ((mObj) ? mObj->getPosition() : mPosition) + mAimOffset; } + bool checkInLos(GameBase* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); + bool checkInFoV(GameBase* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); + AIAimTarget(AIController* controller) : Parent(controller) {}; + AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; + AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; +}; + +#endif diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp new file mode 100644 index 000000000..ba14b2c34 --- /dev/null +++ b/Engine/source/T3D/AI/AIController.cpp @@ -0,0 +1,430 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "AIController.h" +#include "T3D/player.h" + + +IMPLEMENT_CONOBJECT(AIController); + +//----------------------------------------------------------------------------- +void AIController::throwCallback(const char* name) +{ + Con::executef(mControllerData, name, getIdString()); //controller data callbacks + + GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); + if (!gbo) return; + Con::executef(gbo->getDataBlock(), name, getAIInfo()->mObj->getIdString()); //legacy support for object db callbacks +} + +void AIController::initPersistFields() +{ + addProtectedField("ControllerData", TYPEID< AIControllerData >(), Offset(mControllerData, AIController), + &setControllerDataProperty, &defaultProtectedGetFn, + "Script datablock used for game objects."); + addFieldV("MoveSpeed", TypeRangedF32, Offset(mMovement.mMoveSpeed, AIController), &CommonValidators::PositiveFloat, + "@brief default move sepeed."); +} + +bool AIController::setControllerDataProperty(void* obj, const char* index, const char* db) +{ + if (db == NULL || !db[0]) + { + Con::errorf("AIController::setControllerDataProperty - Can't unset ControllerData on AIController objects"); + return false; + } + + AIController* object = static_cast(obj); + AIControllerData* data; + if (Sim::findObject(db, data)) + { + object->mControllerData = data; + return true; + } + Con::errorf("AIController::setControllerDataProperty - Could not find ControllerData \"%s\"", db); + return false; +} + +#ifdef TORQUE_NAVIGATION_ENABLED +bool AIController::getAIMove(Move* movePtr) +{ + *movePtr = NullMove; + ShapeBase* sbo = dynamic_cast(getAIInfo()->mObj.getPointer()); + if (!sbo) return false; + + // Use the eye as the current position. + MatrixF eye; + sbo->getEyeTransform(&eye); + Point3F location = eye.getPosition(); + Point3F rotation = sbo->getTransform().getForwardVector(); + +#ifdef TORQUE_NAVIGATION_ENABLED + if (sbo->getDamageState() == ShapeBase::Enabled) + { + if (mMovement.mMoveState != ModeStop) + getNav()->updateNavMesh(); + if (!getGoal()->mObj.isNull()) + { + if (getNav()->mPathData.path.isNull()) + { + if (getGoal()->getDist() > mControllerData->mMoveTolerance) + getNav()->followObject(getGoal()); + } + else + { + if (getGoal()->getDist() > mControllerData->mMoveTolerance) + getNav()->repath(); + + if (getAim()->getDist() < mControllerData->mMoveTolerance) + { + getNav()->clearPath(); + mMovement.mMoveState = ModeStop; + throwCallback("onTargetInRange"); + } + else if (getAim()->getDist() < mControllerData->mAttackRadius) + { + throwCallback("onTargetInFiringRange"); + } + } + } + } +#endif // TORQUE_NAVIGATION_ENABLED + + // Orient towards the aim point, aim object, or towards + // our destination. + if (getAim()->mObj || getAim()->mPosSet || mMovement.mMoveState != ModeStop) + { + // Update the aim position if we're aiming for an object or explicit position + if (getAim()->mObj || getAim()->mPosSet) + mMovement.mAimLocation = getAim()->getPosition(); + else + mMovement.mAimLocation = mMovement.mMoveDestination; + + mControllerData->resolveYaw(this, location, movePtr); + mControllerData->resolvePitch(this, location, movePtr); + mControllerData->resolveRoll(this, location, movePtr); + mControllerData->resolveSpeed(this, location, movePtr); + mControllerData->resolveStuck(this); + } + + // Test for target location in sight if it's an object. The LOS is + // run from the eye position to the center of the object's bounding, + // which is not very accurate. + if (getAim()->mObj) + { + GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); + if (getAim()->checkInLos(gbo)) + { + if (!getAim()->mTargetInLOS) + { + throwCallback("onTargetEnterLOS"); + getAim()->mTargetInLOS = true; + } + } + else if (getAim()->mTargetInLOS) + { + throwCallback("onTargetExitLOS"); + getAim()->mTargetInLOS = false; + } + } + + /* + // Replicate the trigger state into the move so that + // triggers can be controlled from scripts. + for (U32 i = 0; i < MaxTriggerKeys; i++) + movePtr->trigger[i] = getImageTriggerState(i); + */ + +#ifdef TORQUE_NAVIGATION_ENABLED + if (getNav()->mJump == AINavigation::Now) + { + movePtr->trigger[2] = true; + getNav()->mJump = AINavigation::None; + } + else if (getNav()->mJump == AINavigation::Ledge) + { + // If we're not touching the ground, jump! + RayInfo info; + if (!getAIInfo()->mObj->getContainer()->castRay(getAIInfo()->getPosition(), getAIInfo()->getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info)) + { + movePtr->trigger[2] = true; + getNav()->mJump = AINavigation::None; + } + } +#endif // TORQUE_NAVIGATION_ENABLED + + return true; +} + +void AIController::clearCover() +{ + // Notify cover that we are no longer on our way. + if (!getCover()->mCoverPoint.isNull()) + getCover()->mCoverPoint->setOccupied(false); + SAFE_DELETE(mCover); +} + +//----------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(AIControllerData); +void AIControllerData::resolveYaw(AIController* obj, Point3F location, Move* move) +{ + F32 xDiff = obj->mMovement.mAimLocation.x - location.x; + F32 yDiff = obj->mMovement.mAimLocation.y - location.y; + Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector(); + + if (!mIsZero(xDiff) || !mIsZero(yDiff)) + { + // First do Yaw + // use the cur yaw between -Pi and Pi + F32 curYaw = rotation.z; + while (curYaw > M_2PI_F) + curYaw -= M_2PI_F; + while (curYaw < -M_2PI_F) + curYaw += M_2PI_F; + + // find the yaw offset + F32 newYaw = mAtan2(xDiff, yDiff); + F32 yawDiff = newYaw - curYaw; + + // make it between 0 and 2PI + if (yawDiff < 0.0f) + yawDiff += M_2PI_F; + else if (yawDiff >= M_2PI_F) + yawDiff -= M_2PI_F; + + // now make sure we take the short way around the circle + if (yawDiff > M_PI_F) + yawDiff -= M_2PI_F; + else if (yawDiff < -M_PI_F) + yawDiff += M_2PI_F; + + move->yaw = yawDiff; + } +} + + +void AIControllerData::resolveRoll(AIController* obj, Point3F location, Move* movePtr) +{ +} + +void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) +{ + // Move towards the destination + if (obj->mMovement.mMoveState != AIController::ModeStop) + { + F32 xDiff = obj->mMovement.mMoveDestination.x - location.x; + F32 yDiff = obj->mMovement.mMoveDestination.y - location.y; + Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector(); + + // Check if we should mMove, or if we are 'close enough' + if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) + { + obj->mMovement.mMoveState = AIController::ModeStop; + obj->getNav()->onReachDestination(); + } + else + { + // Build move direction in world space + if (mIsZero(xDiff)) + movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -1.0f : 1.0f; + else + if (mIsZero(yDiff)) + movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -1.0f : 1.0f; + else + if (mFabs(xDiff) > mFabs(yDiff)) + { + F32 value = mFabs(yDiff / xDiff); + movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -value : value; + movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -1.0f : 1.0f; + } + else + { + F32 value = mFabs(xDiff / yDiff); + movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -value : value; + movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -1.0f : 1.0f; + } + + // Rotate the move into object space (this really only needs + // a 2D matrix) + Point3F newMove; + MatrixF moveMatrix; + moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); + moveMatrix.mulV(Point3F(movePtr->x, movePtr->y, 0.0f), &newMove); + movePtr->x = newMove.x; + movePtr->y = newMove.y; + + // Set movement speed. We'll slow down once we get close + // to try and stop on the spot... + if (obj->mMovement.mMoveSlowdown) + { + F32 speed = obj->mMovement.mMoveSpeed; + F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff); + F32 maxDist = mMoveTolerance * 2; + if (dist < maxDist) + speed *= dist / maxDist; + movePtr->x *= speed; + movePtr->y *= speed; + + obj->mMovement.mMoveState = AIController::ModeSlowing; + } + else + { + movePtr->x *= obj->mMovement.mMoveSpeed; + movePtr->y *= obj->mMovement.mMoveSpeed; + + obj->mMovement.mMoveState = AIController::ModeMove; + } + } + } +} + +void AIControllerData::resolveStuck(AIController* obj) +{ + ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + // Don't check for ai stuckness if animation during + // an anim-clip effect override. + if (sbo->getDamageState() == ShapeBase::Enabled && !(sbo->anim_clip_flags & ShapeBase::ANIM_OVERRIDDEN) && !sbo->isAnimationLocked()) { + if (obj->mMovement.mMoveStuckTestCountdown > 0) + --obj->mMovement.mMoveStuckTestCountdown; + else + { + // We should check to see if we are stuck... + F32 locationDelta = (obj->getAIInfo()->getPosition() - obj->getAIInfo()->mLastPos).len(); + if (locationDelta < mMoveStuckTolerance && (sbo->getDamageState() == ShapeBase::Enabled)) + { + // If we are slowing down, then it's likely that our location delta will be less than + // our move stuck tolerance. Because we can be both slowing and stuck + // we should TRY to check if we've moved. This could use better detection. + if (obj->mMovement.mMoveState != AIController::ModeSlowing || locationDelta == 0) + { + obj->mMovement.mMoveState = AIController::ModeStuck; + obj->throwCallback("onStuck"); + } + } + } + obj->getAIInfo()->mLastPos = obj->getAIInfo()->getPosition(); + } +} + +void AIControllerData::initPersistFields() +{ + docsURL; + addGroup("AI"); + + addFieldV("moveTolerance", TypeRangedF32, Offset(mMoveTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before stopping.\n\n" + "When the AIPlayer is moving to a given destination it will move to within " + "this distance of the destination and then stop. By providing this tolerance " + "it helps the AIPlayer from never reaching its destination due to minor obstacles, " + "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); + + addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before stopping.\n\n" + "When the AIPlayer is moving to a given destination it will move to within " + "this distance of the destination and then stop. By providing this tolerance " + "it helps the AIPlayer from never reaching its destination due to minor obstacles, " + "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); + + addFieldV("moveStuckTolerance", TypeRangedF32, Offset(mMoveStuckTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance tolerance on stuck check.\n\n" + "When the AIPlayer is moving to a given destination, if it ever moves less than " + "this tolerance during a single tick, the AIPlayer is considered stuck. At this point " + "the onMoveStuck() callback is called on the datablock.\n"); + + addFieldV("moveStuckTestDelay", TypeRangedS32, Offset(mMoveStuckTestDelay, AIControllerData), &CommonValidators::PositiveInt, + "@brief The number of ticks to wait before testing if the AIPlayer is stuck.\n\n" + "When the AIPlayer is asked to move, this property is the number of ticks to wait " + "before the AIPlayer starts to check if it is stuck. This delay allows the AIPlayer " + "to accelerate to full speed without its initial slow start being considered as stuck.\n" + "@note Set to zero to have the stuck test start immediately.\n"); + + addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance considered in firing range for callback purposes."); + + endGroup("AI"); + +#ifdef TORQUE_NAVIGATION_ENABLED + addGroup("Pathfinding"); + + addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIControllerData), + "Allow the character to walk on dry land."); + addField("allowJump", TypeBool, Offset(mLinkTypes.jump, AIControllerData), + "Allow the character to use jump links."); + addField("allowDrop", TypeBool, Offset(mLinkTypes.drop, AIControllerData), + "Allow the character to use drop links."); + addField("allowSwim", TypeBool, Offset(mLinkTypes.swim, AIControllerData), + "Allow the character to move in water."); + addField("allowLedge", TypeBool, Offset(mLinkTypes.ledge, AIControllerData), + "Allow the character to jump ledges."); + addField("allowClimb", TypeBool, Offset(mLinkTypes.climb, AIControllerData), + "Allow the character to use climb links."); + addField("allowTeleport", TypeBool, Offset(mLinkTypes.teleport, AIControllerData), + "Allow the character to use teleporters."); + + endGroup("Pathfinding"); +#endif // TORQUE_NAVIGATION_ENABLED + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) +{ + Player* po = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!po) return;//not a player + + if (obj->getAim()->mObj || obj->getAim()->mPosSet || obj->mMovement.mMoveState != AIController::ModeStop) + { + // Next do pitch. + if (!obj->getAim()->mObj && !obj->getAim()->mPosSet) + { + // Level out if were just looking at our next way point. + Point3F headRotation = po->getHeadRotation(); + movePtr->pitch = -headRotation.x; + } + else + { + F32 xDiff = obj->mMovement.mAimLocation.x - location.x; + F32 yDiff = obj->mMovement.mAimLocation.y - location.y; + // This should be adjusted to run from the + // eye point to the object's center position. Though this + // works well enough for now. + F32 vertDist = obj->mMovement.mAimLocation.z - location.z; + F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff); + F32 newPitch = mAtan2(horzDist, vertDist) - (M_PI_F / 2.0f); + if (mFabs(newPitch) > 0.01f) + { + Point3F headRotation = po->getHeadRotation(); + movePtr->pitch = newPitch - headRotation.x; + } + } + } + else + { + // Level out if we're not doing anything else + Point3F headRotation = po->getHeadRotation(); + movePtr->pitch = -headRotation.x; + } +} +#endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h new file mode 100644 index 000000000..69ebbe27f --- /dev/null +++ b/Engine/source/T3D/AI/AIController.h @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _AICONTROLLER_H_ +#define _AICONTROLLER_H_ +#ifdef TORQUE_NAVIGATION_ENABLED +#include "navigation/coverPoint.h" +#include "AIInfo.h" +#include "AIGoal.h" +#include "AIAimTarget.h" +#include "AICover.h" +#include "AINavigation.h" +class AIControllerData; +class AIController; + +//----------------------------------------------------------------------------- +class AIController : public SimObject { + + typedef SimObject Parent; + +public: + AIControllerData* mControllerData; +protected: + static bool setControllerDataProperty(void* object, const char* index, const char* data); +public: + enum MoveState { + ModeStop, // AI has stopped moving. + ModeMove, // AI is currently moving. + ModeStuck, // AI is stuck, but wants to move. + ModeSlowing, // AI is slowing down as it reaches it's destination. + }; + +private: + AIInfo*mAIInfo; +public: + void setAIInfo(SimObjectPtr objIn, F32 rad = 0.0f) { delete(mAIInfo); mAIInfo = new AIInfo(this, objIn, rad); } + AIInfo* getAIInfo() { return mAIInfo; } +private: + AIGoal* mGoal; +public: + void setGoal(AIInfo* targ) { mGoal = (targ) ? new AIGoal(this, targ->getPosition(), targ->mRadius) : NULL; } + void setGoal(Point3F loc, F32 rad = 0.0f) { delete(mGoal); mGoal = new AIGoal(this, loc, rad); } + void setGoal(SimObjectPtr objIn, F32 rad = 0.0f) { delete(mGoal); mGoal = new AIGoal(this, objIn, rad); } + AIGoal* getGoal() { return mGoal; } + void clearGoal() { SAFE_DELETE(mGoal); } +private: + AIAimTarget* mAimTarget; +public: + void setAim(Point3F loc, F32 rad = 0.0f, Point3F offset = Point3F(0.0f,0.0f,0.0f)) { delete(mAimTarget); mAimTarget = new AIAimTarget(this, loc, rad); mAimTarget->mAimOffset = offset; } + void setAim(SimObjectPtr objIn, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f)) { delete(mAimTarget); mAimTarget = new AIAimTarget(this, objIn, rad); mAimTarget->mAimOffset = offset; } + AIAimTarget* getAim() { return mAimTarget; } + void clearAim() { SAFE_DELETE(mAimTarget); } +private: + AICover* mCover; +public: + void setCover(Point3F loc, F32 rad = 0.0f) { delete(mCover); mCover = new AICover(this, loc, rad); } + void setCover(SimObjectPtr objIn, F32 rad = 0.0f) { delete(mCover); mCover = new AICover(this, objIn, rad); } + AICover* getCover() { return mCover; } + bool findCover(const Point3F& from, F32 radius); + void clearCover(); + + // Utility Methods + void throwCallback(const char* name); + AINavigation* mNav; + AINavigation* getNav() { return mNav; }; + struct Movement + { + MoveState mMoveState; + F32 mMoveSpeed = 1.0; + bool mMoveSlowdown; // Slowdown as we near the destination + Point3F mLastLocation; // For stuck check + S32 mMoveStuckTestCountdown; // The current countdown until at AI starts to check if it is stuck + Point3F mAimLocation; + Point3F mMoveDestination; + // move triggers + bool mMoveTriggers[MaxTriggerKeys]; + void stopMove(); + void onStuck(); + } mMovement; + + struct TriggerState + { + // Trigger sets/gets + void setMoveTrigger(U32 slot, const bool isSet = true); + bool getMoveTrigger(U32 slot) const; + void clearMoveTriggers(); + } mTriggerState; + bool getAIMove(Move* move); + + static void initPersistFields(); + AIController() + { + mControllerData = NULL; + mAIInfo = new AIInfo(this); + mGoal = new AIGoal(this); + mAimTarget = new AIAimTarget(this); + mCover = new AICover(this); + mNav = new AINavigation(this); + }; + + DECLARE_CONOBJECT(AIController); +}; + +//----------------------------------------------------------------------------- +class AIControllerData : public SimDataBlock { + + typedef SimDataBlock Parent; + +public: + + AIControllerData() {}; + ~AIControllerData() {}; + + static void initPersistFields(); + DECLARE_CONOBJECT(AIControllerData); + + F32 mMoveTolerance; // Distance from destination point before we stop + F32 mFollowTolerance; // Distance from destination object before we stop + F32 mAttackRadius; // Distance to trigger weaponry calcs + F32 mMoveStuckTolerance; // Distance tolerance on stuck check + S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck + /// Types of link we can use. + LinkData mLinkTypes; + + void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + void resolvePitch(AIController* obj, Point3F location, Move* movePtr) {}; + void resolveRoll(AIController* obj, Point3F location, Move* movePtr); + void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); + void resolveStuck(AIController* obj); +}; + +class AIPlayerControllerData : AIControllerData +{ + void resolvePitch(AIController* obj, Point3F location, Move* movePtr); +}; +#endif // TORQUE_NAVIGATION_ENABLED +#endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AICover.cpp b/Engine/source/T3D/AI/AICover.cpp new file mode 100644 index 000000000..509b0f0c0 --- /dev/null +++ b/Engine/source/T3D/AI/AICover.cpp @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- diff --git a/Engine/source/T3D/AI/AICover.h b/Engine/source/T3D/AI/AICover.h new file mode 100644 index 000000000..d9d1cb638 --- /dev/null +++ b/Engine/source/T3D/AI/AICover.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _AICOVER_H_ +#define _AICOVER_H_ + +#include "AIInfo.h" + +struct AICover : AIInfo +{ + typedef AIInfo Parent; + /// Pointer to a cover point. + SimObjectPtr mCoverPoint; + AICover(AIController* controller) : Parent(controller) {}; + AICover(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; + AICover(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; +}; + +#endif diff --git a/Engine/source/T3D/AI/AIGoal.cpp b/Engine/source/T3D/AI/AIGoal.cpp new file mode 100644 index 000000000..e94fdd8ee --- /dev/null +++ b/Engine/source/T3D/AI/AIGoal.cpp @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "AIGoal.h" +#include "AIController.h" diff --git a/Engine/source/T3D/AI/AIGoal.h b/Engine/source/T3D/AI/AIGoal.h new file mode 100644 index 000000000..e169e489d --- /dev/null +++ b/Engine/source/T3D/AI/AIGoal.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _AIGOAL_H_ +#define _AIGOAL_H_ + +#include "AIInfo.h" + +struct AIGoal : AIInfo +{ + typedef AIInfo Parent; + AIGoal(AIController* controller): Parent(controller) {}; + AIGoal(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; + AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; +}; +#endif diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp new file mode 100644 index 000000000..3ce55d86c --- /dev/null +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "AIInfo.h" +#include "AIController.h" + +AIInfo::AIInfo(AIController* controller) +{ + mControllerRef = controller; + mObj = NULL; + mPosition = mLastPos = Point3F(0.0f, 0.0f, 0.0f); + mRadius = 0.0f; + mPosSet = false; +}; + +AIInfo::AIInfo(AIController* controller, SimObjectPtr objIn, F32 radIn) +{ + mControllerRef = controller; + mObj = objIn; + mPosition = mLastPos = objIn->getPosition(); + mRadius = radIn; + mPosSet = false; +}; + +AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn) +{ + mControllerRef = controller; + mObj = NULL; + mPosition = mLastPos = pointIn; + mRadius = radIn; + mPosSet = true; +}; + +F32 AIInfo::getDist() +{ + AIInfo* controlObj = getCtrl()->getAIInfo(); + F32 ret = VectorF(controlObj->mObj->getPosition() - getPosition()).len(); + ret -= controlObj->mRadius + mRadius; + return ret; +} diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h new file mode 100644 index 000000000..db0dc191d --- /dev/null +++ b/Engine/source/T3D/AI/AIInfo.h @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _AIINFO_H_ +#define _AIINFO_H_ + +#ifndef _SHAPEBASE_H_ +#include "T3D/shapeBase.h" +#endif + +class AIController; +struct AIInfo +{ + AIController* mControllerRef; + AIController* getCtrl() { return mControllerRef; }; + void setCtrl(AIController* controller) { mControllerRef = controller; }; + SimObjectPtr mObj; + Point3F mPosition, mLastPos; + bool mPosSet; + F32 mRadius; + Point3F getPosition() { return (mObj) ? mObj->getPosition() : mPosition; } + F32 getDist(); + AIInfo() = delete; + AIInfo(AIController* controller); + AIInfo(AIController* controller, SimObjectPtr objIn, F32 radIn = 0.0f); + AIInfo(AIController* controller,Point3F pointIn, F32 radIn = 0.0f); +}; +#endif diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp new file mode 100644 index 000000000..18efeb3da --- /dev/null +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -0,0 +1,259 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "AINavigation.h" +#include "AIController.h" + +AINavigation::AINavigation(AIController* controller) +{ + mControllerRef = controller; +} + +NavMesh* AINavigation::findNavMesh() const +{ + GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); + // Search for NavMeshes that contain us entirely with the smallest possible + // volume. + NavMesh* mesh = NULL; + SimSet* set = NavMesh::getServerSet(); + for (U32 i = 0; i < set->size(); i++) + { + NavMesh* m = static_cast(set->at(i)); + if (m->getWorldBox().isContained(gbo->getWorldBox())) + { + if (!mesh || m->getWorldBox().getVolume() < mesh->getWorldBox().getVolume()) + mesh = m; + } + } + return mesh; +} + +void AINavigation::updateNavMesh() +{ + GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); + NavMesh* old = mNavMesh; + if (mNavMesh.isNull()) + mNavMesh = findNavMesh(); + else + { + if (!mNavMesh->getWorldBox().isContained(gbo->getWorldBox())) + mNavMesh = findNavMesh(); + } + // See if we need to update our path. + if (mNavMesh != old && !mPathData.path.isNull()) + { + setPathDestination(mPathData.path->mTo); + } +} + +void AINavigation::moveToNode(S32 node) +{ + if (mPathData.path.isNull()) + return; + + // -1 is shorthand for 'last path node'. + if (node == -1) + node = mPathData.path->size() - 1; + + // Consider slowing down on the last path node. + setMoveDestination(mPathData.path->getNode(node), false); + + // Check flags for this segment. + if (mPathData.index) + { + U16 flags = mPathData.path->getFlags(node - 1); + // Jump if we must. + if (flags & LedgeFlag) + mJump = Ledge; + else if (flags & JumpFlag) + mJump = Now; + else + // Catch pathing errors. + mJump = None; + } + + // Store current index. + mPathData.index = node; +} + +void AINavigation::repath() +{ + // Ineffectual if we don't have a path, or are using someone else's. + if (mPathData.path.isNull() || !mPathData.owned) + return; + + // If we're following, get their position. + mPathData.path->mTo = mControllerRef->getGoal()->getPosition(); + // Update from position and replan. + mPathData.path->mFrom = mControllerRef->getAIInfo()->getPosition(); + mPathData.path->plan(); + // Move to first node (skip start pos). + moveToNode(1); +} + +Point3F AINavigation::getPathDestination() const +{ + if (!mPathData.path.isNull()) + return mPathData.path->mTo; + return Point3F(0, 0, 0); + +} + +void AINavigation::setMoveDestination(const Point3F& location, bool slowdown) +{ + mMoveDestination = location; + mControllerRef->mMovement.mMoveState = AIController::ModeMove; + mControllerRef->mMovement.mMoveSlowdown = slowdown; + mControllerRef->mMovement.mMoveStuckTestCountdown = mControllerRef->mControllerData->mMoveStuckTestDelay; +} + +void AINavigation::onReachDestination() +{ + +#ifdef TORQUE_NAVIGATION_ENABLED + if (!getPath().isNull()) + { + if (mPathData.index == getPath()->size() - 1) + { + // Handle looping paths. + if (getPath()->mIsLooping) + moveToNode(0); + // Otherwise end path. + else + { + clearPath(); + getCtrl()->throwCallback("onReachDestination"); + } + } + else + { + moveToNode(mPathData.index + 1); + // Throw callback every time if we're on a looping path. + //if(mPathData.path->mIsLooping) + //throwCallback("onReachDestination"); + } + } + else +#endif + getCtrl()->throwCallback("onReachDestination"); +} + +bool AINavigation::setPathDestination(const Point3F& pos) +{ + if (!mNavMesh) + updateNavMesh(); + // If we can't find a mesh, just move regularly. + if (!mNavMesh) + { + //setMoveDestination(pos); + mControllerRef->throwCallback("onPathFailed"); + return false; + } + + // Create a new path. + NavPath* path = new NavPath(); + + path->mMesh = mNavMesh; + path->mFrom = mControllerRef->getAIInfo()->getPosition(); + path->mTo = pos; + path->mFromSet = path->mToSet = true; + path->mAlwaysRender = true; + path->mLinkTypes = mControllerRef->mControllerData->mLinkTypes; + path->mXray = true; + // Paths plan automatically upon being registered. + if (!path->registerObject()) + { + delete path; + return false; + } + + if (path->success()) + { + // Clear any current path we might have. + clearPath(); + mControllerRef->clearCover(); + clearFollow(); + // Store new path. + mPathData.path = path; + mPathData.owned = true; + // Skip node 0, which we are currently standing on. + moveToNode(1); + mControllerRef->throwCallback("onPathSuccess"); + return true; + } + else + { + // Just move normally if we can't path. + //setMoveDestination(pos, true); + //return; + mControllerRef->throwCallback("onPathFailed"); + path->deleteObject(); + return false; + } +} + +void AINavigation::followObject(AIInfo* targ) +{ + if (!targ) return; + + if (targ->getDist() < mControllerRef->mControllerData->mMoveTolerance) + return; + + if (setPathDestination(targ->getPosition())) + { + mControllerRef->clearCover(); + mControllerRef->setGoal(targ); + } +} + +void AINavigation::followObject(SceneObject* obj, F32 radius) +{ + mControllerRef->setGoal(obj, radius); + followObject(mControllerRef->getGoal()); +} + +void AINavigation::clearFollow() +{ + mControllerRef->clearGoal(); +} + +void AINavigation::followNavPath(NavPath* path) +{ + // Get rid of our current path. + clearPath(); + mControllerRef->clearCover(); + clearFollow(); + + // Follow new path. + mPathData.path = path; + mPathData.owned = false; + // Start from 0 since we might not already be there. + moveToNode(0); +} + +void AINavigation::clearPath() +{ + // Only delete if we own the path. + if (!mPathData.path.isNull() && mPathData.owned) + mPathData.path->deleteObject(); + // Reset path data. + mPathData = PathData(); +} diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h new file mode 100644 index 000000000..130305eea --- /dev/null +++ b/Engine/source/T3D/AI/AINavigation.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _AINAVIGATION_H_ +#define _AINAVIGATION_H_ + +#include "AIInfo.h" + +#include "navigation/navPath.h" +#include "navigation/navMesh.h" + +class AIController; +struct AINavigation +{ + AIController* mControllerRef; + AIController* getCtrl() { return mControllerRef; }; + + AINavigation() = delete; + AINavigation(AIController* controller); + + /// Stores information about a path. + struct PathData { + /// Pointer to path object. + SimObjectPtr path; + /// Do we own our path? If so, we will delete it when finished. + bool owned; + /// Path node we're at. + U32 index; + /// Default constructor. + PathData() : path(NULL) + { + owned = false; + index = 0; + } + }; + + /// Should we jump? + enum JumpStates { + None, ///< No, don't jump. + Now, ///< Jump immediately. + Ledge, ///< Jump when we walk off a ledge. + }; + + Point3F mMoveDestination; + void setMoveDestination(const Point3F& location, bool slowdown); + void onReachDestination(); + + /// NavMesh we pathfind on. + SimObjectPtr mNavMesh; + NavMesh* findNavMesh() const; + void updateNavMesh(); + PathData mPathData; + JumpStates mJump; + + /// Clear out the current path. + void clearPath(); + bool setPathDestination(const Point3F& pos); + Point3F getPathDestination() const; + void repath(); + + /// Get the current path we're following. + SimObjectPtr getPath() { return mPathData.path; }; + void followNavPath(NavPath* path); + + void followObject(AIInfo* targ); + void followObject(SceneObject* obj, F32 radius); + void clearFollow(); + /// Move to the specified node in the current path. + void moveToNode(S32 node); + +}; + +#endif diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 801df58df..cd32ea024 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -460,6 +460,7 @@ PlayerData::PlayerData() jumpTowardsNormal = true; physicsPlayerType = StringTable->EmptyString(); + mControlMap = StringTable->EmptyString(); dMemset( actionList, 0, sizeof(actionList) ); } @@ -739,7 +740,9 @@ void PlayerData::initPersistFields() endGroup( "Camera" ); addGroup( "Movement" ); - + addField("controlMap", TypeString, Offset(mControlMap, PlayerData), + "@brief movemap used by these types of objects.\n\n"); + addFieldV( "maxStepHeight", TypeRangedF32, Offset(maxStepHeight, PlayerData), &CommonValidators::PositiveFloat, "@brief Maximum height the player can step up.\n\n" "The player will automatically step onto changes in ground height less " @@ -1738,7 +1741,7 @@ bool Player::onAdd() world ); mPhysicsRep->setTransform( getTransform() ); } - + mAIController = NULL; return true; } @@ -2256,8 +2259,36 @@ void Player::advanceTime(F32 dt) } } +bool Player::setAIController(const char* controller) +{ + if (Sim::findObject(controller, mAIController)) + { + mAIController->setAIInfo(this); + return true; + } + + mAIController = NULL; + return false; +} + +DefineEngineMethod(Player, setAIController, bool, (const char* controller), , "") +{ + return object->setAIController(controller); +} + +DefineEngineMethod(Player, getAIController, AIController*, (), , "") +{ + return object->getAIController(); +} + + bool Player::getAIMove(Move* move) { + if (mAIController) + { + mAIController->getAIMove(move); //actual result + } + return false; } diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index b80b3f0e5..78367edab 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -50,6 +50,13 @@ class Player; class OpenVRTrackedObject; #endif +#ifdef TORQUE_NAVIGATION_ENABLED +#include "navigation/navPath.h" +#include "navigation/navMesh.h" +#include "navigation/coverPoint.h" +#endif // TORQUE_NAVIGATION_ENABLED +#include "AI/AIController.h" + //---------------------------------------------------------------------------- struct PlayerData: public ShapeBaseData { @@ -346,7 +353,8 @@ struct PlayerData: public ShapeBaseData { // Jump off surfaces at their normal rather than straight up bool jumpTowardsNormal; - + StringTableEntry mControlMap; + AIControllerData* mAIControllData; // For use if/when mPhysicsPlayer is created StringTableEntry physicsPlayerType; @@ -488,6 +496,7 @@ protected: SimObjectPtr mControlObject; ///< Controlling object + AIController* mAIController; /// @name Animation threads & data /// @{ @@ -754,6 +763,8 @@ public: void setMomentum(const Point3F &momentum) override; bool displaceObject(const Point3F& displaceVector) override; virtual bool getAIMove(Move*); + bool setAIController(const char* controller); + AIController* getAIController() { return mAIController; }; bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here? diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 706f54b72..66a48fc29 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -1895,7 +1895,6 @@ public: void registerCollisionCallback(CollisionEventCallback*); void unregisterCollisionCallback(CollisionEventCallback*); -protected: enum { ANIM_OVERRIDDEN = BIT(0), BLOCK_USER_CONTROL = BIT(1), @@ -1903,6 +1902,8 @@ protected: BAD_ANIM_ID = 999999999, BLENDED_CLIP = 0x80000000, }; + U8 anim_clip_flags; +protected: struct BlendThread { TSThread* thread; @@ -1910,7 +1911,6 @@ protected: }; Vector blend_clips; static U32 unique_anim_tag_counter; - U8 anim_clip_flags; S32 last_anim_id; U32 last_anim_tag; U32 last_anim_lock_tag; From 19e73c0be29d20cc44baed6f2df6c03d24d142b4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 15:34:58 -0500 Subject: [PATCH 22/74] expose the AIPlayerControllerData subtype to console --- Engine/source/T3D/AI/AIController.cpp | 1 + Engine/source/T3D/AI/AIController.h | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index ba14b2c34..deb3450b9 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -389,6 +389,7 @@ void AIControllerData::initPersistFields() //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- +IMPLEMENT_CO_DATABLOCK_V1(AIPlayerControllerData); void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) { Player* po = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 69ebbe27f..8d780c362 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -147,9 +147,13 @@ public: void resolveStuck(AIController* obj); }; -class AIPlayerControllerData : AIControllerData +class AIPlayerControllerData : public AIControllerData { + typedef AIControllerData Parent; + +public: void resolvePitch(AIController* obj, Point3F location, Move* movePtr); + DECLARE_CONOBJECT(AIPlayerControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED #endif //_AICONTROLLER_H_ From 201b7bf695586709d77a4e77b9f52be0f9eed910 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 17:00:36 -0500 Subject: [PATCH 23/74] aiController.setMoveDestination test --- Engine/source/T3D/AI/AIController.h | 2 +- Engine/source/T3D/AI/AINavigation.cpp | 16 ++++++++++++++++ Engine/source/T3D/player.cpp | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 8d780c362..db13b6ee4 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -126,7 +126,7 @@ class AIControllerData : public SimDataBlock { public: - AIControllerData() {}; + AIControllerData() { mMoveTolerance = 0.25; mFollowTolerance = 1.0; mAttackRadius = 2.0; mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30;}; ~AIControllerData() {}; static void initPersistFields(); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 18efeb3da..ed0fce56f 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -257,3 +257,19 @@ void AINavigation::clearPath() // Reset path data. mPathData = PathData(); } + +DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), + "@brief Tells the AI to move to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@param slowDown A boolean value. If set to true, the bot will slow down " + "when it gets within 5-meters of its move destination. If false, the bot " + "will stop abruptly when it reaches the move destination. By default, this is true.\n\n" + + "@note Upon reaching a move destination, the bot will clear its move destination and " + "calls to getMoveDestination will return \"0 0 0\"." + + "@see getMoveDestination()\n") +{ + object->getNav()->setMoveDestination(goal, slowDown); +} diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index cd32ea024..4552e5618 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2287,6 +2287,7 @@ bool Player::getAIMove(Move* move) if (mAIController) { mAIController->getAIMove(move); //actual result + return true; } return false; From 2fe36a571b937af9d902c0131c45ff331f74be54 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 18:01:18 -0500 Subject: [PATCH 24/74] setaicontroller: use Ids get rid of duplicated mMoveDestination --- Engine/source/T3D/AI/AIController.cpp | 18 +++++++++--------- Engine/source/T3D/AI/AIController.h | 1 - Engine/source/T3D/AI/AINavigation.cpp | 1 + Engine/source/T3D/player.cpp | 4 ++-- Engine/source/T3D/player.h | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index deb3450b9..3197f3579 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -117,7 +117,7 @@ bool AIController::getAIMove(Move* movePtr) if (getAim()->mObj || getAim()->mPosSet) mMovement.mAimLocation = getAim()->getPosition(); else - mMovement.mAimLocation = mMovement.mMoveDestination; + mMovement.mAimLocation = getNav()->mMoveDestination; mControllerData->resolveYaw(this, location, movePtr); mControllerData->resolvePitch(this, location, movePtr); @@ -232,8 +232,8 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m // Move towards the destination if (obj->mMovement.mMoveState != AIController::ModeStop) { - F32 xDiff = obj->mMovement.mMoveDestination.x - location.x; - F32 yDiff = obj->mMovement.mMoveDestination.y - location.y; + F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; + F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector(); // Check if we should mMove, or if we are 'close enough' @@ -246,22 +246,22 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m { // Build move direction in world space if (mIsZero(xDiff)) - movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -1.0f : 1.0f; + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) - movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -1.0f : 1.0f; + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); - movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -value : value; - movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -1.0f : 1.0f; + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); - movePtr->x = (location.x > obj->mMovement.mMoveDestination.x) ? -value : value; - movePtr->y = (location.y > obj->mMovement.mMoveDestination.y) ? -1.0f : 1.0f; + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index db13b6ee4..bc5ad3a33 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -89,7 +89,6 @@ public: Point3F mLastLocation; // For stuck check S32 mMoveStuckTestCountdown; // The current countdown until at AI starts to check if it is stuck Point3F mAimLocation; - Point3F mMoveDestination; // move triggers bool mMoveTriggers[MaxTriggerKeys]; void stopMove(); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index ed0fce56f..d9f4bb3ea 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -25,6 +25,7 @@ AINavigation::AINavigation(AIController* controller) { mControllerRef = controller; + mJump = None; } NavMesh* AINavigation::findNavMesh() const diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 4552e5618..42e8a9d5f 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2259,7 +2259,7 @@ void Player::advanceTime(F32 dt) } } -bool Player::setAIController(const char* controller) +bool Player::setAIController(S32 controller) { if (Sim::findObject(controller, mAIController)) { @@ -2271,7 +2271,7 @@ bool Player::setAIController(const char* controller) return false; } -DefineEngineMethod(Player, setAIController, bool, (const char* controller), , "") +DefineEngineMethod(Player, setAIController, bool, (S32 controller), , "") { return object->setAIController(controller); } diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index 78367edab..752ea9ca1 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -763,7 +763,7 @@ public: void setMomentum(const Point3F &momentum) override; bool displaceObject(const Point3F& displaceVector) override; virtual bool getAIMove(Move*); - bool setAIController(const char* controller); + bool setAIController(S32 controller); AIController* getAIController() { return mAIController; }; bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here? From f00b8e1ae0d174c314e47083747176300c887989 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 15 Apr 2025 18:23:12 -0500 Subject: [PATCH 25/74] toEuler. n ot getforwardvector --- Engine/source/T3D/AI/AIController.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 3197f3579..49d3e2a76 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -75,7 +75,7 @@ bool AIController::getAIMove(Move* movePtr) MatrixF eye; sbo->getEyeTransform(&eye); Point3F location = eye.getPosition(); - Point3F rotation = sbo->getTransform().getForwardVector(); + Point3F rotation = sbo->getTransform().toEuler(); #ifdef TORQUE_NAVIGATION_ENABLED if (sbo->getDamageState() == ShapeBase::Enabled) @@ -190,7 +190,7 @@ void AIControllerData::resolveYaw(AIController* obj, Point3F location, Move* mov { F32 xDiff = obj->mMovement.mAimLocation.x - location.x; F32 yDiff = obj->mMovement.mAimLocation.y - location.y; - Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector(); + Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); if (!mIsZero(xDiff) || !mIsZero(yDiff)) { @@ -234,7 +234,7 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m { F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; - Point3F rotation = obj->getAIInfo()->mObj->getTransform().getForwardVector(); + Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); // Check if we should mMove, or if we are 'close enough' if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) From f0c0f3c42c3862b2e2250d18b19e00d78eccd95b Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 16 Apr 2025 13:08:39 -0500 Subject: [PATCH 26/74] projectile augs use impactforce to applyimpulse for stock physics too add an optional explodeOnTmeout for nonballistic projectiles, track if they hit something before their armingdelay is up, and delete them next simulation cycle --- Engine/source/T3D/projectile.cpp | 37 ++++++++++++++++++++++++++------ Engine/source/T3D/projectile.h | 4 ++-- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Engine/source/T3D/projectile.cpp b/Engine/source/T3D/projectile.cpp index 56f8af638..58c6f3aa6 100644 --- a/Engine/source/T3D/projectile.cpp +++ b/Engine/source/T3D/projectile.cpp @@ -56,6 +56,7 @@ #include "T3D/decal/decalData.h" #include "T3D/lightDescription.h" #include "console/engineAPI.h" +#include "T3D/rigidShape.h" IMPLEMENT_CO_DATABLOCK_V1(ProjectileData); @@ -163,6 +164,7 @@ ProjectileData::ProjectileData() scale.set( 1.0f, 1.0f, 1.0f ); isBallistic = false; + mExplodeOnTmeout = false; velInheritFactor = 1.0f; muzzleVelocity = 50; @@ -203,6 +205,7 @@ ProjectileData::ProjectileData(const ProjectileData& other, bool temp_clone) : G muzzleVelocity = other.muzzleVelocity; impactForce = other.impactForce; isBallistic = other.isBallistic; + mExplodeOnTmeout = other.mExplodeOnTmeout; bounceElasticity = other.bounceElasticity; bounceFriction = other.bounceFriction; gravityMod = other.gravityMod; @@ -285,6 +288,8 @@ void ProjectileData::initPersistFields() addProtectedFieldV("fadeDelay", TypeRangedS32, Offset(fadeDelay, ProjectileData), &setFadeDelay, &getScaledValue, &CommonValidators::NaturalNumber, "@brief Amount of time, in milliseconds, before the projectile begins to fade out.\n\n" "This value must be smaller than the projectile's lifetime to have an affect."); + addField("explodeOnTmeout", TypeBool, Offset(mExplodeOnTmeout, ProjectileData), + "@brief Detetmines if the projectile should explode on timeout"); addField("isBallistic", TypeBool, Offset(isBallistic, ProjectileData), "@brief Detetmines if the projectile should be affected by gravity and whether or not " "it bounces before exploding.\n\n"); @@ -455,13 +460,14 @@ void ProjectileData::packData(BitStream* stream) stream->write(armingDelay); stream->write(fadeDelay); + stream->writeFlag(mExplodeOnTmeout); if(stream->writeFlag(isBallistic)) { stream->write(gravityMod); stream->write(bounceElasticity); stream->write(bounceFriction); } - + } void ProjectileData::unpackData(BitStream* stream) @@ -514,6 +520,7 @@ void ProjectileData::unpackData(BitStream* stream) stream->read(&armingDelay); stream->read(&fadeDelay); + mExplodeOnTmeout = stream->readFlag(); isBallistic = stream->readFlag(); if(isBallistic) { @@ -611,6 +618,7 @@ Projectile::Projectile() mProjectileShape( NULL ), mActivateThread( NULL ), mMaintainThread( NULL ), + mHasHit(false), mHasExploded( false ), mFadeValue( 1.0f ) { @@ -1128,10 +1136,18 @@ void Projectile::processTick( const Move *move ) void Projectile::simulate( F32 dt ) { - if ( isServerObject() && mCurrTick >= mDataBlock->lifetime ) + if ( isServerObject() ) { - deleteObject(); - return; + if (mCurrTick >= (mDataBlock->lifetime - TickMs)) + { + if (mDataBlock->mExplodeOnTmeout) + explode(mCurrPosition, Point3F::UnitZ, VehicleObjectType); + } + if (mCurrTick >= mDataBlock->lifetime || (mHasHit && mCurrTick < mDataBlock->armingDelay)) + { + deleteObject(); + return; + } } if ( mHasExploded ) @@ -1167,9 +1183,16 @@ void Projectile::simulate( F32 dt ) if ( mPhysicsWorld ) hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce ); - else + else + { hit = getContainer()->castRay(oldPosition, newPosition, dynamicCollisionMask | staticCollisionMask, &rInfo); - + if (hit && rInfo.object->getTypeMask() & VehicleObjectType) + { + RigidShape* aRigid = dynamic_cast(rInfo.object); + if (aRigid) + aRigid->applyImpulse(rInfo.point, Point3F(newPosition - oldPosition) * mDataBlock->impactForce); + } + } if ( hit ) { // make sure the client knows to bounce @@ -1237,6 +1260,8 @@ void Projectile::simulate( F32 dt ) else { mCurrVelocity = Point3F::Zero; + newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f; + mHasHit = true; } } diff --git a/Engine/source/T3D/projectile.h b/Engine/source/T3D/projectile.h index 671c3f98f..04e91aa60 100644 --- a/Engine/source/T3D/projectile.h +++ b/Engine/source/T3D/projectile.h @@ -87,9 +87,9 @@ public: /// Force imparted on a hit object. F32 impactForce; + bool mExplodeOnTmeout; /// Should it arc? bool isBallistic; - /// How HIGH should it bounce (parallel to normal), [0,1] F32 bounceElasticity; /// How much momentum should be lost when it bounces (perpendicular to normal), [0,1] @@ -274,7 +274,7 @@ protected: LightInfo *mLight; LightState mLightState; - + bool mHasHit; bool mHasExploded; ///< Prevent rendering, lighting, and duplicate explosions. F32 mFadeValue; ///< set in processTick, interpolation between fadeDelay and lifetime ///< in data block From f3ef698e890b41a3f7a7fe22c63b8d893b715961 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 16 Apr 2025 17:27:26 -0500 Subject: [PATCH 27/74] getmovedestination, set/get speed, targetting script commands todo: need to see why getAIController().setAimLocation("10 10 0"); seems to get into an onreachdestination+stuck callback loop --- Engine/source/T3D/AI/AIAimTarget.cpp | 146 +++++++++++++++++++++++++- Engine/source/T3D/AI/AIAimTarget.h | 5 +- Engine/source/T3D/AI/AIController.cpp | 22 ++++ Engine/source/T3D/AI/AIController.h | 2 + Engine/source/T3D/AI/AINavigation.cpp | 14 +++ Engine/source/T3D/AI/AINavigation.h | 1 + 6 files changed, 187 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.cpp b/Engine/source/T3D/AI/AIAimTarget.cpp index 907b2da3d..6e4b3d299 100644 --- a/Engine/source/T3D/AI/AIAimTarget.cpp +++ b/Engine/source/T3D/AI/AIAimTarget.cpp @@ -25,7 +25,31 @@ static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType; -bool AIAimTarget::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) +F32 AIAimTarget::getTargetDistance(SceneObject* target, bool _checkEnabled) +{ + if (!target) + { + target = mObj.getPointer(); + if (!target) + return F32_MAX; + } + + if (_checkEnabled) + { + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase* shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false; + } + else + return F32_MAX; + } + + return (getPosition() - target->getPosition()).len(); +} + +bool AIAimTarget::checkInLos(SceneObject* target, bool _useMuzzle, bool _checkEnabled) { ShapeBase* sbo = dynamic_cast(getCtrl()->getAIInfo()->mObj.getPointer()); if (!target) @@ -75,3 +99,123 @@ bool AIAimTarget::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabl } return hit; } + +bool AIAimTarget::checkInFoV(SceneObject* target, F32 camFov, bool _checkEnabled) +{ + ShapeBase* sbo = dynamic_cast(getCtrl()->getAIInfo()->mObj.getPointer()); + if (!target) + { + target = dynamic_cast(mObj.getPointer()); + if (!target) + return false; + } + if (_checkEnabled) + { + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase* shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != ShapeBase::Enabled) return false; + } + else + return false; + } + + MatrixF cam = sbo->getTransform(); + Point3F camPos; + VectorF camDir; + + cam.getColumn(3, &camPos); + cam.getColumn(1, &camDir); + + camFov = mDegToRad(camFov) / 2; + + Point3F shapePos = target->getBoxCenter(); + VectorF shapeDir = shapePos - camPos; + // Test to see if it's within our viewcone, this test doesn't + // actually match the viewport very well, should consider + // projection and box test. + shapeDir.normalize(); + F32 dot = mDot(shapeDir, camDir); + return (dot > mCos(camFov)); +} + +DefineEngineMethod(AIController, setAimLocation, void, (Point3F target), , + "@brief Tells the AIPlayer to aim at the location provided.\n\n" + + "@param target An \"x y z\" position in the game world to target.\n\n" + + "@see getAimLocation()\n") +{ + object->setAim(target); +} + +DefineEngineMethod(AIController, getAimLocation, Point3F, (), , + "@brief Returns the point the AIPlayer is aiming at.\n\n" + + "This will reflect the position set by setAimLocation(), " + "or the position of the object that the bot is now aiming at. " + "If the bot is not aiming at anything, this value will " + "change to whatever point the bot's current line-of-sight intercepts." + + "@return World space coordinates of the object AI is aiming at. Formatted as \"X Y Z\".\n\n" + + "@see setAimLocation()\n" + "@see setAimObject()\n") +{ + return object->getAim()->getPosition(); +} + +DefineEngineMethod(AIController, setAimObject, void, (const char* objName, Point3F offset), (Point3F::Zero), "( GameBase obj, [Point3F offset] )" + "Sets the bot's target object. Optionally set an offset from target location." + "@hide") +{ + // Find the target + SceneObject* targetObject; + if (Sim::findObject(objName, targetObject)) + { + + object->setAim(targetObject, 0.0f, offset); + } + else + object->setAim(0, 0.0f, offset); +} + +DefineEngineMethod(AIController, getAimObject, S32, (), , + "@brief Gets the object the AIPlayer is targeting.\n\n" + + "@return Returns -1 if no object is being aimed at, " + "or the SimObjectID of the object the AIPlayer is aiming at.\n\n" + + "@see setAimObject()\n") +{ + SceneObject* obj = dynamic_cast(object->getAim()->mObj.getPointer()); + return obj ? obj->getId() : -1; +} + + +DefineEngineMethod(AIController, getTargetDistance, F32, (SceneObject* obj, bool checkEnabled), (nullAsType(), false), + "@brief The distance to a given target.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") +{ + return object->getAim()->getTargetDistance(obj, checkEnabled); +} + +DefineEngineMethod(AIController, checkInLos, bool, (SceneObject* obj, bool useMuzzle, bool checkEnabled), (nullAsType(), false, false), + "@brief Check whether an object is in line of sight.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@useMuzzle Use muzzle position. Otherwise use eye position. (defaults to false).\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") +{ + return object->getAim()->checkInLos(obj, useMuzzle, checkEnabled); +} + +DefineEngineMethod(AIController, checkInFoV, bool, (SceneObject* obj, F32 fov, bool checkEnabled), (nullAsType(), 45.0f, false), + "@brief Check whether an object is within a specified veiw cone.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@fov view angle in degrees.(Defaults to 45)\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") +{ + return object->getAim()->checkInFoV(obj, fov, checkEnabled); +} diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h index 650a4f9b8..f9b66a217 100644 --- a/Engine/source/T3D/AI/AIAimTarget.h +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -29,8 +29,9 @@ struct AIAimTarget : AIInfo Point3F mAimOffset; bool mTargetInLOS; // Is target object visible? Point3F getPosition() { return ((mObj) ? mObj->getPosition() : mPosition) + mAimOffset; } - bool checkInLos(GameBase* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); - bool checkInFoV(GameBase* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); + bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); + bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); + F32 getTargetDistance(SceneObject* target, bool _checkEnabled); AIAimTarget(AIController* controller) : Parent(controller) {}; AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 49d3e2a76..c51518a53 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -183,6 +183,28 @@ void AIController::clearCover() SAFE_DELETE(mCover); } +DefineEngineMethod(AIController, setMoveSpeed, void, (F32 speed), , + "@brief Sets the move speed for an AI object.\n\n" + + "@param speed A speed multiplier between 0.0 and 1.0. " + "This is multiplied by the AIPlayer's base movement rates (as defined in " + "its PlayerData datablock)\n\n" + + "@see getMoveDestination()\n") +{ + object->mMovement.setMoveSpeed(speed); +} + +DefineEngineMethod(AIController, getMoveSpeed, F32, (), , + "@brief Gets the move speed of an AI object.\n\n" + + "@return A speed multiplier between 0.0 and 1.0.\n\n" + + "@see setMoveSpeed()\n") +{ + return object->mMovement.getMoveSpeed(); +} + //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIControllerData); diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index bc5ad3a33..2f0839498 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -85,6 +85,8 @@ public: { MoveState mMoveState; F32 mMoveSpeed = 1.0; + void setMoveSpeed(F32 speed) { mMoveSpeed = speed; }; + F32 getMoveSpeed() { return mMoveSpeed; }; bool mMoveSlowdown; // Slowdown as we near the destination Point3F mLastLocation; // For stuck check S32 mMoveStuckTestCountdown; // The current countdown until at AI starts to check if it is stuck diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index d9f4bb3ea..63491b459 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -274,3 +274,17 @@ DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool s { object->getNav()->setMoveDestination(goal, slowDown); } + + +DefineEngineMethod(AIController, getMoveDestination, Point3F, (), , + "@brief Get the AIPlayer's current destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current move destination. If no move destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setMoveDestination()\n") +{ + return object->getNav()->getMoveDestination(); +} + diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 130305eea..2d6ce1ac3 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -61,6 +61,7 @@ struct AINavigation Point3F mMoveDestination; void setMoveDestination(const Point3F& location, bool slowdown); + Point3F getMoveDestination() { return mMoveDestination; }; void onReachDestination(); /// NavMesh we pathfind on. From 14a03dfc6c68419a358d470c118bd41b5e8cb4ea Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 16 Apr 2025 17:40:53 -0500 Subject: [PATCH 28/74] if you've stopped moving on purpose, you're not stuck --- Engine/source/T3D/AI/AIController.cpp | 1 + Engine/source/T3D/AI/AINavigation.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index c51518a53..e5d7a3ebe 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -322,6 +322,7 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m void AIControllerData::resolveStuck(AIController* obj) { + if (obj->mMovement.mMoveState == AIController::ModeStop) return; ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); // Don't check for ai stuckness if animation during // an anim-clip effect override. diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 63491b459..896586ff7 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -155,6 +155,7 @@ void AINavigation::onReachDestination() else #endif getCtrl()->throwCallback("onReachDestination"); + getCtrl()->mMovement.mMoveState = AIController::ModeStop; } bool AINavigation::setPathDestination(const Point3F& pos) From e37ae27bc02a4f1506bcb260e71cd12322f38698 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 16 Apr 2025 17:58:30 -0500 Subject: [PATCH 29/74] fix aim safeties to actually be safe, add an explicit clearAim script command --- Engine/source/T3D/AI/AIAimTarget.cpp | 5 +++++ Engine/source/T3D/AI/AIController.cpp | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.cpp b/Engine/source/T3D/AI/AIAimTarget.cpp index 6e4b3d299..35c7d927e 100644 --- a/Engine/source/T3D/AI/AIAimTarget.cpp +++ b/Engine/source/T3D/AI/AIAimTarget.cpp @@ -181,6 +181,11 @@ DefineEngineMethod(AIController, setAimObject, void, (const char* objName, Point object->setAim(0, 0.0f, offset); } +DefineEngineMethod(AIController, clearAim, void, (), , "clears the bot's target.") +{ + object->clearAim(); +} + DefineEngineMethod(AIController, getAimObject, S32, (), , "@brief Gets the object the AIPlayer is targeting.\n\n" diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index e5d7a3ebe..30530a3e5 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -111,10 +111,10 @@ bool AIController::getAIMove(Move* movePtr) // Orient towards the aim point, aim object, or towards // our destination. - if (getAim()->mObj || getAim()->mPosSet || mMovement.mMoveState != ModeStop) + if (getAim() || mMovement.mMoveState != ModeStop) { // Update the aim position if we're aiming for an object or explicit position - if (getAim()->mObj || getAim()->mPosSet) + if (getAim()) mMovement.mAimLocation = getAim()->getPosition(); else mMovement.mAimLocation = getNav()->mMoveDestination; @@ -129,7 +129,7 @@ bool AIController::getAIMove(Move* movePtr) // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. - if (getAim()->mObj) + if (getAim() && getAim()->mObj) { GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); if (getAim()->checkInLos(gbo)) From 4fb92f02a390ad5b93f00e61fcdb0561dc49bec6 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 01:27:08 -0500 Subject: [PATCH 30/74] completed list of roughly ported over scripthooks. todo: need to figure out why followobject is only hitting the first path node. likely amixup with goal handling --- Engine/source/T3D/AI/AIAimTarget.h | 2 +- Engine/source/T3D/AI/AIController.cpp | 87 +++++++++++- Engine/source/T3D/AI/AIController.h | 23 ++- Engine/source/T3D/AI/AICover.cpp | 87 ++++++++++++ Engine/source/T3D/AI/AICover.h | 5 +- Engine/source/T3D/AI/AIInfo.cpp | 2 +- Engine/source/T3D/AI/AIInfo.h | 2 +- Engine/source/T3D/AI/AINavigation.cpp | 131 +++++++++++++++++- Engine/source/T3D/AI/AINavigation.h | 11 +- Engine/source/T3D/player.cpp | 4 +- Engine/source/T3D/player.h | 2 +- Engine/source/navigation/guiNavEditorCtrl.cpp | 48 +++++-- Engine/source/navigation/guiNavEditorCtrl.h | 4 +- .../datablocks/defaultDatablocks.tscript | 5 + .../game/tools/navEditor/main.tscript | 3 +- .../game/tools/navEditor/navEditor.tscript | 10 +- 16 files changed, 386 insertions(+), 40 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h index f9b66a217..4e26f8306 100644 --- a/Engine/source/T3D/AI/AIAimTarget.h +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -28,7 +28,7 @@ struct AIAimTarget : AIInfo typedef AIInfo Parent; Point3F mAimOffset; bool mTargetInLOS; // Is target object visible? - Point3F getPosition() { return ((mObj) ? mObj->getPosition() : mPosition) + mAimOffset; } + Point3F getPosition() { return ((mObj.isValid()) ? mObj->getPosition() : mPosition) + mAimOffset; } bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); F32 getTargetDistance(SceneObject* target, bool _checkEnabled); diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 30530a3e5..2ede9aea9 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -82,25 +82,25 @@ bool AIController::getAIMove(Move* movePtr) { if (mMovement.mMoveState != ModeStop) getNav()->updateNavMesh(); - if (!getGoal()->mObj.isNull()) + if (getGoal() && !getGoal()->mObj.isNull()) { if (getNav()->mPathData.path.isNull()) { - if (getGoal()->getDist() > mControllerData->mMoveTolerance) - getNav()->followObject(getGoal()); + if (getGoal()->getDist() > mControllerData->mFollowTolerance) + getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); } else { - if (getGoal()->getDist() > mControllerData->mMoveTolerance) + if (getGoal()->getDist() > mControllerData->mFollowTolerance) getNav()->repath(); - if (getAim()->getDist() < mControllerData->mMoveTolerance) + if (getGoal()->getDist() < mControllerData->mFollowTolerance) { getNav()->clearPath(); mMovement.mMoveState = ModeStop; throwCallback("onTargetInRange"); } - else if (getAim()->getDist() < mControllerData->mAttackRadius) + else if (getGoal()->getDist() < mControllerData->mAttackRadius) { throwCallback("onTargetInFiringRange"); } @@ -178,11 +178,29 @@ bool AIController::getAIMove(Move* movePtr) void AIController::clearCover() { // Notify cover that we are no longer on our way. - if (!getCover()->mCoverPoint.isNull()) + if (getCover() && !getCover()->mCoverPoint.isNull()) getCover()->mCoverPoint->setOccupied(false); SAFE_DELETE(mCover); } +void AIController::Movement::stopMove() +{ + mMoveState = ModeStop; +#ifdef TORQUE_NAVIGATION_ENABLED + mControllerRef->getNav()->clearPath(); + mControllerRef->clearCover(); + mControllerRef->getNav()->clearFollow(); +#endif +} +void AIController::Movement::onStuck() +{ + mControllerRef->throwCallback("onMoveStuck"); +#ifdef TORQUE_NAVIGATION_ENABLED + if (!mControllerRef->getNav()->getPath().isNull()) + mControllerRef->getNav()->repath(); +#endif +} + DefineEngineMethod(AIController, setMoveSpeed, void, (F32 speed), , "@brief Sets the move speed for an AI object.\n\n" @@ -205,6 +223,60 @@ DefineEngineMethod(AIController, getMoveSpeed, F32, (), , return object->mMovement.getMoveSpeed(); } +DefineEngineMethod(AIController, stop, void, (), , + "@brief Tells the AIPlayer to stop moving.\n\n") +{ + object->mMovement.stopMove(); +} + + +/** + * Set the state of a movement trigger. + * + * @param slot The trigger slot to set + * @param isSet set/unset the trigger + */ +void AIController::TriggerState::setMoveTrigger(U32 slot, const bool isSet) +{ + if (slot >= MaxTriggerKeys) + { + Con::errorf("Attempting to set an invalid trigger slot (%i)", slot); + } + else + { + mMoveTriggers[slot] = isSet; // set the trigger + mControllerRef->getAIInfo()->mObj->setMaskBits(ShapeBase::NoWarpMask); // force the client to updateMove + } +} + +/** + * Get the state of a movement trigger. + * + * @param slot The trigger slot to query + * @return True if the trigger is set, false if it is not set + */ +bool AIController::TriggerState::getMoveTrigger(U32 slot) const +{ + if (slot >= MaxTriggerKeys) + { + Con::errorf("Attempting to get an invalid trigger slot (%i)", slot); + return false; + } + else + { + return mMoveTriggers[slot]; + } +} + +/** + * Clear the trigger state for all movement triggers. + */ +void AIController::TriggerState::clearMoveTriggers() +{ + for (U32 i = 0; i < MaxTriggerKeys; i++) + setMoveTrigger(i, false); +} + //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIControllerData); @@ -341,6 +413,7 @@ void AIControllerData::resolveStuck(AIController* obj) if (obj->mMovement.mMoveState != AIController::ModeSlowing || locationDelta == 0) { obj->mMovement.mMoveState = AIController::ModeStuck; + obj->mMovement.onStuck(); obj->throwCallback("onStuck"); } } diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 2f0839498..d64148a9e 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -29,7 +29,6 @@ #include "AICover.h" #include "AINavigation.h" class AIControllerData; -class AIController; //----------------------------------------------------------------------------- class AIController : public SimObject { @@ -83,6 +82,7 @@ public: AINavigation* getNav() { return mNav; }; struct Movement { + AIController* mControllerRef; MoveState mMoveState; F32 mMoveSpeed = 1.0; void setMoveSpeed(F32 speed) { mMoveSpeed = speed; }; @@ -99,6 +99,8 @@ public: struct TriggerState { + AIController* mControllerRef; + bool mMoveTriggers[MaxTriggerKeys]; // Trigger sets/gets void setMoveTrigger(U32 slot, const bool isSet = true); bool getMoveTrigger(U32 slot) const; @@ -109,12 +111,18 @@ public: static void initPersistFields(); AIController() { + for (S32 i = 0; i < MaxTriggerKeys; i++) + mTriggerState.mMoveTriggers[i] = false; + + mMovement.mControllerRef = this; + mTriggerState.mControllerRef = this; mControllerData = NULL; mAIInfo = new AIInfo(this); mGoal = new AIGoal(this); mAimTarget = new AIAimTarget(this); mCover = new AICover(this); mNav = new AINavigation(this); + mMovement.mMoveState = ModeStop; }; DECLARE_CONOBJECT(AIController); @@ -127,7 +135,16 @@ class AIControllerData : public SimDataBlock { public: - AIControllerData() { mMoveTolerance = 0.25; mFollowTolerance = 1.0; mAttackRadius = 2.0; mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30;}; + AIControllerData() + { + mMoveTolerance = 0.25; + mFollowTolerance = 1.0; + mAttackRadius = 2.0; + mMoveStuckTolerance = 0.01f; + mMoveStuckTestDelay = 30; + mLinkTypes = LinkData(AllFlags); + mNavSize = AINavigation::Regular; + }; ~AIControllerData() {}; static void initPersistFields(); @@ -140,7 +157,7 @@ public: S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck /// Types of link we can use. LinkData mLinkTypes; - + AINavigation::NavSize mNavSize; void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolvePitch(AIController* obj, Point3F location, Move* movePtr) {}; void resolveRoll(AIController* obj, Point3F location, Move* movePtr); diff --git a/Engine/source/T3D/AI/AICover.cpp b/Engine/source/T3D/AI/AICover.cpp index 509b0f0c0..97cf82a39 100644 --- a/Engine/source/T3D/AI/AICover.cpp +++ b/Engine/source/T3D/AI/AICover.cpp @@ -19,3 +19,90 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "AICover.h" +#include "AIController.h" + +struct CoverSearch +{ + Point3F loc; + Point3F from; + F32 dist; + F32 best; + CoverPoint* point; + CoverSearch() : loc(0, 0, 0), from(0, 0, 0) + { + best = -FLT_MAX; + point = NULL; + dist = FLT_MAX; + } +}; + +static void findCoverCallback(SceneObject* obj, void* key) +{ + CoverPoint* p = dynamic_cast(obj); + if (!p || p->isOccupied()) + return; + CoverSearch* s = static_cast(key); + Point3F dir = s->from - p->getPosition(); + dir.normalizeSafe(); + // Score first based on angle of cover point to enemy. + F32 score = mDot(p->getNormal(), dir); + // Score also based on distance from seeker. + score -= (p->getPosition() - s->loc).len() / s->dist; + // Finally, consider cover size. + score += (p->getSize() + 1) / CoverPoint::NumSizes; + score *= p->getQuality(); + if (score > s->best) + { + s->best = score; + s->point = p; + } +} + +bool AIController::findCover(const Point3F& from, F32 radius) +{ + if (radius <= 0) + return false; + + // Create a search state. + CoverSearch s; + s.loc = getAIInfo()->getPosition(); + s.dist = radius; + // Direction we seek cover FROM. + s.from = from; + + // Find cover points. + Box3F box(radius * 2.0f); + box.setCenter(getAIInfo()->getPosition()); + getAIInfo()->mObj->getContainer()->findObjects(box, MarkerObjectType, findCoverCallback, &s); + + // Go to cover! + if (s.point) + { + // Calling setPathDestination clears cover... + bool foundPath = getNav()->setPathDestination(s.point->getPosition()); + setCover(s.point); + s.point->setOccupied(true); + return foundPath; + } + return false; +} + + +DefineEngineMethod(AIController, findCover, S32, (Point3F from, F32 radius), , + "@brief Tells the AI to find cover nearby.\n\n" + + "@param from Location to find cover from (i.e., enemy position).\n" + "@param radius Distance to search for cover.\n" + "@return Cover point ID if cover was found, -1 otherwise.\n\n") +{ + if (object->findCover(from, radius)) + { + CoverPoint* cover = object->getCover()->mCoverPoint.getObject(); + return cover ? cover->getId() : -1; + } + else + { + return -1; + } +} diff --git a/Engine/source/T3D/AI/AICover.h b/Engine/source/T3D/AI/AICover.h index d9d1cb638..4fe33cab7 100644 --- a/Engine/source/T3D/AI/AICover.h +++ b/Engine/source/T3D/AI/AICover.h @@ -23,6 +23,9 @@ #define _AICOVER_H_ #include "AIInfo.h" +#include "navigation/coverPoint.h" + + struct AICover : AIInfo { @@ -30,7 +33,7 @@ struct AICover : AIInfo /// Pointer to a cover point. SimObjectPtr mCoverPoint; AICover(AIController* controller) : Parent(controller) {}; - AICover(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; + AICover(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mCoverPoint = dynamic_cast(objIn.getPointer());}; AICover(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; }; diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp index 3ce55d86c..c2c11de9a 100644 --- a/Engine/source/T3D/AI/AIInfo.cpp +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -53,7 +53,7 @@ AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn) F32 AIInfo::getDist() { AIInfo* controlObj = getCtrl()->getAIInfo(); - F32 ret = VectorF(controlObj->mObj->getPosition() - getPosition()).len(); + F32 ret = VectorF(controlObj->getPosition() - getPosition()).len(); ret -= controlObj->mRadius + mRadius; return ret; } diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h index db0dc191d..a392f4553 100644 --- a/Engine/source/T3D/AI/AIInfo.h +++ b/Engine/source/T3D/AI/AIInfo.h @@ -36,7 +36,7 @@ struct AIInfo Point3F mPosition, mLastPos; bool mPosSet; F32 mRadius; - Point3F getPosition() { return (mObj) ? mObj->getPosition() : mPosition; } + Point3F getPosition() { return (mObj.isValid()) ? mObj->getPosition() : mPosition; } F32 getDist(); AIInfo() = delete; AIInfo(AIController* controller); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 896586ff7..a4511921f 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -26,6 +26,7 @@ AINavigation::AINavigation(AIController* controller) { mControllerRef = controller; mJump = None; + mNavSize = Regular; } NavMesh* AINavigation::findNavMesh() const @@ -40,6 +41,19 @@ NavMesh* AINavigation::findNavMesh() const NavMesh* m = static_cast(set->at(i)); if (m->getWorldBox().isContained(gbo->getWorldBox())) { + // Check that mesh size is appropriate. + if (gbo->isMounted()) + { + if (!m->mVehicles) + continue; + } + else + { + if ((getNavSize() == Small && !m->mSmallCharacters) || + (getNavSize() == Regular && !m->mRegularCharacters) || + (getNavSize() == Large && !m->mLargeCharacters)) + continue; + } if (!mesh || m->getWorldBox().getVolume() < mesh->getWorldBox().getVolume()) mesh = m; } @@ -101,6 +115,8 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; + if (!mControllerRef->getGoal()) return; + // If we're following, get their position. mPathData.path->mTo = mControllerRef->getGoal()->getPosition(); // Update from position and replan. @@ -212,24 +228,21 @@ bool AINavigation::setPathDestination(const Point3F& pos) } } -void AINavigation::followObject(AIInfo* targ) +void AINavigation::followObject() { - if (!targ) return; - - if (targ->getDist() < mControllerRef->mControllerData->mMoveTolerance) + if ((mControllerRef->getGoal()->mLastPos - mControllerRef->getAIInfo()->getPosition()).len() < mControllerRef->mControllerData->mMoveTolerance) return; - if (setPathDestination(targ->getPosition())) + if (setPathDestination(mControllerRef->getGoal()->getPosition())) { mControllerRef->clearCover(); - mControllerRef->setGoal(targ); } } void AINavigation::followObject(SceneObject* obj, F32 radius) { mControllerRef->setGoal(obj, radius); - followObject(mControllerRef->getGoal()); + followObject(); } void AINavigation::clearFollow() @@ -289,3 +302,107 @@ DefineEngineMethod(AIController, getMoveDestination, Point3F, (), , return object->getNav()->getMoveDestination(); } +DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), , + "@brief Tells the AI to find a path to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@return True if a path was found.\n\n" + + "@see getPathDestination()\n" + "@see setMoveDestination()\n") +{ + return object->getNav()->setPathDestination(goal); +} + + +DefineEngineMethod(AIController, getPathDestination, Point3F, (), , + "@brief Get the AIPlayer's current pathfinding destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current path destination. If no path destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setPathDestination()\n") +{ + return object->getNav()->getPathDestination(); +} + +DefineEngineMethod(AIController, followNavPath, void, (SimObjectId obj), , + "@brief Tell the AIPlayer to follow a path.\n\n" + + "@param obj ID of a NavPath object for the character to follow.") +{ + NavPath* path; + if (Sim::findObject(obj, path)) + object->getNav()->followNavPath(path); +} + +DefineEngineMethod(AIController, followObject, void, (SimObjectId obj, F32 radius), , + "@brief Tell the AIPlayer to follow another object.\n\n" + + "@param obj ID of the object to follow.\n" + "@param radius Maximum distance we let the target escape to.") +{ + SceneObject* follow; + object->getNav()->clearPath(); + object->clearCover(); + object->getNav()->clearFollow(); + + if (Sim::findObject(obj, follow)) + object->getNav()->followObject(follow, radius); +} + + +DefineEngineMethod(AIController, repath, void, (), , + "@brief Tells the AI to re-plan its path. Does nothing if the character " + "has no path, or if it is following a mission path.\n\n") +{ + object->getNav()->repath(); +} + +DefineEngineMethod(AIController, findNavMesh, S32, (), , + "@brief Get the NavMesh object this AIPlayer is currently using.\n\n" + + "@return The ID of the NavPath object this character is using for " + "pathfinding. This is determined by the character's location, " + "navigation type and other factors. Returns -1 if no NavMesh is " + "found.") +{ + NavMesh* mesh = object->getNav()->getNavMesh(); + return mesh ? mesh->getId() : -1; +} + +DefineEngineMethod(AIController, getNavMesh, S32, (), , + "@brief Return the NavMesh this AIPlayer is using to navigate.\n\n") +{ + NavMesh* m = object->getNav()->getNavMesh(); + return m ? m->getId() : 0; +} + +DefineEngineMethod(AIController, setNavSize, void, (const char* size), , + "@brief Set the size of NavMesh this character uses. One of \"Small\", \"Regular\" or \"Large\".") +{ + if (!String::compare(size, "Small")) + object->getNav()->setNavSize(AINavigation::Small); + else if (!String::compare(size, "Regular")) + object->getNav()->setNavSize(AINavigation::Regular); + else if (!String::compare(size, "Large")) + object->getNav()->setNavSize(AINavigation::Large); + else + Con::errorf("AIPlayer::setNavSize: no such size '%s'.", size); +} + +DefineEngineMethod(AIController, getNavSize, const char*, (), , + "@brief Return the size of NavMesh this character uses for pathfinding.") +{ + switch (object->getNav()->getNavSize()) + { + case AINavigation::Small: + return "Small"; + case AINavigation::Regular: + return "Regular"; + case AINavigation::Large: + return "Large"; + } + return ""; +} diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 2d6ce1ac3..2efcbe536 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -59,6 +59,14 @@ struct AINavigation Ledge, ///< Jump when we walk off a ledge. }; + enum NavSize { + Small, + Regular, + Large + } mNavSize; + void setNavSize(NavSize size) { mNavSize = size; updateNavMesh(); } + NavSize getNavSize() const { return mNavSize; } + Point3F mMoveDestination; void setMoveDestination(const Point3F& location, bool slowdown); Point3F getMoveDestination() { return mMoveDestination; }; @@ -68,6 +76,7 @@ struct AINavigation SimObjectPtr mNavMesh; NavMesh* findNavMesh() const; void updateNavMesh(); + NavMesh* getNavMesh() const { return mNavMesh; } PathData mPathData; JumpStates mJump; @@ -81,7 +90,7 @@ struct AINavigation SimObjectPtr getPath() { return mPathData.path; }; void followNavPath(NavPath* path); - void followObject(AIInfo* targ); + void followObject(); void followObject(SceneObject* obj, F32 radius); void clearFollow(); /// Move to the specified node in the current path. diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 42e8a9d5f..38ef5efcb 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2259,14 +2259,14 @@ void Player::advanceTime(F32 dt) } } -bool Player::setAIController(S32 controller) +bool Player::setAIController(SimObjectId controller) { if (Sim::findObject(controller, mAIController)) { mAIController->setAIInfo(this); return true; } - + Con::errorf("unable to find AIController : %i", controller); mAIController = NULL; return false; } diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index 752ea9ca1..df10ba291 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -763,7 +763,7 @@ public: void setMomentum(const Point3F &momentum) override; bool displaceObject(const Point3F& displaceVector) override; virtual bool getAIMove(Move*); - bool setAIController(S32 controller); + bool setAIController(SimObjectId controller); AIController* getAIController() { return mAIController; }; bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here? diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index bf164bc4b..b67272aff 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -225,8 +225,18 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) SimGroup* missionCleanup = dynamic_cast(cleanup); missionCleanup->addObject(obj); } - mPlayer = static_cast(obj); - Con::executef(this, "onPlayerSelected", Con::getIntArg(mPlayer->mLinkTypes.getFlags())); + mPlayer = obj; + Player* po = dynamic_cast(obj); + if (!po) return; //todo, more types + if (po->getAIController()) + { + if (po->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(po->getAIController()->mControllerData->mLinkTypes.getFlags())); + } + else + { + Con::executef(this, "onPlayerSelected"); + } } } @@ -383,16 +393,34 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) // Select/move character else { - if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType, &ri)) + if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri)) { - if(dynamic_cast(ri.object)) + if(ri.object) { - mPlayer = dynamic_cast(ri.object); - Con::executef(this, "onPlayerSelected", Con::getIntArg(mPlayer->mLinkTypes.getFlags())); + mPlayer = ri.object; + Player* po = dynamic_cast(ri.object); + if (!po) return; //todo, more types + if (po->getAIController()) + { + if (po->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(po->getAIController()->mControllerData->mLinkTypes.getFlags())); + } + else + { + Con::executef(this, "onPlayerSelected"); + } + } + } + else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) + { + Player* po = dynamic_cast(mPlayer.getPointer()); + if (!po) return; //todo, more types + if (po->getAIController()) + { + if (po->getAIController()->mControllerData) + po->getAIController()->getNav()->setPathDestination(ri.point); } } - else if(!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) - mPlayer->setPathDestination(ri.point); } } } @@ -455,8 +483,8 @@ void GuiNavEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event) if(mMode == mTestMode) { - if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType, &ri)) - mCurPlayer = dynamic_cast(ri.object); + if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri)) + mCurPlayer = ri.object; else mCurPlayer = NULL; } diff --git a/Engine/source/navigation/guiNavEditorCtrl.h b/Engine/source/navigation/guiNavEditorCtrl.h index 6e0b34f5e..c5d2f1b5a 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.h +++ b/Engine/source/navigation/guiNavEditorCtrl.h @@ -155,8 +155,8 @@ protected: /// @name Test mode /// @{ - SimObjectPtr mPlayer; - SimObjectPtr mCurPlayer; + SimObjectPtr mPlayer; + SimObjectPtr mCurPlayer; /// @} diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index 1035f2d9a..fbd8cd5fc 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -169,3 +169,8 @@ datablock LightAnimData( SpinLightAnim ) rotKeys[2] = "az"; rotSmooth[2] = true; }; + +datablock AIPlayerControllerData( aiPlayerControl ) +{ + +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/navEditor/main.tscript b/Templates/BaseGame/game/tools/navEditor/main.tscript index 56739e3a6..bfc06d103 100644 --- a/Templates/BaseGame/game/tools/navEditor/main.tscript +++ b/Templates/BaseGame/game/tools/navEditor/main.tscript @@ -104,6 +104,7 @@ function ENavEditorSettingsPage::init(%this) { // Initialises the settings controls in the settings dialog box. %this-->SpawnClassOptions.clear(); + %this-->SpawnClassOptions.add("Player"); %this-->SpawnClassOptions.add("AIPlayer"); %this-->SpawnClassOptions.setFirstSelected(); } @@ -240,7 +241,7 @@ function NavEditorPlugin::initSettings(%this) { EditorSettings.beginGroup("NavEditor", true); - EditorSettings.setDefaultValue("SpawnClass", "AIPlayer"); + EditorSettings.setDefaultValue("SpawnClass", "Player"); EditorSettings.setDefaultValue("SpawnDatablock", "DefaultPlayerData"); EditorSettings.endGroup(); diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 8e2234646..14d758923 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -459,6 +459,12 @@ function NavEditorGui::onLinkSelected(%this, %flags) function NavEditorGui::onPlayerSelected(%this, %flags) { + if (!isObject(%this.getPlayer().aiController)) + { + %this.getPlayer().aiController = new AIController(){ ControllerData = aiPlayerControl; }; + %this.getPlayer().setAIController(%this.getPlayer().aiController); + } + NavMeshIgnore(%this.getPlayer(), true); updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags); } @@ -526,7 +532,7 @@ function NavEditorGui::findCover(%this) %text = NavEditorOptionsWindow-->TestProperties->CoverPosition.getText(); if(%text !$= "") %pos = eval("return " @ %text); - %this.getPlayer().findCover(%pos, NavEditorOptionsWindow-->TestProperties->CoverRadius.getText()); + %this.getPlayer().getAIController().findCover(%pos, NavEditorOptionsWindow-->TestProperties->CoverRadius.getText()); } } @@ -547,7 +553,7 @@ function NavEditorGui::followObject(%this) toolsMessageBoxOk("Error", "Cannot find object" SPC %text); } if(isObject(%obj)) - %this.getPlayer().followObject(%obj, NavEditorOptionsWindow-->TestProperties->FollowRadius.getText()); + %this.getPlayer().getAIController().followObject(%obj, NavEditorOptionsWindow-->TestProperties->FollowRadius.getText()); } } From a10169accf61352fbcefd75e76eef32b0a4c6727 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 09:44:04 -0500 Subject: [PATCH 31/74] fix node following stopping early --- Engine/source/T3D/AI/AINavigation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index a4511921f..4897e0617 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -170,8 +170,10 @@ void AINavigation::onReachDestination() } else #endif + { getCtrl()->throwCallback("onReachDestination"); - getCtrl()->mMovement.mMoveState = AIController::ModeStop; + getCtrl()->mMovement.mMoveState = AIController::ModeStop; + } } bool AINavigation::setPathDestination(const Point3F& pos) From eaa6a62b0c2626c299c820fd84203d4e0eb3ccb2 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 10:38:36 -0500 Subject: [PATCH 32/74] misc cleanups --- Engine/source/T3D/AI/AIController.cpp | 5 ++-- Engine/source/T3D/AI/AINavigation.cpp | 36 +++++++++++++-------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 2ede9aea9..fd6692f07 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -398,14 +398,15 @@ void AIControllerData::resolveStuck(AIController* obj) ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); // Don't check for ai stuckness if animation during // an anim-clip effect override. - if (sbo->getDamageState() == ShapeBase::Enabled && !(sbo->anim_clip_flags & ShapeBase::ANIM_OVERRIDDEN) && !sbo->isAnimationLocked()) { + if (sbo->getDamageState() == ShapeBase::Enabled && !(sbo->anim_clip_flags & ShapeBase::ANIM_OVERRIDDEN) && !sbo->isAnimationLocked()) + { if (obj->mMovement.mMoveStuckTestCountdown > 0) --obj->mMovement.mMoveStuckTestCountdown; else { // We should check to see if we are stuck... F32 locationDelta = (obj->getAIInfo()->getPosition() - obj->getAIInfo()->mLastPos).len(); - if (locationDelta < mMoveStuckTolerance && (sbo->getDamageState() == ShapeBase::Enabled)) + if (locationDelta < mMoveStuckTolerance) { // If we are slowing down, then it's likely that our location delta will be less than // our move stuck tolerance. Because we can be both slowing and stuck diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 4897e0617..aa2dd09c6 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -115,12 +115,12 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; - if (!mControllerRef->getGoal()) return; + if (!getCtrl()->getGoal()) return; // If we're following, get their position. - mPathData.path->mTo = mControllerRef->getGoal()->getPosition(); + mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); // Update from position and replan. - mPathData.path->mFrom = mControllerRef->getAIInfo()->getPosition(); + mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(); mPathData.path->plan(); // Move to first node (skip start pos). moveToNode(1); @@ -137,9 +137,9 @@ Point3F AINavigation::getPathDestination() const void AINavigation::setMoveDestination(const Point3F& location, bool slowdown) { mMoveDestination = location; - mControllerRef->mMovement.mMoveState = AIController::ModeMove; - mControllerRef->mMovement.mMoveSlowdown = slowdown; - mControllerRef->mMovement.mMoveStuckTestCountdown = mControllerRef->mControllerData->mMoveStuckTestDelay; + getCtrl()->mMovement.mMoveState = AIController::ModeMove; + getCtrl()->mMovement.mMoveSlowdown = slowdown; + getCtrl()->mMovement.mMoveStuckTestCountdown = getCtrl()->mControllerData->mMoveStuckTestDelay; } void AINavigation::onReachDestination() @@ -184,7 +184,7 @@ bool AINavigation::setPathDestination(const Point3F& pos) if (!mNavMesh) { //setMoveDestination(pos); - mControllerRef->throwCallback("onPathFailed"); + getCtrl()->throwCallback("onPathFailed"); return false; } @@ -192,11 +192,11 @@ bool AINavigation::setPathDestination(const Point3F& pos) NavPath* path = new NavPath(); path->mMesh = mNavMesh; - path->mFrom = mControllerRef->getAIInfo()->getPosition(); + path->mFrom = getCtrl()->getAIInfo()->getPosition(); path->mTo = pos; path->mFromSet = path->mToSet = true; path->mAlwaysRender = true; - path->mLinkTypes = mControllerRef->mControllerData->mLinkTypes; + path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; path->mXray = true; // Paths plan automatically upon being registered. if (!path->registerObject()) @@ -209,14 +209,14 @@ bool AINavigation::setPathDestination(const Point3F& pos) { // Clear any current path we might have. clearPath(); - mControllerRef->clearCover(); + getCtrl()->clearCover(); clearFollow(); // Store new path. mPathData.path = path; mPathData.owned = true; // Skip node 0, which we are currently standing on. moveToNode(1); - mControllerRef->throwCallback("onPathSuccess"); + getCtrl()->throwCallback("onPathSuccess"); return true; } else @@ -224,7 +224,7 @@ bool AINavigation::setPathDestination(const Point3F& pos) // Just move normally if we can't path. //setMoveDestination(pos, true); //return; - mControllerRef->throwCallback("onPathFailed"); + getCtrl()->throwCallback("onPathFailed"); path->deleteObject(); return false; } @@ -232,31 +232,31 @@ bool AINavigation::setPathDestination(const Point3F& pos) void AINavigation::followObject() { - if ((mControllerRef->getGoal()->mLastPos - mControllerRef->getAIInfo()->getPosition()).len() < mControllerRef->mControllerData->mMoveTolerance) + if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) return; - if (setPathDestination(mControllerRef->getGoal()->getPosition())) + if (setPathDestination(getCtrl()->getGoal()->getPosition())) { - mControllerRef->clearCover(); + getCtrl()->clearCover(); } } void AINavigation::followObject(SceneObject* obj, F32 radius) { - mControllerRef->setGoal(obj, radius); + getCtrl()->setGoal(obj, radius); followObject(); } void AINavigation::clearFollow() { - mControllerRef->clearGoal(); + getCtrl()->clearGoal(); } void AINavigation::followNavPath(NavPath* path) { // Get rid of our current path. clearPath(); - mControllerRef->clearCover(); + getCtrl()->clearCover(); clearFollow(); // Follow new path. From 25b3a7c0705172304960bdd6974a8c19fbc6dc43 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 10:50:58 -0500 Subject: [PATCH 33/74] break wether we *should* be trying to move out of the resolver --- Engine/source/T3D/AI/AIController.cpp | 127 +++++++++++++------------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index fd6692f07..7e9207ebd 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -122,8 +122,22 @@ bool AIController::getAIMove(Move* movePtr) mControllerData->resolveYaw(this, location, movePtr); mControllerData->resolvePitch(this, location, movePtr); mControllerData->resolveRoll(this, location, movePtr); - mControllerData->resolveSpeed(this, location, movePtr); - mControllerData->resolveStuck(this); + + if (mMovement.mMoveState != AIController::ModeStop) + { + F32 xDiff = getNav()->mMoveDestination.x - location.x; + F32 yDiff = getNav()->mMoveDestination.y - location.y; + if (mFabs(xDiff) < mControllerData->mMoveTolerance && mFabs(yDiff) < mControllerData->mMoveTolerance) + { + mMovement.mMoveState = AIController::ModeStop; + getNav()->onReachDestination(); + } + else + { + mControllerData->resolveSpeed(this, location, movePtr); + mControllerData->resolveStuck(this); + } + } } // Test for target location in sight if it's an object. The LOS is @@ -323,72 +337,59 @@ void AIControllerData::resolveRoll(AIController* obj, Point3F location, Move* mo void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { - // Move towards the destination - if (obj->mMovement.mMoveState != AIController::ModeStop) - { - F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; - F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; - Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); + F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; + F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; + Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); - // Check if we should mMove, or if we are 'close enough' - if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) - { - obj->mMovement.mMoveState = AIController::ModeStop; - obj->getNav()->onReachDestination(); - } + // Build move direction in world space + if (mIsZero(xDiff)) + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + else + if (mIsZero(yDiff)) + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; else - { - // Build move direction in world space - if (mIsZero(xDiff)) + if (mFabs(xDiff) > mFabs(yDiff)) + { + F32 value = mFabs(yDiff / xDiff); + movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + } + else + { + F32 value = mFabs(xDiff / yDiff); + movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; - else - if (mIsZero(yDiff)) - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; - else - if (mFabs(xDiff) > mFabs(yDiff)) - { - F32 value = mFabs(yDiff / xDiff); - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; - } - else - { - F32 value = mFabs(xDiff / yDiff); - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; - } - - // Rotate the move into object space (this really only needs - // a 2D matrix) - Point3F newMove; - MatrixF moveMatrix; - moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); - moveMatrix.mulV(Point3F(movePtr->x, movePtr->y, 0.0f), &newMove); - movePtr->x = newMove.x; - movePtr->y = newMove.y; - - // Set movement speed. We'll slow down once we get close - // to try and stop on the spot... - if (obj->mMovement.mMoveSlowdown) - { - F32 speed = obj->mMovement.mMoveSpeed; - F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff); - F32 maxDist = mMoveTolerance * 2; - if (dist < maxDist) - speed *= dist / maxDist; - movePtr->x *= speed; - movePtr->y *= speed; - - obj->mMovement.mMoveState = AIController::ModeSlowing; } - else - { - movePtr->x *= obj->mMovement.mMoveSpeed; - movePtr->y *= obj->mMovement.mMoveSpeed; - obj->mMovement.mMoveState = AIController::ModeMove; - } - } + // Rotate the move into object space (this really only needs + // a 2D matrix) + Point3F newMove; + MatrixF moveMatrix; + moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); + moveMatrix.mulV(Point3F(movePtr->x, movePtr->y, 0.0f), &newMove); + movePtr->x = newMove.x; + movePtr->y = newMove.y; + + // Set movement speed. We'll slow down once we get close + // to try and stop on the spot... + if (obj->mMovement.mMoveSlowdown) + { + F32 speed = obj->mMovement.mMoveSpeed; + F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff); + F32 maxDist = mMoveTolerance * 2; + if (dist < maxDist) + speed *= dist / maxDist; + movePtr->x *= speed; + movePtr->y *= speed; + + obj->mMovement.mMoveState = AIController::ModeSlowing; + } + else + { + movePtr->x *= obj->mMovement.mMoveSpeed; + movePtr->y *= obj->mMovement.mMoveSpeed; + + obj->mMovement.mMoveState = AIController::ModeMove; } } From 83822f114808b361b2ff438a22d640ce742dd9a3 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 11:38:21 -0500 Subject: [PATCH 34/74] fix eroneous defaults --- Engine/source/T3D/AI/AIController.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index d64148a9e..054671171 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -118,10 +118,10 @@ public: mTriggerState.mControllerRef = this; mControllerData = NULL; mAIInfo = new AIInfo(this); - mGoal = new AIGoal(this); - mAimTarget = new AIAimTarget(this); - mCover = new AICover(this); mNav = new AINavigation(this); + mGoal = NULL; + mAimTarget = NULL; + mCover = NULL; mMovement.mMoveState = ModeStop; }; From 2956223a60f8b545033d3de39979533f287286ab Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 11:39:06 -0500 Subject: [PATCH 35/74] simplify setpathdestination loop --- Engine/source/T3D/AI/AIController.cpp | 4 ++-- Engine/source/T3D/AI/AINavigation.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 7e9207ebd..c878ca9c4 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -78,11 +78,11 @@ bool AIController::getAIMove(Move* movePtr) Point3F rotation = sbo->getTransform().toEuler(); #ifdef TORQUE_NAVIGATION_ENABLED - if (sbo->getDamageState() == ShapeBase::Enabled) + if (sbo->getDamageState() == ShapeBase::Enabled && getGoal()) { if (mMovement.mMoveState != ModeStop) getNav()->updateNavMesh(); - if (getGoal() && !getGoal()->mObj.isNull()) + if (getGoal()->mObj.isValid()) { if (getNav()->mPathData.path.isNull()) { diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index aa2dd09c6..180f575a6 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -64,7 +64,6 @@ NavMesh* AINavigation::findNavMesh() const void AINavigation::updateNavMesh() { GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); - NavMesh* old = mNavMesh; if (mNavMesh.isNull()) mNavMesh = findNavMesh(); else @@ -73,9 +72,9 @@ void AINavigation::updateNavMesh() mNavMesh = findNavMesh(); } // See if we need to update our path. - if (mNavMesh != old && !mPathData.path.isNull()) + if (mNavMesh) { - setPathDestination(mPathData.path->mTo); + setPathDestination(getCtrl()->getGoal()->getPosition()); } } @@ -178,6 +177,7 @@ void AINavigation::onReachDestination() bool AINavigation::setPathDestination(const Point3F& pos) { + if (!getCtrl()->getGoal()) getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); if (!mNavMesh) updateNavMesh(); // If we can't find a mesh, just move regularly. From 4f87ad4cf7ecc5c6224895557b75b617669723ed Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 12:27:05 -0500 Subject: [PATCH 36/74] moar gaol tracking cleanups --- Engine/source/T3D/AI/AIAimTarget.h | 6 ++-- Engine/source/T3D/AI/AIController.cpp | 46 +++++++++++++++------------ Engine/source/T3D/AI/AINavigation.cpp | 2 -- Engine/source/T3D/player.cpp | 4 +-- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h index 4e26f8306..1a42911fd 100644 --- a/Engine/source/T3D/AI/AIAimTarget.h +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -32,9 +32,9 @@ struct AIAimTarget : AIInfo bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); F32 getTargetDistance(SceneObject* target, bool _checkEnabled); - AIAimTarget(AIController* controller) : Parent(controller) {}; - AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; - AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; + AIAimTarget(AIController* controller) : Parent(controller) { mTargetInLOS = false; }; + AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mTargetInLOS = false; }; + AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) { mTargetInLOS = false; }; }; #endif diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index c878ca9c4..3050df148 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -82,28 +82,31 @@ bool AIController::getAIMove(Move* movePtr) { if (mMovement.mMoveState != ModeStop) getNav()->updateNavMesh(); - if (getGoal()->mObj.isValid()) - { - if (getNav()->mPathData.path.isNull()) - { - if (getGoal()->getDist() > mControllerData->mFollowTolerance) - getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); - } - else - { - if (getGoal()->getDist() > mControllerData->mFollowTolerance) - getNav()->repath(); - if (getGoal()->getDist() < mControllerData->mFollowTolerance) - { - getNav()->clearPath(); - mMovement.mMoveState = ModeStop; - throwCallback("onTargetInRange"); - } - else if (getGoal()->getDist() < mControllerData->mAttackRadius) - { - throwCallback("onTargetInFiringRange"); - } + if (getNav()->mPathData.path.isNull()) + { + if (getGoal()->getDist() > mControllerData->mFollowTolerance) + { + if (getGoal()->mObj.isValid()) + getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); + else + getNav()->setPathDestination(getGoal()->getPosition()); + } + } + else + { + if (getGoal()->getDist() > mControllerData->mFollowTolerance) + getNav()->repath(); + + if (getGoal()->getDist() < mControllerData->mFollowTolerance) + { + getNav()->clearPath(); + mMovement.mMoveState = ModeStop; + throwCallback("onTargetInRange"); + } + else if (getGoal()->getDist() < mControllerData->mAttackRadius) + { + throwCallback("onTargetInFiringRange"); } } } @@ -396,6 +399,7 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m void AIControllerData::resolveStuck(AIController* obj) { if (obj->mMovement.mMoveState == AIController::ModeStop) return; + if (!obj->getGoal()) return; ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); // Don't check for ai stuckness if animation during // an anim-clip effect override. diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 180f575a6..126f05d29 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -114,8 +114,6 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; - if (!getCtrl()->getGoal()) return; - // If we're following, get their position. mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); // Update from position and replan. diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 38ef5efcb..00f929742 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2261,9 +2261,9 @@ void Player::advanceTime(F32 dt) bool Player::setAIController(SimObjectId controller) { - if (Sim::findObject(controller, mAIController)) + if (Sim::findObject(controller, mAIController) && mAIController->mControllerData) { - mAIController->setAIInfo(this); + mAIController->setAIInfo(this, mAIController->mControllerData->mMoveTolerance); return true; } Con::errorf("unable to find AIController : %i", controller); From c72c3068f8bdaf323ddc88473975f287324f8ce4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 14:38:27 -0500 Subject: [PATCH 37/74] aiinfo subclass inheritance cleanups, and default constructor removals for safeties --- Engine/source/T3D/AI/AIAimTarget.h | 3 +- Engine/source/T3D/AI/AIController.cpp | 44 ++++++++++++++++++++++++++- Engine/source/T3D/AI/AIController.h | 10 +++--- Engine/source/T3D/AI/AICover.h | 3 +- Engine/source/T3D/AI/AIGoal.h | 3 +- Engine/source/T3D/AI/AINavigation.cpp | 13 +++++--- 6 files changed, 63 insertions(+), 13 deletions(-) diff --git a/Engine/source/T3D/AI/AIAimTarget.h b/Engine/source/T3D/AI/AIAimTarget.h index 1a42911fd..4c609af27 100644 --- a/Engine/source/T3D/AI/AIAimTarget.h +++ b/Engine/source/T3D/AI/AIAimTarget.h @@ -23,7 +23,7 @@ #define _AIAIMTARGET_H_ #include "AIInfo.h" -struct AIAimTarget : AIInfo +struct AIAimTarget : public AIInfo { typedef AIInfo Parent; Point3F mAimOffset; @@ -32,6 +32,7 @@ struct AIAimTarget : AIInfo bool checkInLos(SceneObject* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); bool checkInFoV(SceneObject* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); F32 getTargetDistance(SceneObject* target, bool _checkEnabled); + AIAimTarget() = delete; AIAimTarget(AIController* controller) : Parent(controller) { mTargetInLOS = false; }; AIAimTarget(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mTargetInLOS = false; }; AIAimTarget(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) { mTargetInLOS = false; }; diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 3050df148..2e46a8d4b 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -64,6 +64,48 @@ bool AIController::setControllerDataProperty(void* obj, const char* index, const return false; } +void AIController::setGoal(AIInfo* targ) +{ + if (mGoal) { delete(mGoal); mGoal = NULL; } + + if (targ->mObj.isValid()) + { + delete(mGoal); + mGoal = new AIGoal(this, targ->mObj, targ->mRadius); + } + else if (targ->mPosSet) + { + delete(mGoal); + mGoal = new AIGoal(this, targ->mPosition, targ->mRadius); + } +} + +void AIController::setGoal(Point3F loc, F32 rad) +{ + if (mGoal) delete(mGoal); + mGoal = new AIGoal(this, loc, rad); +} + +void AIController::setGoal(SimObjectPtr objIn, F32 rad) +{ + if (mGoal) delete(mGoal); + mGoal = new AIGoal(this, objIn, rad); +} + +void AIController::setAim(Point3F loc, F32 rad, Point3F offset) +{ + if (mAimTarget) delete(mAimTarget); + mAimTarget = new AIAimTarget(this, loc, rad); + mAimTarget->mAimOffset = offset; +} + +void AIController::setAim(SimObjectPtr objIn, F32 rad, Point3F offset) +{ + if (mAimTarget) delete(mAimTarget); + mAimTarget = new AIAimTarget(this, objIn, rad); + mAimTarget->mAimOffset = offset; +} + #ifdef TORQUE_NAVIGATION_ENABLED bool AIController::getAIMove(Move* movePtr) { @@ -89,7 +131,7 @@ bool AIController::getAIMove(Move* movePtr) { if (getGoal()->mObj.isValid()) getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); - else + else if (getGoal()->mPosSet) getNav()->setPathDestination(getGoal()->getPosition()); } } diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 054671171..140eb030b 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -55,16 +55,16 @@ public: private: AIGoal* mGoal; public: - void setGoal(AIInfo* targ) { mGoal = (targ) ? new AIGoal(this, targ->getPosition(), targ->mRadius) : NULL; } - void setGoal(Point3F loc, F32 rad = 0.0f) { delete(mGoal); mGoal = new AIGoal(this, loc, rad); } - void setGoal(SimObjectPtr objIn, F32 rad = 0.0f) { delete(mGoal); mGoal = new AIGoal(this, objIn, rad); } + void setGoal(AIInfo* targ); + void setGoal(Point3F loc, F32 rad = 0.0f); + void setGoal(SimObjectPtr objIn, F32 rad = 0.0f); AIGoal* getGoal() { return mGoal; } void clearGoal() { SAFE_DELETE(mGoal); } private: AIAimTarget* mAimTarget; public: - void setAim(Point3F loc, F32 rad = 0.0f, Point3F offset = Point3F(0.0f,0.0f,0.0f)) { delete(mAimTarget); mAimTarget = new AIAimTarget(this, loc, rad); mAimTarget->mAimOffset = offset; } - void setAim(SimObjectPtr objIn, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f)) { delete(mAimTarget); mAimTarget = new AIAimTarget(this, objIn, rad); mAimTarget->mAimOffset = offset; } + void setAim(Point3F loc, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f)); + void setAim(SimObjectPtr objIn, F32 rad = 0.0f, Point3F offset = Point3F(0.0f, 0.0f, 0.0f)); AIAimTarget* getAim() { return mAimTarget; } void clearAim() { SAFE_DELETE(mAimTarget); } private: diff --git a/Engine/source/T3D/AI/AICover.h b/Engine/source/T3D/AI/AICover.h index 4fe33cab7..e18863bcf 100644 --- a/Engine/source/T3D/AI/AICover.h +++ b/Engine/source/T3D/AI/AICover.h @@ -27,11 +27,12 @@ -struct AICover : AIInfo +struct AICover : public AIInfo { typedef AIInfo Parent; /// Pointer to a cover point. SimObjectPtr mCoverPoint; + AICover() = delete; AICover(AIController* controller) : Parent(controller) {}; AICover(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mCoverPoint = dynamic_cast(objIn.getPointer());}; AICover(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; diff --git a/Engine/source/T3D/AI/AIGoal.h b/Engine/source/T3D/AI/AIGoal.h index e169e489d..ba5096bd7 100644 --- a/Engine/source/T3D/AI/AIGoal.h +++ b/Engine/source/T3D/AI/AIGoal.h @@ -24,9 +24,10 @@ #include "AIInfo.h" -struct AIGoal : AIInfo +struct AIGoal : public AIInfo { typedef AIInfo Parent; + AIGoal() = delete; AIGoal(AIController* controller): Parent(controller) {}; AIGoal(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 126f05d29..0eb5e48d4 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -64,6 +64,7 @@ NavMesh* AINavigation::findNavMesh() const void AINavigation::updateNavMesh() { GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); + NavMesh* old = mNavMesh; if (mNavMesh.isNull()) mNavMesh = findNavMesh(); else @@ -72,9 +73,9 @@ void AINavigation::updateNavMesh() mNavMesh = findNavMesh(); } // See if we need to update our path. - if (mNavMesh) + if (mNavMesh != old && !mPathData.path.isNull()) { - setPathDestination(getCtrl()->getGoal()->getPosition()); + setPathDestination(mPathData.path->mTo); } } @@ -175,7 +176,11 @@ void AINavigation::onReachDestination() bool AINavigation::setPathDestination(const Point3F& pos) { - if (!getCtrl()->getGoal()) getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); + AIGoal* curgoal = getCtrl()->getGoal(); + + if (!curgoal || !curgoal->mObj.isValid()) + getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); + if (!mNavMesh) updateNavMesh(); // If we can't find a mesh, just move regularly. @@ -191,7 +196,7 @@ bool AINavigation::setPathDestination(const Point3F& pos) path->mMesh = mNavMesh; path->mFrom = getCtrl()->getAIInfo()->getPosition(); - path->mTo = pos; + path->mTo = getCtrl()->getGoal()->getPosition(); path->mFromSet = path->mToSet = true; path->mAlwaysRender = true; path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; From f84bf058c98b968ca4b5e7fa50e5fc31e629eed5 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 17:51:31 -0500 Subject: [PATCH 38/74] navigation: setPathdestination now takes a replace bool to preserve the goal also killed a few now extraneous clearfollow calls --- Engine/source/T3D/AI/AINavigation.cpp | 11 ++++------- Engine/source/T3D/AI/AINavigation.h | 2 +- Engine/source/navigation/guiNavEditorCtrl.cpp | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 0eb5e48d4..57d2be27a 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -174,15 +174,14 @@ void AINavigation::onReachDestination() } } -bool AINavigation::setPathDestination(const Point3F& pos) +bool AINavigation::setPathDestination(const Point3F& pos, bool replace) { - AIGoal* curgoal = getCtrl()->getGoal(); - - if (!curgoal || !curgoal->mObj.isValid()) + if (replace) getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); if (!mNavMesh) updateNavMesh(); + // If we can't find a mesh, just move regularly. if (!mNavMesh) { @@ -213,7 +212,6 @@ bool AINavigation::setPathDestination(const Point3F& pos) // Clear any current path we might have. clearPath(); getCtrl()->clearCover(); - clearFollow(); // Store new path. mPathData.path = path; mPathData.owned = true; @@ -260,7 +258,6 @@ void AINavigation::followNavPath(NavPath* path) // Get rid of our current path. clearPath(); getCtrl()->clearCover(); - clearFollow(); // Follow new path. mPathData.path = path; @@ -316,7 +313,7 @@ DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), , "@see getPathDestination()\n" "@see setMoveDestination()\n") { - return object->getNav()->setPathDestination(goal); + return object->getNav()->setPathDestination(goal,true); } diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 2efcbe536..ad9ae6a70 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -82,7 +82,7 @@ struct AINavigation /// Clear out the current path. void clearPath(); - bool setPathDestination(const Point3F& pos); + bool setPathDestination(const Point3F& pos, bool replace = false); Point3F getPathDestination() const; void repath(); diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index b67272aff..3237816bd 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -418,7 +418,7 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) if (po->getAIController()) { if (po->getAIController()->mControllerData) - po->getAIController()->getNav()->setPathDestination(ri.point); + po->getAIController()->getNav()->setPathDestination(ri.point,true); } } } From 32f9917ed2c88f8fe4b26361c2325dad5d768818 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 17:55:38 -0500 Subject: [PATCH 39/74] aicontroller: in order to call derivatives of AIControllerData datablocks *without* requiring an accompanying AIController subclass as well, leverage the fastdelegate system for our resolver callbacks additionally, don't try and repath in mid air aigoal: initialize inange/infirinrange to false. use those to filter callbacks --- Engine/source/T3D/AI/AIController.cpp | 98 +++++++++++++++++---------- Engine/source/T3D/AI/AIController.h | 27 +++++++- Engine/source/T3D/AI/AIGoal.h | 7 +- 3 files changed, 91 insertions(+), 41 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 2e46a8d4b..fa9befa03 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -138,17 +138,33 @@ bool AIController::getAIMove(Move* movePtr) else { if (getGoal()->getDist() > mControllerData->mFollowTolerance) - getNav()->repath(); - - if (getGoal()->getDist() < mControllerData->mFollowTolerance) + { + RayInfo info; + if (getAIInfo()->mObj->getContainer()->castRay(getAIInfo()->getPosition(), getAIInfo()->getPosition() - Point3F(0, 0, 0.001f), StaticShapeObjectType, &info)) + { + getNav()->repath(); + } + getGoal()->mInRange = false; + } + if (getGoal()->getDist() < mControllerData->mFollowTolerance && !getGoal()->mInRange) { getNav()->clearPath(); mMovement.mMoveState = ModeStop; + getGoal()->mInRange = true; throwCallback("onTargetInRange"); } - else if (getGoal()->getDist() < mControllerData->mAttackRadius) + else { - throwCallback("onTargetInFiringRange"); + if (getGoal()->getDist() < mControllerData->mAttackRadius ) + { + if (!getGoal()->mInFiringRange) + { + getGoal()->mInFiringRange = true; + throwCallback("onTargetInFiringRange"); + } + } + else + getGoal()->mInFiringRange = false; } } } @@ -164,9 +180,9 @@ bool AIController::getAIMove(Move* movePtr) else mMovement.mAimLocation = getNav()->mMoveDestination; - mControllerData->resolveYaw(this, location, movePtr); - mControllerData->resolvePitch(this, location, movePtr); - mControllerData->resolveRoll(this, location, movePtr); + mControllerData->resolveYawPtr(this, location, movePtr); + mControllerData->resolvePitchPtr(this, location, movePtr); + mControllerData->resolveRollPtr(this, location, movePtr); if (mMovement.mMoveState != AIController::ModeStop) { @@ -179,12 +195,14 @@ bool AIController::getAIMove(Move* movePtr) } else { - mControllerData->resolveSpeed(this, location, movePtr); - mControllerData->resolveStuck(this); + mControllerData->resolveSpeedPtr(this, location, movePtr); + mControllerData->resolveStuckPtr(this); } } } + mControllerData->resolveTriggerStatePtr(this, movePtr); + // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. @@ -206,31 +224,6 @@ bool AIController::getAIMove(Move* movePtr) } } - /* - // Replicate the trigger state into the move so that - // triggers can be controlled from scripts. - for (U32 i = 0; i < MaxTriggerKeys; i++) - movePtr->trigger[i] = getImageTriggerState(i); - */ - -#ifdef TORQUE_NAVIGATION_ENABLED - if (getNav()->mJump == AINavigation::Now) - { - movePtr->trigger[2] = true; - getNav()->mJump = AINavigation::None; - } - else if (getNav()->mJump == AINavigation::Ledge) - { - // If we're not touching the ground, jump! - RayInfo info; - if (!getAIInfo()->mObj->getContainer()->castRay(getAIInfo()->getPosition(), getAIInfo()->getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info)) - { - movePtr->trigger[2] = true; - getNav()->mJump = AINavigation::None; - } - } -#endif // TORQUE_NAVIGATION_ENABLED - return true; } @@ -438,6 +431,15 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m } } +void AIControllerData::resolveTriggerState(AIController* obj, Move* movePtr) +{ + //check for scripted overides + for (U32 slot = 0; slot < MaxTriggerKeys; slot++) + { + movePtr->trigger[slot] = obj->mTriggerState.mMoveTriggers[slot]; + } +} + void AIControllerData::resolveStuck(AIController* obj) { if (obj->mMovement.mMoveState == AIController::ModeStop) return; @@ -539,10 +541,10 @@ void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, M Player* po = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); if (!po) return;//not a player - if (obj->getAim()->mObj || obj->getAim()->mPosSet || obj->mMovement.mMoveState != AIController::ModeStop) + if (obj->getAim() || obj->mMovement.mMoveState != AIController::ModeStop) { // Next do pitch. - if (!obj->getAim()->mObj && !obj->getAim()->mPosSet) + if (!obj->getAim()) { // Level out if were just looking at our next way point. Point3F headRotation = po->getHeadRotation(); @@ -572,4 +574,26 @@ void AIPlayerControllerData::resolvePitch(AIController* obj, Point3F location, M movePtr->pitch = -headRotation.x; } } + +void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePtr) +{ + Parent::resolveTriggerState(obj, movePtr); +#ifdef TORQUE_NAVIGATION_ENABLED + if (obj->getNav()->mJump == AINavigation::Now) + { + movePtr->trigger[2] = true; + obj->getNav()->mJump = AINavigation::None; + } + else if (obj->getNav()->mJump == AINavigation::Ledge) + { + // If we're not touching the ground, jump! + RayInfo info; + if (!obj->getAIInfo()->mObj->getContainer()->castRay(obj->getAIInfo()->getPosition(), obj->getAIInfo()->getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info)) + { + movePtr->trigger[2] = true; + obj->getNav()->mJump = AINavigation::None; + } + } +#endif // TORQUE_NAVIGATION_ENABLED +} #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 140eb030b..78c7cb86b 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -124,7 +124,7 @@ public: mCover = NULL; mMovement.mMoveState = ModeStop; }; - + DECLARE_CONOBJECT(AIController); }; @@ -144,6 +144,13 @@ public: mMoveStuckTestDelay = 30; mLinkTypes = LinkData(AllFlags); mNavSize = AINavigation::Regular; + + resolveYawPtr.bind(this, &AIControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); + resolveRollPtr.bind(this, &AIControllerData::resolveRoll); + resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); + resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState); + resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); }; ~AIControllerData() {}; @@ -158,10 +165,22 @@ public: /// Types of link we can use. LinkData mLinkTypes; AINavigation::NavSize mNavSize; + Delegate resolveYawPtr; void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + + Delegate resolvePitchPtr; void resolvePitch(AIController* obj, Point3F location, Move* movePtr) {}; + + Delegate resolveRollPtr; void resolveRoll(AIController* obj, Point3F location, Move* movePtr); + + Delegate resolveSpeedPtr; void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); + + Delegate resolveTriggerStatePtr; + void resolveTriggerState(AIController* obj, Move* movePtr); + + Delegate resolveStuckPtr; void resolveStuck(AIController* obj); }; @@ -170,7 +189,13 @@ class AIPlayerControllerData : public AIControllerData typedef AIControllerData Parent; public: + AIPlayerControllerData() + { + resolvePitchPtr.bind(this, &AIPlayerControllerData::resolvePitch); + resolveTriggerStatePtr.bind(this, &AIPlayerControllerData::resolveTriggerState); + } void resolvePitch(AIController* obj, Point3F location, Move* movePtr); + void resolveTriggerState(AIController* obj, Move* movePtr); DECLARE_CONOBJECT(AIPlayerControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/AI/AIGoal.h b/Engine/source/T3D/AI/AIGoal.h index ba5096bd7..e8a22b873 100644 --- a/Engine/source/T3D/AI/AIGoal.h +++ b/Engine/source/T3D/AI/AIGoal.h @@ -27,9 +27,10 @@ struct AIGoal : public AIInfo { typedef AIInfo Parent; + bool mInRange, mInFiringRange; AIGoal() = delete; - AIGoal(AIController* controller): Parent(controller) {}; - AIGoal(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) {}; - AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) {}; + AIGoal(AIController* controller) : Parent(controller) { mInRange = mInFiringRange = false; }; + AIGoal(AIController* controller, SimObjectPtr objIn, F32 radIn) : Parent(controller, objIn, radIn) { mInRange = mInFiringRange = false; }; + AIGoal(AIController* controller, Point3F pointIn, F32 radIn) : Parent(controller, pointIn, radIn) { mInRange = mInFiringRange = false; }; }; #endif From d0b0070ec7271f42b41b537fccf2b15866e83afd Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 18:15:41 -0500 Subject: [PATCH 40/74] set navmesh test tool bots to damage enabled and navmeshignore --- Templates/BaseGame/game/tools/navEditor/navEditor.tscript | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 14d758923..75a84977b 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -463,8 +463,10 @@ function NavEditorGui::onPlayerSelected(%this, %flags) { %this.getPlayer().aiController = new AIController(){ ControllerData = aiPlayerControl; }; %this.getPlayer().setAIController(%this.getPlayer().aiController); + NavMeshIgnore(%this.getPlayer(), true); + %this.getPlayer().setDamageState("Enabled"); } - NavMeshIgnore(%this.getPlayer(), true); + updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags); } From 1fad2c73729c685118d4cb3ccd4f1812e35061b4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 20:01:36 -0500 Subject: [PATCH 41/74] AIWheeledVehicleControllerData wipwork --- Engine/source/T3D/AI/AIController.cpp | 140 +++++++++++++++++- Engine/source/T3D/AI/AIController.h | 26 ++++ Engine/source/T3D/vehicles/vehicle.h | 1 + .../datablocks/defaultDatablocks.tscript | 5 + 4 files changed, 171 insertions(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index fa9befa03..f69345e10 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -22,6 +22,8 @@ #include "AIController.h" #include "T3D/player.h" +#include "T3D/rigidShape.h" +#include "T3D/vehicles/wheeledVehicle.h" IMPLEMENT_CONOBJECT(AIController); @@ -139,8 +141,12 @@ bool AIController::getAIMove(Move* movePtr) { if (getGoal()->getDist() > mControllerData->mFollowTolerance) { + SceneObject* obj = getAIInfo()->mObj->getObjectMount(); + if (!obj) + obj = getAIInfo()->mObj; + RayInfo info; - if (getAIInfo()->mObj->getContainer()->castRay(getAIInfo()->getPosition(), getAIInfo()->getPosition() - Point3F(0, 0, 0.001f), StaticShapeObjectType, &info)) + if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, 0.001f), StaticShapeObjectType, &info)) { getNav()->repath(); } @@ -596,4 +602,136 @@ void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePt } #endif // TORQUE_NAVIGATION_ENABLED } + +IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); + +// Build a Triangle .. calculate angle of rotation required to meet target.. +// man there has to be a better way! >:) +F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) +{ + WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return 0;//not a WheeledVehicle + + // What is our target + Point3F desired; + desired = obj->getNav()->mMoveDestination; + + MatrixF mat = wvo->getTransform(); + Point3F center, front; + Point3F wFront; + Box3F box = wvo->getObjBox(); + + box.getCenter(¢er); + front = center; + front.y = box.maxExtents.y; // should be true for all these objects + + obj->getAIInfo()->mObj->getWorldBox().getCenter(¢er); + front = center + front; + + Point3F objFront = front; + Point3F offset = front - center; + EulerF rot; + rot = mat.toEuler(); + MatrixF transform(rot); + transform.mulV(offset, &wFront); + front = wFront + center; + + Point3F ftoc; + ftoc.x = mFabs(front.x - center.x); + ftoc.y = mFabs(front.y - center.y); + ftoc.z = mFabs(front.z - center.z); + F32 fToc = mSqrt((ftoc.x * ftoc.x) + (ftoc.y * ftoc.y)); + + Point3F ltoc; + ltoc.x = mFabs(desired.x - center.x); + ltoc.y = mFabs(desired.y - center.y); + ltoc.z = mFabs(desired.z - center.z); + F32 lToc = mSqrt((ltoc.x * ltoc.x) + (ltoc.y * ltoc.y)); + + Point3F ftol; + ftol.x = mFabs(front.x - desired.x); + ftol.y = mFabs(front.y - desired.y); + ftol.z = mFabs(front.z - desired.z); + F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); + + F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); + Point3F location = obj->getAIInfo()->getPosition(); + + F32 xDiff = desired.x - location.x; + F32 yDiff = desired.y - location.y; + + F32 finalYaw = mRadToDeg(myAngle); + + F32 maxSteeringAngle = 0; + + VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); + maxSteeringAngle = vd->maxSteeringAngle; + + // if(finalYaw > 150) + // steerState = TurnAround; + if (finalYaw < 5) + mSteerState = Straight; + else + {// Quickly Hack out left or right turn info + Point3F rotData = objFront - desired; + MatrixF leftM(-rot); + Point3F leftP; + leftM.mulV(rotData, &leftP); + leftP = leftP + desired; + + if (leftP.x < desired.x) + mSteerState = Right; + else + mSteerState = Left; + } + + Point2F steering = wvo->getSteering(); + + F32 steer = 0; + switch (mSteerState) + { + case SteerNull: + break; + case Left: + steer = myAngle < maxSteeringAngle ? -myAngle - steering.x : -maxSteeringAngle - steering.x; + break; + case Right: + steer = myAngle < maxSteeringAngle ? myAngle - steering.x : maxSteeringAngle - steering.x; + break; + case Straight: + steer = -steering.x; + break; + case TurnAround: + steer = maxSteeringAngle - steering.x; + break; + }; + + // Con::printf("AI Steering : %f", steer); + return steer; +} + + +void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) +{ + WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a WheeledVehicle + + // Orient towards our destination. + if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { + movePtr->yaw = getSteeringAngle(obj); + } +}; +void AIWheeledVehicleControllerData::resolveTriggerState(AIController* obj, Move* movePtr) {}; #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 78c7cb86b..d724d9ee5 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -45,6 +45,7 @@ public: ModeMove, // AI is currently moving. ModeStuck, // AI is stuck, but wants to move. ModeSlowing, // AI is slowing down as it reaches it's destination. + ModeReverse // AI is reversing }; private: @@ -198,5 +199,30 @@ public: void resolveTriggerState(AIController* obj, Move* movePtr); DECLARE_CONOBJECT(AIPlayerControllerData); }; + +class AIWheeledVehicleControllerData : public AIControllerData +{ + typedef AIControllerData Parent; + + enum DrivingState { + SteerNull, + Left, + Right, + Straight, + TurnAround + } mSteerState; + +public: + AIWheeledVehicleControllerData() + { + mSteerState = SteerNull; + resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); + resolveTriggerStatePtr.bind(this, &AIWheeledVehicleControllerData::resolveTriggerState); + } + F32 getSteeringAngle(AIController* obj); + void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + void resolveTriggerState(AIController* obj, Move* movePtr); + DECLARE_CONOBJECT(AIWheeledVehicleControllerData); +}; #endif // TORQUE_NAVIGATION_ENABLED #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 402a3109a..52dbb8e68 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -146,6 +146,7 @@ public: bool onAdd() override; void onRemove() override; + Point2F getSteering() { return mSteering; }; /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void advanceTime(F32 dt) override; diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index fbd8cd5fc..ffdc4806c 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -173,4 +173,9 @@ datablock LightAnimData( SpinLightAnim ) datablock AIPlayerControllerData( aiPlayerControl ) { +}; + +datablock AIWheeledVehicleControllerData( aiCarControl ) +{ + }; \ No newline at end of file From 2d5e8c156043f601a7696ec448d1247e402c0866 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Thu, 17 Apr 2025 23:31:30 -0500 Subject: [PATCH 42/74] make steerstate for AIWheeledVehicleControllerData's self contained variable raylength for the rpath filter. same 0.001 for players to stop recalculating a path when jumping, but bump anything they're mounted to to a 2 unit check bit of work towards parallel parking. or at least not ending up arcing back and forth infinitely in an arc --- Engine/source/T3D/AI/AIController.cpp | 88 ++++++++++++++++++++------- Engine/source/T3D/AI/AIController.h | 8 +-- 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index f69345e10..8ce21583b 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -141,12 +141,16 @@ bool AIController::getAIMove(Move* movePtr) { if (getGoal()->getDist() > mControllerData->mFollowTolerance) { + F32 raylength = 2.0; //for vehicles SceneObject* obj = getAIInfo()->mObj->getObjectMount(); if (!obj) + { obj = getAIInfo()->mObj; + raylength = 0.001f; //for jumping + } RayInfo info; - if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, 0.001f), StaticShapeObjectType, &info)) + if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, raylength), StaticShapeObjectType, &info)) { getNav()->repath(); } @@ -607,7 +611,7 @@ IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) -F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) +F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) { WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); if (!wvo) @@ -618,6 +622,8 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) } if (!wvo) return 0;//not a WheeledVehicle + DrivingState steerState = SteerNull; + // What is our target Point3F desired; desired = obj->getNav()->mMoveDestination; @@ -661,10 +667,6 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); - Point3F location = obj->getAIInfo()->getPosition(); - - F32 xDiff = desired.x - location.x; - F32 yDiff = desired.y - location.y; F32 finalYaw = mRadToDeg(myAngle); @@ -673,10 +675,12 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); maxSteeringAngle = vd->maxSteeringAngle; - // if(finalYaw > 150) - // steerState = TurnAround; - if (finalYaw < 5) - mSteerState = Straight; + Point2F steering = wvo->getSteering(); + + if (finalYaw < 5 && steering.x != 0.0f) + steerState = Straight; + else if (finalYaw < 5) + steerState = SteerNull; else {// Quickly Hack out left or right turn info Point3F rotData = objFront - desired; @@ -686,32 +690,74 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj) leftP = leftP + desired; if (leftP.x < desired.x) - mSteerState = Right; + steerState = Right; else - mSteerState = Left; + steerState = Left; } - Point2F steering = wvo->getSteering(); + + F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; + F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; + Point3F rotation = wvo->getTransform().toEuler(); + Point2F mov; + // Build move direction in world space + if (mIsZero(xDiff)) + mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + else + { + if (mIsZero(yDiff)) + mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + else + if (mFabs(xDiff) > mFabs(yDiff)) + { + F32 value = mFabs(yDiff / xDiff); + mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; + mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + } + else + { + F32 value = mFabs(xDiff / yDiff); + mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; + mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + } + } + // Rotate the move into object space (this really only needs + // a 2D matrix) + Point3F throttle; + MatrixF moveMatrix; + moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + steering.x))); + moveMatrix.mulV(Point3F(mov.x, mov.y, 0.0f), &throttle); + + F32 turnAdjust = myAngle - steering.x; + + if (throttle.y < 0.0f) + { + F32 reverseReduction = 0.25f; + if (steerState == Left) + steerState = Right; + else if (steerState == Right) + steerState = Left; + turnAdjust *= reverseReduction; + myAngle *= reverseReduction; + } F32 steer = 0; - switch (mSteerState) + switch (steerState) { - case SteerNull: - break; case Left: - steer = myAngle < maxSteeringAngle ? -myAngle - steering.x : -maxSteeringAngle - steering.x; + steer = myAngle < maxSteeringAngle ? -turnAdjust : -maxSteeringAngle - steering.x; break; case Right: - steer = myAngle < maxSteeringAngle ? myAngle - steering.x : maxSteeringAngle - steering.x; + steer = myAngle < maxSteeringAngle ? turnAdjust : maxSteeringAngle - steering.x; break; case Straight: steer = -steering.x; break; - case TurnAround: - steer = maxSteeringAngle - steering.x; + default: break; }; + // Con::printf("AI Steering : %f", steer); return steer; } @@ -730,7 +776,7 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat // Orient towards our destination. if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { - movePtr->yaw = getSteeringAngle(obj); + movePtr->yaw = getSteeringAngle(obj, location); } }; void AIWheeledVehicleControllerData::resolveTriggerState(AIController* obj, Move* movePtr) {}; diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index d724d9ee5..ccaaf5a35 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -208,18 +208,16 @@ class AIWheeledVehicleControllerData : public AIControllerData SteerNull, Left, Right, - Straight, - TurnAround - } mSteerState; + Straight + }; public: AIWheeledVehicleControllerData() { - mSteerState = SteerNull; resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); resolveTriggerStatePtr.bind(this, &AIWheeledVehicleControllerData::resolveTriggerState); } - F32 getSteeringAngle(AIController* obj); + F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveTriggerState(AIController* obj, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); From 78a26b010876502bf1b9cd5743dc627340122f36 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 18 Apr 2025 11:00:13 -0500 Subject: [PATCH 43/74] expose a getThrottle for vehicles. save some calcs in AIWheeledVehicleControllerData deletion cleanups --- Engine/source/T3D/AI/AIController.cpp | 37 ++------------------------- Engine/source/T3D/AI/AIController.h | 9 ++++++- Engine/source/T3D/AI/AINavigation.cpp | 8 ++++++ Engine/source/T3D/AI/AINavigation.h | 2 +- Engine/source/T3D/player.cpp | 3 ++- Engine/source/T3D/vehicles/vehicle.h | 1 + 6 files changed, 22 insertions(+), 38 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 8ce21583b..974f5e10a 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -695,42 +695,10 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F steerState = Left; } - - F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; - F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; - Point3F rotation = wvo->getTransform().toEuler(); - Point2F mov; - // Build move direction in world space - if (mIsZero(xDiff)) - mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; - else - { - if (mIsZero(yDiff)) - mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; - else - if (mFabs(xDiff) > mFabs(yDiff)) - { - F32 value = mFabs(yDiff / xDiff); - mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; - mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; - } - else - { - F32 value = mFabs(xDiff / yDiff); - mov.x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; - mov.y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; - } - } - // Rotate the move into object space (this really only needs - // a 2D matrix) - Point3F throttle; - MatrixF moveMatrix; - moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + steering.x))); - moveMatrix.mulV(Point3F(mov.x, mov.y, 0.0f), &throttle); - F32 turnAdjust = myAngle - steering.x; - if (throttle.y < 0.0f) + F32 throttle = wvo->getThrottle(); + if (throttle < 0.0f) { F32 reverseReduction = 0.25f; if (steerState == Left) @@ -757,7 +725,6 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F break; }; - // Con::printf("AI Steering : %f", steer); return steer; } diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index ccaaf5a35..43c9be04c 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -125,7 +125,14 @@ public: mCover = NULL; mMovement.mMoveState = ModeStop; }; - + ~AIController() + { + SAFE_DELETE(mAIInfo); + SAFE_DELETE(mNav); + clearGoal(); + clearAim(); + clearCover(); + } DECLARE_CONOBJECT(AIController); }; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 57d2be27a..abe6d512a 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -29,6 +29,14 @@ AINavigation::AINavigation(AIController* controller) mNavSize = Regular; } +AINavigation::~AINavigation() +{ +#ifdef TORQUE_NAVIGATION_ENABLED + clearPath(); + clearFollow(); +#endif +} + NavMesh* AINavigation::findNavMesh() const { GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index ad9ae6a70..978ae01c6 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -35,7 +35,7 @@ struct AINavigation AINavigation() = delete; AINavigation(AIController* controller); - + ~AINavigation(); /// Stores information about a path. struct PathData { /// Pointer to path object. diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 00f929742..e48f204e6 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -1643,7 +1643,7 @@ Player::Player() mLastAbsoluteYaw = 0.0f; mLastAbsolutePitch = 0.0f; mLastAbsoluteRoll = 0.0f; - + mAIController = NULL; afx_init(); } @@ -1654,6 +1654,7 @@ Player::~Player() delete mShapeFPInstance[i]; mShapeFPInstance[i] = 0; } + if (mAIController) mAIController->deleteObject(); } diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 52dbb8e68..8c43a6e41 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -147,6 +147,7 @@ public: void onRemove() override; Point2F getSteering() { return mSteering; }; + F32 getThrottle() { return mThrottle;}; /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void advanceTime(F32 dt) override; From 2d0bcbcf8d7fc734987f8359eed87bfb1cd9b9b2 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 18 Apr 2025 11:36:27 -0500 Subject: [PATCH 44/74] behavioural change: feeding an AInfo an object with a 0 radius causes the class to fill in radius from that objects bounds box also, vehicle direct hooks --- Engine/source/T3D/AI/AIInfo.cpp | 2 ++ Engine/source/T3D/player.cpp | 2 +- Engine/source/T3D/vehicles/vehicle.cpp | 25 ++++++++++++++++++++++++- Engine/source/T3D/vehicles/vehicle.h | 6 +++++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp index c2c11de9a..2f0022658 100644 --- a/Engine/source/T3D/AI/AIInfo.cpp +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -39,6 +39,8 @@ AIInfo::AIInfo(AIController* controller, SimObjectPtr objIn, F32 ra mPosition = mLastPos = objIn->getPosition(); mRadius = radIn; mPosSet = false; + if (radIn == 0.0f) + mRadius = mMax(objIn->getObjBox().len_x(), objIn->getObjBox().len_y()) * 0.5; }; AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index e48f204e6..938fc3e6d 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2264,7 +2264,7 @@ bool Player::setAIController(SimObjectId controller) { if (Sim::findObject(controller, mAIController) && mAIController->mControllerData) { - mAIController->setAIInfo(this, mAIController->mControllerData->mMoveTolerance); + mAIController->setAIInfo(this); return true; } Con::errorf("unable to find AIController : %i", controller); diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index cfbd9a876..cd420c9d3 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -405,6 +405,7 @@ Vehicle::Vehicle() mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; mPhysicsRep = NULL; + mAIController = NULL; } U32 Vehicle::getCollisionMask() @@ -471,7 +472,7 @@ bool Vehicle::onAdd() void Vehicle::onRemove() { SAFE_DELETE(mPhysicsRep); - + if (mAIController) mAIController->deleteObject(); U32 i=0; for( i=0; imControllerData) + { + mAIController->setAIInfo(this); + return true; + } + Con::errorf("unable to find AIController : %i", controller); + mAIController = NULL; + return false; +} + +DefineEngineMethod(Vehicle, setAIController, bool, (S32 controller), , "") +{ + return object->setAIController(controller); +} + +DefineEngineMethod(Vehicle, getAIController, AIController*, (), , "") +{ + return object->getAIController(); +} diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 8c43a6e41..4dd518552 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -27,6 +27,8 @@ #include "T3D/rigidShape.h" #endif +#include "T3D/AI/AIController.h" + class ParticleEmitter; class ParticleEmitterData; class ClippedPolyList; @@ -98,7 +100,7 @@ class Vehicle : public RigidShape Point2F mSteering; F32 mThrottle; bool mJetting; - + AIController* mAIController; GFXStateBlockRef mSolidSB; SimObjectPtr mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS]; @@ -148,6 +150,8 @@ public: Point2F getSteering() { return mSteering; }; F32 getThrottle() { return mThrottle;}; + bool setAIController(SimObjectId controller); + AIController* getAIController() { return mAIController; }; /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void advanceTime(F32 dt) override; From 712404c9b44feb8e94b17e228caaf3b8ea90ccfe Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 18 Apr 2025 12:28:49 -0500 Subject: [PATCH 45/74] hook up Vehicle's getAIMove(Move*); list aiControllerData's in the datablock. though the command is still required to set the controler and look up the relevant db for game specific logic --- Engine/source/T3D/AI/AIController.cpp | 2 +- Engine/source/T3D/player.cpp | 6 ++++-- Engine/source/T3D/vehicles/vehicle.cpp | 25 +++++++++++++++++++++++++ Engine/source/T3D/vehicles/vehicle.h | 4 ++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 974f5e10a..89d1decd3 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -31,6 +31,7 @@ IMPLEMENT_CONOBJECT(AIController); //----------------------------------------------------------------------------- void AIController::throwCallback(const char* name) { + Con::warnf("throwCallback: %s", name); Con::executef(mControllerData, name, getIdString()); //controller data callbacks GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); @@ -676,7 +677,6 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F maxSteeringAngle = vd->maxSteeringAngle; Point2F steering = wvo->getSteering(); - if (finalYaw < 5 && steering.x != 0.0f) steerState = Straight; else if (finalYaw < 5) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 938fc3e6d..18a0560b7 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -461,7 +461,7 @@ PlayerData::PlayerData() physicsPlayerType = StringTable->EmptyString(); mControlMap = StringTable->EmptyString(); - + mAIControllData = NULL; dMemset( actionList, 0, sizeof(actionList) ); } @@ -740,8 +740,10 @@ void PlayerData::initPersistFields() endGroup( "Camera" ); addGroup( "Movement" ); - addField("controlMap", TypeString, Offset(mControlMap, PlayerData), + addField("controlMap", TypeString, Offset(mControlMap, PlayerData), "@brief movemap used by these types of objects.\n\n"); + addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, PlayerData), + "@brief ai controller used by these types of objects.\n\n"); addFieldV( "maxStepHeight", TypeRangedF32, Offset(maxStepHeight, PlayerData), &CommonValidators::PositiveFloat, "@brief Maximum height the player can step up.\n\n" diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index cd420c9d3..ac01b74e7 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -147,6 +147,7 @@ VehicleData::VehicleData() collDamageThresholdVel = 20; collDamageMultiplier = 0.05f; enablePhysicsRep = true; + mAIControllData = NULL; } @@ -320,6 +321,13 @@ void VehicleData::initPersistFields() "velocity).\n\nCurrently unused." ); endGroup("Collision"); + addGroup("Movement"); + addField("controlMap", TypeString, Offset(mControlMap, VehicleData), + "@brief movemap used by these types of objects.\n\n"); + addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, VehicleData), + "@brief ai controller used by these types of objects.\n\n"); + endGroup("Collision"); + addGroup("Steering"); addFieldV( "jetForce", TypeRangedF32, Offset(jetForce, VehicleData), &CommonValidators::PositiveFloat, "@brief Additional force applied to the vehicle when it is jetting.\n\n" @@ -501,6 +509,12 @@ void Vehicle::processTick(const Move* move) if ( isMounted() ) return; + // If we're not being controlled by a client, let the + // AI sub-module get a chance at producing a move. + Move aiMove; + if (!move && isServerObject() && getAIMove(&aiMove)) + move = &aiMove; + // Warp to catch up to server if (mDelta.warpCount < mDelta.warpTicks) { @@ -1233,6 +1247,17 @@ bool Vehicle::setAIController(SimObjectId controller) return false; } +bool Vehicle::getAIMove(Move* move) +{ + if (mAIController) + { + mAIController->getAIMove(move); //actual result + return true; + } + + return false; +} + DefineEngineMethod(Vehicle, setAIController, bool, (S32 controller), , "") { return object->setAIController(controller); diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 4dd518552..ee3913a29 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -72,6 +72,8 @@ struct VehicleData : public RigidShapeData F32 numDmgEmitterAreas; bool enablePhysicsRep; + StringTableEntry mControlMap; + AIControllerData* mAIControllData; // VehicleData(); @@ -152,6 +154,8 @@ public: F32 getThrottle() { return mThrottle;}; bool setAIController(SimObjectId controller); AIController* getAIController() { return mAIController; }; + virtual bool getAIMove(Move*); + /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void advanceTime(F32 dt) override; From d36cf317075358cd718e1b5cc4da76c0d8666db4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 18 Apr 2025 18:27:39 -0500 Subject: [PATCH 46/74] more safeties. simplified reverse steering calc --- Engine/source/T3D/AI/AIController.cpp | 16 ++++------------ Engine/source/T3D/AI/AIController.h | 5 ++--- Engine/source/T3D/player.cpp | 2 +- Engine/source/T3D/vehicles/vehicle.cpp | 11 +++++++---- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 89d1decd3..ad0243b18 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -609,7 +609,6 @@ void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePt } IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); - // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) @@ -695,19 +694,13 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F steerState = Left; } - F32 turnAdjust = myAngle - steering.x; - F32 throttle = wvo->getThrottle(); - if (throttle < 0.0f) + if (throttle < 0.0f && steerState != Straight) { - F32 reverseReduction = 0.25f; - if (steerState == Left) - steerState = Right; - else if (steerState == Right) - steerState = Left; - turnAdjust *= reverseReduction; - myAngle *= reverseReduction; + F32 reverseReduction = 0.25; + steering.x = steering.x * reverseReduction * throttle; } + F32 turnAdjust = myAngle - steering.x; F32 steer = 0; switch (steerState) @@ -746,5 +739,4 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat movePtr->yaw = getSteeringAngle(obj, location); } }; -void AIWheeledVehicleControllerData::resolveTriggerState(AIController* obj, Move* movePtr) {}; #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 43c9be04c..5e8b8bf0e 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -161,7 +161,8 @@ public: resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); }; ~AIControllerData() {}; - + void packData(BitStream* stream) override {}; + void unpackData(BitStream* stream) override {}; static void initPersistFields(); DECLARE_CONOBJECT(AIControllerData); @@ -222,11 +223,9 @@ public: AIWheeledVehicleControllerData() { resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); - resolveTriggerStatePtr.bind(this, &AIWheeledVehicleControllerData::resolveTriggerState); } F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); - void resolveTriggerState(AIController* obj, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 18a0560b7..ee715517b 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -1744,7 +1744,6 @@ bool Player::onAdd() world ); mPhysicsRep->setTransform( getTransform() ); } - mAIController = NULL; return true; } @@ -2287,6 +2286,7 @@ DefineEngineMethod(Player, getAIController, AIController*, (), , "") bool Player::getAIMove(Move* move) { + if (!isServerObject()) return false; if (mAIController) { mAIController->getAIMove(move); //actual result diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index ac01b74e7..e6a86baad 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -147,6 +147,7 @@ VehicleData::VehicleData() collDamageThresholdVel = 20; collDamageMultiplier = 0.05f; enablePhysicsRep = true; + mControlMap = StringTable->EmptyString(); mAIControllData = NULL; } @@ -505,16 +506,17 @@ void Vehicle::processTick(const Move* move) { PROFILE_SCOPE( Vehicle_ProcessTick ); - ShapeBase::processTick(move); - if ( isMounted() ) - return; - // If we're not being controlled by a client, let the // AI sub-module get a chance at producing a move. Move aiMove; if (!move && isServerObject() && getAIMove(&aiMove)) move = &aiMove; + ShapeBase::processTick(move); + if ( isMounted() ) + return; + + // Warp to catch up to server if (mDelta.warpCount < mDelta.warpTicks) { @@ -1249,6 +1251,7 @@ bool Vehicle::setAIController(SimObjectId controller) bool Vehicle::getAIMove(Move* move) { + if (!isServerObject()) return false; if (mAIController) { mAIController->getAIMove(move); //actual result From 3210325f3fff93d0c989912587d3285297c5cf33 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 19 Apr 2025 04:25:36 -0500 Subject: [PATCH 47/74] elevated mAicontroller to shapebase aiwheeleedveiclecontrollerdata resolvespeed now only touches throttle objects assigned aicontrollers now reflect that by thier objecttype basic flocking --- Engine/source/T3D/AI/AIController.cpp | 191 +++++++++++++----- Engine/source/T3D/AI/AIController.h | 30 ++- Engine/source/T3D/AI/AIInfo.h | 4 +- Engine/source/T3D/AI/AINavigation.cpp | 124 ++++++++++++ Engine/source/T3D/AI/AINavigation.h | 1 + Engine/source/T3D/gameFunctions.cpp | 1 + Engine/source/T3D/objectTypes.h | 2 +- Engine/source/T3D/player.cpp | 41 ---- Engine/source/T3D/player.h | 7 - Engine/source/T3D/shapeBase.cpp | 52 ++++- Engine/source/T3D/shapeBase.h | 8 + Engine/source/T3D/vehicles/vehicle.cpp | 41 +--- Engine/source/T3D/vehicles/vehicle.h | 5 - Engine/source/console/simObject.cpp | 2 +- Engine/source/navigation/guiNavEditorCtrl.cpp | 28 ++- 15 files changed, 352 insertions(+), 185 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index ad0243b18..8740165d0 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -122,6 +122,27 @@ bool AIController::getAIMove(Move* movePtr) Point3F location = eye.getPosition(); Point3F rotation = sbo->getTransform().toEuler(); + // Test for target location in sight if it's an object. The LOS is + // run from the eye position to the center of the object's bounding, + // which is not very accurate. + if (getAim() && getAim()->mObj) + { + GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); + if (getAim()->checkInLos(gbo)) + { + if (!getAim()->mTargetInLOS) + { + throwCallback("onTargetEnterLOS"); + getAim()->mTargetInLOS = true; + } + } + else if (getAim()->mTargetInLOS) + { + throwCallback("onTargetExitLOS"); + getAim()->mTargetInLOS = false; + } + } + #ifdef TORQUE_NAVIGATION_ENABLED if (sbo->getDamageState() == ShapeBase::Enabled && getGoal()) { @@ -181,6 +202,7 @@ bool AIController::getAIMove(Move* movePtr) } #endif // TORQUE_NAVIGATION_ENABLED + getNav()->flock(); // Orient towards the aim point, aim object, or towards // our destination. if (getAim() || mMovement.mMoveState != ModeStop) @@ -189,7 +211,7 @@ bool AIController::getAIMove(Move* movePtr) if (getAim()) mMovement.mAimLocation = getAim()->getPosition(); else - mMovement.mAimLocation = getNav()->mMoveDestination; + mMovement.mAimLocation = getNav()->getMoveDestination(); mControllerData->resolveYawPtr(this, location, movePtr); mControllerData->resolvePitchPtr(this, location, movePtr); @@ -197,11 +219,10 @@ bool AIController::getAIMove(Move* movePtr) if (mMovement.mMoveState != AIController::ModeStop) { - F32 xDiff = getNav()->mMoveDestination.x - location.x; - F32 yDiff = getNav()->mMoveDestination.y - location.y; + F32 xDiff = getNav()->getMoveDestination().x - location.x; + F32 yDiff = getNav()->getMoveDestination().y - location.y; if (mFabs(xDiff) < mControllerData->mMoveTolerance && mFabs(yDiff) < mControllerData->mMoveTolerance) { - mMovement.mMoveState = AIController::ModeStop; getNav()->onReachDestination(); } else @@ -214,27 +235,7 @@ bool AIController::getAIMove(Move* movePtr) mControllerData->resolveTriggerStatePtr(this, movePtr); - // Test for target location in sight if it's an object. The LOS is - // run from the eye position to the center of the object's bounding, - // which is not very accurate. - if (getAim() && getAim()->mObj) - { - GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); - if (getAim()->checkInLos(gbo)) - { - if (!getAim()->mTargetInLOS) - { - throwCallback("onTargetEnterLOS"); - getAim()->mTargetInLOS = true; - } - } - else if (getAim()->mTargetInLOS) - { - throwCallback("onTargetExitLOS"); - getAim()->mTargetInLOS = false; - } - } - + getAIInfo()->mLastPos = getAIInfo()->getPosition(); return true; } @@ -250,17 +251,18 @@ void AIController::Movement::stopMove() { mMoveState = ModeStop; #ifdef TORQUE_NAVIGATION_ENABLED - mControllerRef->getNav()->clearPath(); - mControllerRef->clearCover(); - mControllerRef->getNav()->clearFollow(); + getCtrl()->getNav()->clearPath(); + getCtrl()->clearCover(); + getCtrl()->getNav()->clearFollow(); #endif } void AIController::Movement::onStuck() { - mControllerRef->throwCallback("onMoveStuck"); + mMoveState = AIController::ModeStuck; + getCtrl()->throwCallback("onMoveStuck"); #ifdef TORQUE_NAVIGATION_ENABLED - if (!mControllerRef->getNav()->getPath().isNull()) - mControllerRef->getNav()->repath(); + if (!getCtrl()->getNav()->getPath().isNull()) + getCtrl()->getNav()->repath(); #endif } @@ -386,28 +388,28 @@ void AIControllerData::resolveRoll(AIController* obj, Point3F location, Move* mo void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { - F32 xDiff = obj->getNav()->mMoveDestination.x - location.x; - F32 yDiff = obj->getNav()->mMoveDestination.y - location.y; + F32 xDiff = obj->getNav()->getMoveDestination().x - location.x; + F32 yDiff = obj->getNav()->getMoveDestination().y - location.y; Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); // Build move direction in world space if (mIsZero(xDiff)) - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; else if (mIsZero(yDiff)) - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; else if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -value : value; - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -1.0f : 1.0f; + movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -value : value; + movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; } else { F32 value = mFabs(xDiff / yDiff); - movePtr->x = (location.x > obj->getNav()->mMoveDestination.x) ? -value : value; - movePtr->y = (location.y > obj->getNav()->mMoveDestination.y) ? -1.0f : 1.0f; + movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value; + movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; } // Rotate the move into object space (this really only needs @@ -430,15 +432,11 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m speed *= dist / maxDist; movePtr->x *= speed; movePtr->y *= speed; - - obj->mMovement.mMoveState = AIController::ModeSlowing; } else { movePtr->x *= obj->mMovement.mMoveSpeed; movePtr->y *= obj->mMovement.mMoveSpeed; - - obj->mMovement.mMoveState = AIController::ModeMove; } } @@ -453,36 +451,57 @@ void AIControllerData::resolveTriggerState(AIController* obj, Move* movePtr) void AIControllerData::resolveStuck(AIController* obj) { - if (obj->mMovement.mMoveState == AIController::ModeStop) return; + if (obj->mMovement.mMoveState == AIController::ModeStuck) return; if (!obj->getGoal()) return; ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); // Don't check for ai stuckness if animation during // an anim-clip effect override. if (sbo->getDamageState() == ShapeBase::Enabled && !(sbo->anim_clip_flags & ShapeBase::ANIM_OVERRIDDEN) && !sbo->isAnimationLocked()) { - if (obj->mMovement.mMoveStuckTestCountdown > 0) - --obj->mMovement.mMoveStuckTestCountdown; - else + // We should check to see if we are stuck... + F32 locationDelta = (obj->getAIInfo()->getPosition() - obj->getAIInfo()->mLastPos).len(); + if (locationDelta < mMoveStuckTolerance) { - // We should check to see if we are stuck... - F32 locationDelta = (obj->getAIInfo()->getPosition() - obj->getAIInfo()->mLastPos).len(); - if (locationDelta < mMoveStuckTolerance) + if (obj->mMovement.mMoveStuckTestCountdown > 0) + --obj->mMovement.mMoveStuckTestCountdown; + else { // If we are slowing down, then it's likely that our location delta will be less than // our move stuck tolerance. Because we can be both slowing and stuck // we should TRY to check if we've moved. This could use better detection. if (obj->mMovement.mMoveState != AIController::ModeSlowing || locationDelta == 0) { - obj->mMovement.mMoveState = AIController::ModeStuck; obj->mMovement.onStuck(); - obj->throwCallback("onStuck"); } + obj->mMovement.mMoveStuckTestCountdown = obj->mControllerData->mMoveStuckTestDelay; } } - obj->getAIInfo()->mLastPos = obj->getAIInfo()->getPosition(); } } +AIControllerData::AIControllerData() +{ + mMoveTolerance = 0.25; + mFollowTolerance = 1.0; + mAttackRadius = 2.0; + mMoveStuckTolerance = 0.01f; + mMoveStuckTestDelay = 30; + mLinkTypes = LinkData(AllFlags); + mNavSize = AINavigation::Regular; + + mFlocking.mChance = 100; + mFlocking.mMin = 1.0f; + mFlocking.mMax = 3.0f; + mFlocking.mSideStep = 0.125f; + + resolveYawPtr.bind(this, &AIControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); + resolveRollPtr.bind(this, &AIControllerData::resolveRoll); + resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); + resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState); + resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); +} + void AIControllerData::initPersistFields() { docsURL; @@ -518,6 +537,14 @@ void AIControllerData::initPersistFields() addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance considered in firing range for callback purposes."); + addFieldV("FlockChance", TypeRangedS32, Offset(mFlocking.mChance, AIControllerData), &CommonValidators::S32Percent, + "@brief chance of flocking."); + addFieldV("FlockMin", TypeRangedF32, Offset(mFlocking.mMin, AIControllerData), &CommonValidators::PositiveFloat, + "@brief min flocking separation distance."); + addFieldV("FlockMax", TypeRangedF32, Offset(mFlocking.mMax, AIControllerData), &CommonValidators::PositiveFloat, + "@brief max flocking clustering distance."); + addFieldV("FlockSideStep", TypeRangedF32, Offset(mFlocking.mSideStep, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before we stop moving out of the way."); endGroup("AI"); #ifdef TORQUE_NAVIGATION_ENABLED @@ -626,7 +653,7 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F // What is our target Point3F desired; - desired = obj->getNav()->mMoveDestination; + desired = obj->getNav()->getMoveDestination(); MatrixF mat = wvo->getTransform(); Point3F center, front; @@ -739,4 +766,60 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat movePtr->yaw = getSteeringAngle(obj, location); } }; + +void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) +{ + F32 xDiff = obj->getNav()->getMoveDestination().x - location.x; + F32 yDiff = obj->getNav()->getMoveDestination().y - location.y; + Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); + + Point2F movTarg; + + // Build move direction in world space + if (mIsZero(xDiff)) + movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; + else + { + if (mIsZero(yDiff)) + movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; + else + { + if (mFabs(xDiff) > mFabs(yDiff)) + { + F32 value = mFabs(yDiff / xDiff); + movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -value : value; + movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; + } + else + { + F32 value = mFabs(xDiff / yDiff); + movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value; + movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; + } + } + } + // Rotate the move into object space (this really only needs + // a 2D matrix) + Point3F newMove; + MatrixF moveMatrix; + moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); + moveMatrix.mulV(Point3F(movTarg.x, movTarg.y, 0.0f), &newMove); + movTarg.y = newMove.y; + + // Set Throttle. We'll slow down once we get close + // to try and stop on the spot... + if (obj->mMovement.mMoveSlowdown) + { + F32 throttle = obj->mMovement.mMoveSpeed; + F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff); + F32 maxDist = mMoveTolerance * 2; + if (dist < maxDist) + throttle *= dist / maxDist; + movePtr->y *= throttle; + } + else + { + movePtr->y *= obj->mMovement.mMoveSpeed; + } +} #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 5e8b8bf0e..9dba64129 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -84,6 +84,7 @@ public: struct Movement { AIController* mControllerRef; + AIController* getCtrl() { return mControllerRef; }; MoveState mMoveState; F32 mMoveSpeed = 1.0; void setMoveSpeed(F32 speed) { mMoveSpeed = speed; }; @@ -101,6 +102,7 @@ public: struct TriggerState { AIController* mControllerRef; + AIController* getCtrl() { return mControllerRef; }; bool mMoveTriggers[MaxTriggerKeys]; // Trigger sets/gets void setMoveTrigger(U32 slot, const bool isSet = true); @@ -143,23 +145,7 @@ class AIControllerData : public SimDataBlock { public: - AIControllerData() - { - mMoveTolerance = 0.25; - mFollowTolerance = 1.0; - mAttackRadius = 2.0; - mMoveStuckTolerance = 0.01f; - mMoveStuckTestDelay = 30; - mLinkTypes = LinkData(AllFlags); - mNavSize = AINavigation::Regular; - - resolveYawPtr.bind(this, &AIControllerData::resolveYaw); - resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); - resolveRollPtr.bind(this, &AIControllerData::resolveRoll); - resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); - resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState); - resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); - }; + AIControllerData(); ~AIControllerData() {}; void packData(BitStream* stream) override {}; void unpackData(BitStream* stream) override {}; @@ -171,6 +157,14 @@ public: F32 mAttackRadius; // Distance to trigger weaponry calcs F32 mMoveStuckTolerance; // Distance tolerance on stuck check S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck + + struct Flocking { + U32 mChance; // chance of flocking + F32 mMin; // min flocking separation distance + F32 mMax; // max flocking clustering distance + F32 mSideStep; // Distance from destination before we stop moving out of the way + } mFlocking; + /// Types of link we can use. LinkData mLinkTypes; AINavigation::NavSize mNavSize; @@ -223,9 +217,11 @@ public: AIWheeledVehicleControllerData() { resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); + resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); } F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h index a392f4553..e64bd38d3 100644 --- a/Engine/source/T3D/AI/AIInfo.h +++ b/Engine/source/T3D/AI/AIInfo.h @@ -22,8 +22,8 @@ #ifndef _AIINFO_H_ #define _AIINFO_H_ -#ifndef _SHAPEBASE_H_ -#include "T3D/shapeBase.h" +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" #endif class AIController; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index abe6d512a..1171af58a 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -21,6 +21,9 @@ //----------------------------------------------------------------------------- #include "AINavigation.h" #include "AIController.h" +#include "T3D/shapeBase.h" + +static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType | AIObjectType; AINavigation::AINavigation(AIController* controller) { @@ -283,6 +286,127 @@ void AINavigation::clearPath() mPathData = PathData(); } +void AINavigation::flock() +{ + AIControllerData::Flocking flockingData = getCtrl()->mControllerData->mFlocking; + SimObjectPtr obj = getCtrl()->getAIInfo()->mObj; + + if (mRandI(0,100) > flockingData.mChance) + return; + + obj->disableCollision(); + Point3F pos = obj->getBoxCenter(); + Point3F searchArea = Point3F(flockingData.mMin / 2, flockingData.mMax / 2, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2); + + F32 maxFlocksq = flockingData.mMax * flockingData.mMax; + + if (getCtrl()->getGoal()) + { + Point3F dest = mMoveDestination; + + if (getCtrl()->mMovement.mMoveState == AIController::ModeStuck) + { + Point3F shuffle = Point3F(mRandF() - 0.5, mRandF() - 0.5, 0); + shuffle.normalize(); + dest += shuffle * flockingData.mMin; + } + + dest.z = pos.z; + if ((pos - dest).len() > flockingData.mSideStep) + { + //find closest object + SimpleQueryList sql; + Box3F queryBox = Box3F(pos - searchArea, pos + searchArea); + obj->getContainer()->findObjects(queryBox, AIObjectType, SimpleQueryList::insertionCallback, &sql); + sql.mList.remove(obj); + + Point3F avoidanceOffset = Point3F::Zero; + U32 found = 0; + + //avoid objects in the way + RayInfo info; + if (obj->getContainer()->castRay(pos, dest + Point3F(0, 0, obj->getObjBox().len_z() / 2), sAILoSMask, &info)) + { + Point3F blockerOffset = (info.point - dest); + blockerOffset.z = 0; + avoidanceOffset += blockerOffset; + } + + //avoid bots that are too close + for (U32 i = 0; i < sql.mList.size(); i++) + { + ShapeBase* other = dynamic_cast(sql.mList[i]); + Point3F objectCenter = other->getBoxCenter(); + + F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin; + sumRad += getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + + Point3F offset = (pos - objectCenter); + F32 offsetLensq = offset.lenSquared(); //square roots are expensive, so use squared val compares + if ((flockingData.mMin > 0) && (offsetLensq < (sumRad * sumRad))) + { + other->disableCollision(); + if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask, &info)) + { + found++; + offset.normalizeSafe(); + offset *= sumRad; + avoidanceOffset += offset; //accumulate total group, move away from that + } + other->enableCollision(); + } + } + //if we don't have to worry about bumping into one another (nothing found lower than minFLock), see about grouping up + if (found == 0) + { + for (U32 i = 0; i < sql.mList.size(); i++) + { + ShapeBase* other = static_cast(sql.mList[i]); + Point3F objectCenter = other->getBoxCenter(); + + F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin; + sumRad += getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + + Point3F offset = (pos - objectCenter); + if ((flockingData.mMin > 0) && ((sumRad * sumRad) < (maxFlocksq))) + { + other->disableCollision(); + if (!obj->getContainer()->castRay(pos, other->getBoxCenter(), sAILoSMask, &info)) + { + found++; + offset.normalizeSafe(); + offset *= sumRad; + avoidanceOffset -= offset; // subtract total group, move toward it + } + other->enableCollision(); + } + } + } + + avoidanceOffset.z = 0; + avoidanceOffset.x = (mRandF() * avoidanceOffset.x) * 0.5 + avoidanceOffset.x * 0.75; + avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75; + if (avoidanceOffset.lenSquared() < (maxFlocksq)) + { + avoidanceOffset.normalizeSafe(); + dest += avoidanceOffset; + } + + //if we're not jumping... + if ((mPathData.path) && !(mPathData.path->getFlags(mPathData.index) & JumpFlag)) + { + //make sure we don't run off a cliff + Point3F zlen(0, 0, getCtrl()->getAIInfo()->mRadius); + if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) + { + mMoveDestination = dest; + } + } + } + } + obj->enableCollision(); +} + DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), "@brief Tells the AI to move to the location provided\n\n" diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 978ae01c6..d4e66c3c1 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -96,6 +96,7 @@ struct AINavigation /// Move to the specified node in the current path. void moveToNode(S32 node); + void flock(); }; #endif diff --git a/Engine/source/T3D/gameFunctions.cpp b/Engine/source/T3D/gameFunctions.cpp index c40e2f081..24487c5ec 100644 --- a/Engine/source/T3D/gameFunctions.cpp +++ b/Engine/source/T3D/gameFunctions.cpp @@ -669,6 +669,7 @@ static void RegisterGameFunctions() Con::setIntVariable("$TypeMasks::PathShapeObjectType", PathShapeObjectType); // PATHSHAPE END Con::setIntVariable("$TypeMasks::TurretObjectType", TurretObjectType); + Con::setIntVariable("$TypeMasks::AIObjectType", AIObjectType); Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut, "InOut ease for curve movement.\n" diff --git a/Engine/source/T3D/objectTypes.h b/Engine/source/T3D/objectTypes.h index c6b05df74..47fb2edee 100644 --- a/Engine/source/T3D/objectTypes.h +++ b/Engine/source/T3D/objectTypes.h @@ -174,7 +174,7 @@ enum SceneObjectTypes /// @see TurretShape TurretObjectType = BIT(29), N_A_31 = BIT(30), - N_A_32 = BIT(31), + AIObjectType = BIT(31), /// @} }; diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index ee715517b..00fd037b0 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -461,7 +461,6 @@ PlayerData::PlayerData() physicsPlayerType = StringTable->EmptyString(); mControlMap = StringTable->EmptyString(); - mAIControllData = NULL; dMemset( actionList, 0, sizeof(actionList) ); } @@ -742,8 +741,6 @@ void PlayerData::initPersistFields() addGroup( "Movement" ); addField("controlMap", TypeString, Offset(mControlMap, PlayerData), "@brief movemap used by these types of objects.\n\n"); - addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, PlayerData), - "@brief ai controller used by these types of objects.\n\n"); addFieldV( "maxStepHeight", TypeRangedF32, Offset(maxStepHeight, PlayerData), &CommonValidators::PositiveFloat, "@brief Maximum height the player can step up.\n\n" @@ -1645,7 +1642,6 @@ Player::Player() mLastAbsoluteYaw = 0.0f; mLastAbsolutePitch = 0.0f; mLastAbsoluteRoll = 0.0f; - mAIController = NULL; afx_init(); } @@ -1656,7 +1652,6 @@ Player::~Player() delete mShapeFPInstance[i]; mShapeFPInstance[i] = 0; } - if (mAIController) mAIController->deleteObject(); } @@ -2260,42 +2255,6 @@ void Player::advanceTime(F32 dt) } } } - -bool Player::setAIController(SimObjectId controller) -{ - if (Sim::findObject(controller, mAIController) && mAIController->mControllerData) - { - mAIController->setAIInfo(this); - return true; - } - Con::errorf("unable to find AIController : %i", controller); - mAIController = NULL; - return false; -} - -DefineEngineMethod(Player, setAIController, bool, (S32 controller), , "") -{ - return object->setAIController(controller); -} - -DefineEngineMethod(Player, getAIController, AIController*, (), , "") -{ - return object->getAIController(); -} - - -bool Player::getAIMove(Move* move) -{ - if (!isServerObject()) return false; - if (mAIController) - { - mAIController->getAIMove(move); //actual result - return true; - } - - return false; -} - void Player::setState(ActionState state, U32 recoverTicks) { if (state != mState) { diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index df10ba291..2c6c88ab5 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -55,7 +55,6 @@ class OpenVRTrackedObject; #include "navigation/navMesh.h" #include "navigation/coverPoint.h" #endif // TORQUE_NAVIGATION_ENABLED -#include "AI/AIController.h" //---------------------------------------------------------------------------- @@ -354,7 +353,6 @@ struct PlayerData: public ShapeBaseData { // Jump off surfaces at their normal rather than straight up bool jumpTowardsNormal; StringTableEntry mControlMap; - AIControllerData* mAIControllData; // For use if/when mPhysicsPlayer is created StringTableEntry physicsPlayerType; @@ -496,7 +494,6 @@ protected: SimObjectPtr mControlObject; ///< Controlling object - AIController* mAIController; /// @name Animation threads & data /// @{ @@ -762,10 +759,6 @@ public: Point3F getMomentum() const override; void setMomentum(const Point3F &momentum) override; bool displaceObject(const Point3F& displaceVector) override; - virtual bool getAIMove(Move*); - bool setAIController(SimObjectId controller); - AIController* getAIController() { return mAIController; }; - bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here? // diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 968e8fb9b..38e1b57a9 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -69,6 +69,7 @@ #include "core/stream/fileStream.h" #include "T3D/accumulationVolume.h" #include "console/persistenceManager.h" +#include "AI/AIController.h" IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData); @@ -195,7 +196,8 @@ ShapeBaseData::ShapeBaseData() useEyePoint( false ), isInvincible( false ), renderWhenDestroyed( true ), - inheritEnergyFromMount( false ) + inheritEnergyFromMount( false ), + mAIControllData(NULL) { INIT_ASSET(Shape); INIT_ASSET(DebrisShape); @@ -544,6 +546,10 @@ void ShapeBaseData::initPersistFields() addField("silentBBoxValidation", TypeBool, Offset(silent_bbox_check, ShapeBaseData)); INITPERSISTFIELD_SHAPEASSET(DebrisShape, ShapeBaseData, "The shape asset to use for auto-generated breakups via blowup(). @note may not be functional."); endGroup( "Shapes" ); + addGroup("Movement"); + addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, ShapeBaseData), + "@brief ai controller used by these types of objects.\n\n"); + endGroup("Movement"); addGroup("Particle Effects"); addField( "explosion", TYPEID< ExplosionData >(), Offset(explosion, ShapeBaseData), @@ -981,7 +987,8 @@ ShapeBase::ShapeBase() mCameraFov( 90.0f ), mIsControlled( false ), mLastRenderFrame( 0 ), - mLastRenderDistance( 0.0f ) + mLastRenderDistance( 0.0f ), + mAIController(NULL) { mTypeMask |= ShapeBaseObjectType | LightObjectType; @@ -1032,6 +1039,7 @@ ShapeBase::~ShapeBase() cur->next = sFreeTimeoutList; sFreeTimeoutList = cur; } + if (mAIController) mAIController->deleteObject(); } void ShapeBase::initPersistFields() @@ -5449,3 +5457,43 @@ DefineEngineMethod(ShapeBase, getNodePoint, Point3F, (const char* nodeName), , return pos; } + +bool ShapeBase::setAIController(SimObjectId controller) +{ + if (Sim::findObject(controller, mAIController) && mAIController->mControllerData) + { + mAIController->setAIInfo(this); + mTypeMask |= AIObjectType; + return true; + } + Con::errorf("unable to find AIController : %i", controller); + mAIController = NULL; + mTypeMask |= ~AIObjectType; + return false; +} + +bool ShapeBase::getAIMove(Move* move) +{ + if (!isServerObject()) return false; + if (!(mTypeMask & VehicleObjectType || mTypeMask & PlayerObjectType)) return false; //only support players and vehicles for now + if (mAIController) + { + mAIController->getAIMove(move); //actual result + mTypeMask |= AIObjectType; + return true; + } + mAIController = NULL; + mTypeMask &= ~AIObjectType; + return false; +} + + +DefineEngineMethod(ShapeBase, setAIController, bool, (S32 controller), , "") +{ + return object->setAIController(controller); +} + +DefineEngineMethod(ShapeBase, getAIController, AIController*, (), , "") +{ + return object->getAIController(); +} diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 66a48fc29..c272110ed 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -88,6 +88,8 @@ class ShapeBase; class SFXSource; class SFXTrack; class SFXProfile; +struct AIController; +struct AIControllerData; typedef void* Light; @@ -555,6 +557,7 @@ public: U32 cubeDescId; ReflectorDesc *reflectorDesc; + AIControllerData* mAIControllData; /// @name Destruction /// /// Everyone likes to blow things up! @@ -1754,6 +1757,11 @@ public: /// Returns true if this object is controlling by something bool isControlled() { return(mIsControlled); } + AIController* mAIController; + bool setAIController(SimObjectId controller); + AIController* getAIController() { return mAIController; }; + virtual bool getAIMove(Move* move); + /// Returns true if this object is being used as a camera in first person bool isFirstPerson() const; diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index e6a86baad..98819cbd2 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -148,7 +148,6 @@ VehicleData::VehicleData() collDamageMultiplier = 0.05f; enablePhysicsRep = true; mControlMap = StringTable->EmptyString(); - mAIControllData = NULL; } @@ -325,9 +324,7 @@ void VehicleData::initPersistFields() addGroup("Movement"); addField("controlMap", TypeString, Offset(mControlMap, VehicleData), "@brief movemap used by these types of objects.\n\n"); - addField("aiControllerData", TYPEID< AIControllerData >(), Offset(mAIControllData, VehicleData), - "@brief ai controller used by these types of objects.\n\n"); - endGroup("Collision"); + endGroup("Movement"); addGroup("Steering"); addFieldV( "jetForce", TypeRangedF32, Offset(jetForce, VehicleData), &CommonValidators::PositiveFloat, @@ -414,7 +411,6 @@ Vehicle::Vehicle() mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; mPhysicsRep = NULL; - mAIController = NULL; } U32 Vehicle::getCollisionMask() @@ -481,7 +477,6 @@ bool Vehicle::onAdd() void Vehicle::onRemove() { SAFE_DELETE(mPhysicsRep); - if (mAIController) mAIController->deleteObject(); U32 i=0; for( i=0; imControllerData) - { - mAIController->setAIInfo(this); - return true; - } - Con::errorf("unable to find AIController : %i", controller); - mAIController = NULL; - return false; -} - -bool Vehicle::getAIMove(Move* move) -{ - if (!isServerObject()) return false; - if (mAIController) - { - mAIController->getAIMove(move); //actual result - return true; - } - - return false; -} - -DefineEngineMethod(Vehicle, setAIController, bool, (S32 controller), , "") -{ - return object->setAIController(controller); -} - -DefineEngineMethod(Vehicle, getAIController, AIController*, (), , "") -{ - return object->getAIController(); -} diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index ee3913a29..ba7bf8e68 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -73,7 +73,6 @@ struct VehicleData : public RigidShapeData bool enablePhysicsRep; StringTableEntry mControlMap; - AIControllerData* mAIControllData; // VehicleData(); @@ -102,7 +101,6 @@ class Vehicle : public RigidShape Point2F mSteering; F32 mThrottle; bool mJetting; - AIController* mAIController; GFXStateBlockRef mSolidSB; SimObjectPtr mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS]; @@ -152,9 +150,6 @@ public: Point2F getSteering() { return mSteering; }; F32 getThrottle() { return mThrottle;}; - bool setAIController(SimObjectId controller); - AIController* getAIController() { return mAIController; }; - virtual bool getAIMove(Move*); /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index fdbc13d72..3a5dca309 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -82,7 +82,7 @@ ImplementBitfieldType(GameTypeMasksType, { SceneObjectTypes::PathShapeObjectType, "$TypeMasks::PathShapeObjectType", "Path-following Objects.\n" }, { SceneObjectTypes::TurretObjectType, "$TypeMasks::TurretObjectType", "Turret Objects.\n" }, { SceneObjectTypes::N_A_31, "$TypeMasks::N_A_31", "unused 31st bit.\n" }, -{ SceneObjectTypes::N_A_32, "$TypeMasks::N_A_32", "unused 32nd bit.\n" }, +{ SceneObjectTypes::AIObjectType, "$TypeMasks::AIObjectType", "AIObjectType.\n" }, EndImplementBitfieldType; diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index 3237816bd..609eb7751 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -37,6 +37,7 @@ #include "gui/buttons/guiButtonCtrl.h" #include "gui/worldEditor/undoActions.h" #include "T3D/gameBase/gameConnection.h" +#include "T3D/AI/AIController.h" IMPLEMENT_CONOBJECT(GuiNavEditorCtrl); @@ -226,12 +227,11 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) missionCleanup->addObject(obj); } mPlayer = obj; - Player* po = dynamic_cast(obj); - if (!po) return; //todo, more types - if (po->getAIController()) + ShapeBase* sbo = dynamic_cast(obj); + if (sbo->getAIController()) { - if (po->getAIController()->mControllerData) - Con::executef(this, "onPlayerSelected", Con::getIntArg(po->getAIController()->mControllerData->mLinkTypes.getFlags())); + if (sbo->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); } else { @@ -398,12 +398,11 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) if(ri.object) { mPlayer = ri.object; - Player* po = dynamic_cast(ri.object); - if (!po) return; //todo, more types - if (po->getAIController()) + ShapeBase* sbo = dynamic_cast(ri.object); + if (sbo->getAIController()) { - if (po->getAIController()->mControllerData) - Con::executef(this, "onPlayerSelected", Con::getIntArg(po->getAIController()->mControllerData->mLinkTypes.getFlags())); + if (sbo->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); } else { @@ -413,12 +412,11 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) } else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) { - Player* po = dynamic_cast(mPlayer.getPointer()); - if (!po) return; //todo, more types - if (po->getAIController()) + ShapeBase* sbo = dynamic_cast(mPlayer.getPointer()); + if (sbo->getAIController()) { - if (po->getAIController()->mControllerData) - po->getAIController()->getNav()->setPathDestination(ri.point,true); + if (sbo->getAIController()->mControllerData) + sbo->getAIController()->getNav()->setPathDestination(ri.point,true); } } } From a609917ceedf67b46dcb2f825e79c5a5ae87d6ed Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 19 Apr 2025 06:37:28 -0500 Subject: [PATCH 48/74] put the flocking protocol ion the repath command itself with a high weight so it prioritizes avoidance vs straight following --- Engine/source/T3D/AI/AIController.cpp | 23 +++++++++++++---------- Engine/source/T3D/AI/AINavigation.cpp | 17 ++++++++++++++--- Engine/source/T3D/AI/AINavigation.h | 1 + 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 8740165d0..5abbc916c 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -110,6 +110,7 @@ void AIController::setAim(SimObjectPtr objIn, F32 rad, Point3F offs } #ifdef TORQUE_NAVIGATION_ENABLED + bool AIController::getAIMove(Move* movePtr) { *movePtr = NullMove; @@ -178,12 +179,17 @@ bool AIController::getAIMove(Move* movePtr) } getGoal()->mInRange = false; } - if (getGoal()->getDist() < mControllerData->mFollowTolerance && !getGoal()->mInRange) + if (getGoal()->getDist() < mControllerData->mFollowTolerance ) { getNav()->clearPath(); mMovement.mMoveState = ModeStop; - getGoal()->mInRange = true; - throwCallback("onTargetInRange"); + + if (!getGoal()->mInRange) + { + getGoal()->mInRange = true; + throwCallback("onTargetInRange"); + } + else getGoal()->mInRange = false; } else { @@ -195,14 +201,11 @@ bool AIController::getAIMove(Move* movePtr) throwCallback("onTargetInFiringRange"); } } - else - getGoal()->mInFiringRange = false; + else getGoal()->mInFiringRange = false; } } } #endif // TORQUE_NAVIGATION_ENABLED - - getNav()->flock(); // Orient towards the aim point, aim object, or towards // our destination. if (getAim() || mMovement.mMoveState != ModeStop) @@ -472,8 +475,8 @@ void AIControllerData::resolveStuck(AIController* obj) if (obj->mMovement.mMoveState != AIController::ModeSlowing || locationDelta == 0) { obj->mMovement.onStuck(); + obj->mMovement.mMoveStuckTestCountdown = obj->mControllerData->mMoveStuckTestDelay; } - obj->mMovement.mMoveStuckTestCountdown = obj->mControllerData->mMoveStuckTestDelay; } } } @@ -489,10 +492,10 @@ AIControllerData::AIControllerData() mLinkTypes = LinkData(AllFlags); mNavSize = AINavigation::Regular; - mFlocking.mChance = 100; + mFlocking.mChance = 90; mFlocking.mMin = 1.0f; mFlocking.mMax = 3.0f; - mFlocking.mSideStep = 0.125f; + mFlocking.mSideStep = 0.01f; resolveYawPtr.bind(this, &AIControllerData::resolveYaw); resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 1171af58a..fdc68de5a 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -126,11 +126,21 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; - // If we're following, get their position. - mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); + if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance) + { + flock(); + mPathData.path->mTo = mMoveDestination; + } + else + { + // If we're following, get their position. + mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); + } + // Update from position and replan. mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(); mPathData.path->plan(); + // Move to first node (skip start pos). moveToNode(1); } @@ -393,8 +403,9 @@ void AINavigation::flock() } //if we're not jumping... - if ((mPathData.path) && !(mPathData.path->getFlags(mPathData.index) & JumpFlag)) + if (mJump == None) { + dest.z -= obj->getObjBox().len_z()*0.5; //make sure we don't run off a cliff Point3F zlen(0, 0, getCtrl()->getAIInfo()->mRadius); if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index d4e66c3c1..acc0aadf2 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -70,6 +70,7 @@ struct AINavigation Point3F mMoveDestination; void setMoveDestination(const Point3F& location, bool slowdown); Point3F getMoveDestination() { return mMoveDestination; }; + void onReachDestination(); /// NavMesh we pathfind on. From b864908efd9c73e1102f464bdf9566e11a451b16 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 19 Apr 2025 07:10:18 -0500 Subject: [PATCH 49/74] clean up a few stray bits --- Engine/source/T3D/AI/AINavigation.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index fdc68de5a..0cb42d998 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -301,9 +301,6 @@ void AINavigation::flock() AIControllerData::Flocking flockingData = getCtrl()->mControllerData->mFlocking; SimObjectPtr obj = getCtrl()->getAIInfo()->mObj; - if (mRandI(0,100) > flockingData.mChance) - return; - obj->disableCollision(); Point3F pos = obj->getBoxCenter(); Point3F searchArea = Point3F(flockingData.mMin / 2, flockingData.mMax / 2, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2); @@ -349,7 +346,8 @@ void AINavigation::flock() Point3F objectCenter = other->getBoxCenter(); F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin; - sumRad += getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + F32 separation = getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + sumRad += separation; Point3F offset = (pos - objectCenter); F32 offsetLensq = offset.lenSquared(); //square roots are expensive, so use squared val compares @@ -360,7 +358,7 @@ void AINavigation::flock() { found++; offset.normalizeSafe(); - offset *= sumRad; + offset *= sumRad + separation; avoidanceOffset += offset; //accumulate total group, move away from that } other->enableCollision(); @@ -375,7 +373,8 @@ void AINavigation::flock() Point3F objectCenter = other->getBoxCenter(); F32 sumRad = flockingData.mMin + other->getAIController()->mControllerData->mFlocking.mMin; - sumRad += getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + F32 separation = getCtrl()->getAIInfo()->mRadius + other->getAIController()->getAIInfo()->mRadius; + sumRad += separation; Point3F offset = (pos - objectCenter); if ((flockingData.mMin > 0) && ((sumRad * sumRad) < (maxFlocksq))) @@ -385,7 +384,7 @@ void AINavigation::flock() { found++; offset.normalizeSafe(); - offset *= sumRad; + offset *= sumRad + separation; avoidanceOffset -= offset; // subtract total group, move toward it } other->enableCollision(); @@ -398,7 +397,6 @@ void AINavigation::flock() avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75; if (avoidanceOffset.lenSquared() < (maxFlocksq)) { - avoidanceOffset.normalizeSafe(); dest += avoidanceOffset; } From 185acd23e068a097576300ed9c2a8cedd6fa068d Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 19 Apr 2025 14:35:18 -0500 Subject: [PATCH 50/74] stop controllobjects from fighting --- Engine/source/T3D/shapeBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 38e1b57a9..4cd5d333b 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -5475,6 +5475,7 @@ bool ShapeBase::setAIController(SimObjectId controller) bool ShapeBase::getAIMove(Move* move) { if (!isServerObject()) return false; + if (isControlled()) return false; //something else is steering us, so use that one's controller if (!(mTypeMask & VehicleObjectType || mTypeMask & PlayerObjectType)) return false; //only support players and vehicles for now if (mAIController) { From 32d95d3b8b5041d6593d06709ee647345a5200e4 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 20 Apr 2025 15:29:26 -0500 Subject: [PATCH 51/74] put AIPlayer support back, and it as the default class to spawn --- Engine/source/navigation/guiNavEditorCtrl.cpp | 52 ++++++++++++++----- .../datablocks/defaultDatablocks.tscript | 6 +-- .../game/tools/navEditor/NavEditorGui.gui | 2 +- .../game/tools/navEditor/main.tscript | 4 +- .../game/tools/navEditor/navEditor.tscript | 15 ++++-- 5 files changed, 56 insertions(+), 23 deletions(-) diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index 609eb7751..90a6e7f78 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -227,15 +227,23 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) missionCleanup->addObject(obj); } mPlayer = obj; - ShapeBase* sbo = dynamic_cast(obj); - if (sbo->getAIController()) + AIPlayer* asAIPlayer = dynamic_cast(obj); + if (asAIPlayer) //try direct { - if (sbo->getAIController()->mControllerData) - Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); + Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags())); } else { - Con::executef(this, "onPlayerSelected"); + ShapeBase* sbo = dynamic_cast(obj); + if (sbo->getAIController()) + { + if (sbo->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); + } + else + { + Con::executef(this, "onPlayerSelected"); + } } } } @@ -398,25 +406,41 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) if(ri.object) { mPlayer = ri.object; - ShapeBase* sbo = dynamic_cast(ri.object); - if (sbo->getAIController()) + AIPlayer* asAIPlayer = dynamic_cast(mPlayer.getPointer()); + if (asAIPlayer) //try direct { - if (sbo->getAIController()->mControllerData) - Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); + Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags())); } else { - Con::executef(this, "onPlayerSelected"); + ShapeBase* sbo = dynamic_cast(mPlayer.getPointer()); + if (sbo->getAIController()) + { + if (sbo->getAIController()->mControllerData) + Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags())); + } + else + { + Con::executef(this, "onPlayerSelected"); + } } } } else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) { - ShapeBase* sbo = dynamic_cast(mPlayer.getPointer()); - if (sbo->getAIController()) + AIPlayer* asAIPlayer = dynamic_cast(mPlayer.getPointer()); + if (asAIPlayer) //try direct { - if (sbo->getAIController()->mControllerData) - sbo->getAIController()->getNav()->setPathDestination(ri.point,true); + asAIPlayer->setPathDestination(ri.point); + } + else + { + ShapeBase* sbo = dynamic_cast(mPlayer.getPointer()); + if (sbo->getAIController()) + { + if (sbo->getAIController()->mControllerData) + sbo->getAIController()->getNav()->setPathDestination(ri.point, true); + } } } } diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index ffdc4806c..6d9ee6067 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -172,10 +172,10 @@ datablock LightAnimData( SpinLightAnim ) datablock AIPlayerControllerData( aiPlayerControl ) { - + moveTolerance = 0.25; followTolerance = 1.0; mAttackRadius = 2; }; datablock AIWheeledVehicleControllerData( aiCarControl ) { - -}; \ No newline at end of file + moveTolerance = 1.0; followTolerance = 2.0; mAttackRadius = 5.0; +}; diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui index 92505f4e7..18646fe1a 100644 --- a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui @@ -485,7 +485,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) { VertSizing = "bottom"; Extent = "90 18"; text = "Stop"; - command = "NavEditorGui.getPlayer().stop();"; + command = "NavEditorGui.stop();"; }; }; }; diff --git a/Templates/BaseGame/game/tools/navEditor/main.tscript b/Templates/BaseGame/game/tools/navEditor/main.tscript index bfc06d103..3c2ef7aab 100644 --- a/Templates/BaseGame/game/tools/navEditor/main.tscript +++ b/Templates/BaseGame/game/tools/navEditor/main.tscript @@ -87,7 +87,7 @@ function NavEditorPlugin::onWorldEditorStartup(%this) // Add ourselves to the Editor Settings window. exec("./NavEditorSettingsTab.gui"); - //ESettingsWindow.addTabPage(ENavEditorSettingsPage); + ESettingsWindow.addTabPage(ENavEditorSettingsPage); ENavEditorSettingsPage.init(); // Add items to World Editor Creator @@ -241,7 +241,7 @@ function NavEditorPlugin::initSettings(%this) { EditorSettings.beginGroup("NavEditor", true); - EditorSettings.setDefaultValue("SpawnClass", "Player"); + EditorSettings.setDefaultValue("SpawnClass", "AIPlayer"); EditorSettings.setDefaultValue("SpawnDatablock", "DefaultPlayerData"); EditorSettings.endGroup(); diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 75a84977b..159599ba6 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -459,13 +459,13 @@ function NavEditorGui::onLinkSelected(%this, %flags) function NavEditorGui::onPlayerSelected(%this, %flags) { - if (!isObject(%this.getPlayer().aiController)) + if (!isObject(%this.getPlayer().aiController) && (!(%this.getPlayer().isMemberOfClass("AIPlayer")))) { %this.getPlayer().aiController = new AIController(){ ControllerData = aiPlayerControl; }; %this.getPlayer().setAIController(%this.getPlayer().aiController); - NavMeshIgnore(%this.getPlayer(), true); - %this.getPlayer().setDamageState("Enabled"); } + NavMeshIgnore(%this.getPlayer(), true); + %this.getPlayer().setDamageState("Enabled"); updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags); } @@ -559,6 +559,15 @@ function NavEditorGui::followObject(%this) } } +function NavEditorGui::stop(%this) +{ + if (isObject(%this.getPlayer().aiController)) + %this.getPlayer().aiController.stop(); + else + { + NavEditorGui.getPlayer().stop(); + } +} function NavInspector::inspect(%this, %obj) { %name = ""; From fdb64b15a84da07755f374f0df9cd0b5217a1834 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 20 Apr 2025 21:41:04 -0500 Subject: [PATCH 52/74] stop spamming the console with callack status --- Engine/source/T3D/AI/AIController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 5abbc916c..daa06a12d 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -31,7 +31,7 @@ IMPLEMENT_CONOBJECT(AIController); //----------------------------------------------------------------------------- void AIController::throwCallback(const char* name) { - Con::warnf("throwCallback: %s", name); + //Con::warnf("throwCallback: %s", name); Con::executef(mControllerData, name, getIdString()); //controller data callbacks GameBase* gbo = dynamic_cast(getAIInfo()->mObj.getPointer()); From 6200a6f1fb3e2f5640321f41c22cd48e4b3a91a9 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 21 Apr 2025 14:58:11 -0500 Subject: [PATCH 53/74] add a calibrateable mHeightTolerance for players this defaults to 0.001, for wheeledvehicles, 2.0, and for flyingvehicles, 200 fix naveditor cript not looking up the object.datablock.aicontrollerdata fix AIWheeledVehicleControllerData not binding the relevant ::resolvespeed also the relevant ::resolvespeed now lowers the throttle post-turning add AIFlyingVehicleControllerData --- Engine/source/T3D/AI/AIController.cpp | 223 ++++++++++++++---- Engine/source/T3D/AI/AIController.h | 29 ++- .../datablocks/defaultDatablocks.tscript | 6 + .../game/tools/navEditor/navEditor.tscript | 2 +- 4 files changed, 205 insertions(+), 55 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index daa06a12d..c06983a72 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -24,6 +24,7 @@ #include "T3D/player.h" #include "T3D/rigidShape.h" #include "T3D/vehicles/wheeledVehicle.h" +#include "T3D/vehicles/flyingVehicle.h" IMPLEMENT_CONOBJECT(AIController); @@ -164,16 +165,13 @@ bool AIController::getAIMove(Move* movePtr) { if (getGoal()->getDist() > mControllerData->mFollowTolerance) { - F32 raylength = 2.0; //for vehicles SceneObject* obj = getAIInfo()->mObj->getObjectMount(); if (!obj) { obj = getAIInfo()->mObj; - raylength = 0.001f; //for jumping } - RayInfo info; - if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, raylength), StaticShapeObjectType, &info)) + if (obj->getContainer()->castRay(obj->getPosition(), obj->getPosition() - Point3F(0, 0, mControllerData->mHeightTolerance), StaticShapeObjectType, &info)) { getNav()->repath(); } @@ -399,9 +397,11 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m if (mIsZero(xDiff)) movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; else + { if (mIsZero(yDiff)) movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; else + { if (mFabs(xDiff) > mFabs(yDiff)) { F32 value = mFabs(yDiff / xDiff); @@ -414,7 +414,8 @@ void AIControllerData::resolveSpeed(AIController* obj, Point3F location, Move* m movePtr->x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value; movePtr->y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; } - + } + } // Rotate the move into object space (this really only needs // a 2D matrix) Point3F newMove; @@ -484,13 +485,14 @@ void AIControllerData::resolveStuck(AIController* obj) AIControllerData::AIControllerData() { - mMoveTolerance = 0.25; - mFollowTolerance = 1.0; - mAttackRadius = 2.0; + mMoveTolerance = 0.25f; + mFollowTolerance = 1.0f; + mAttackRadius = 2.0f; mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30; mLinkTypes = LinkData(AllFlags); mNavSize = AINavigation::Regular; + mHeightTolerance = 0.001f; mFlocking.mChance = 90; mFlocking.mMin = 1.0f; @@ -638,6 +640,8 @@ void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePt #endif // TORQUE_NAVIGATION_ENABLED } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) @@ -772,57 +776,172 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { - F32 xDiff = obj->getNav()->getMoveDestination().x - location.x; - F32 yDiff = obj->getNav()->getMoveDestination().y - location.y; - Point3F rotation = obj->getAIInfo()->mObj->getTransform().toEuler(); - - Point2F movTarg; - - // Build move direction in world space - if (mIsZero(xDiff)) - movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; - else + WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) { - if (mIsZero(yDiff)) - movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a WheeledVehicle + + Parent::resolveSpeed(obj, location, movePtr); + + VehicleData* db = static_cast(wvo->getDataBlock()); + movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; + movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(AIFlyingVehicleControllerData); +// Build a Triangle .. calculate angle of rotation required to meet target.. +// man there has to be a better way! >:) +F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return 0;//not a FlyingVehicle + + DrivingState steerState = SteerNull; + + // What is our target + Point3F desired; + desired = obj->getNav()->getMoveDestination(); + + MatrixF mat = wvo->getTransform(); + Point3F center, front; + Point3F wFront; + Box3F box = wvo->getObjBox(); + + box.getCenter(¢er); + front = center; + front.y = box.maxExtents.y; // should be true for all these objects + + obj->getAIInfo()->mObj->getWorldBox().getCenter(¢er); + front = center + front; + + Point3F objFront = front; + Point3F offset = front - center; + EulerF rot; + rot = mat.toEuler(); + MatrixF transform(rot); + transform.mulV(offset, &wFront); + front = wFront + center; + + Point3F ftoc; + ftoc.x = mFabs(front.x - center.x); + ftoc.y = mFabs(front.y - center.y); + ftoc.z = mFabs(front.z - center.z); + F32 fToc = mSqrt((ftoc.x * ftoc.x) + (ftoc.y * ftoc.y)); + + Point3F ltoc; + ltoc.x = mFabs(desired.x - center.x); + ltoc.y = mFabs(desired.y - center.y); + ltoc.z = mFabs(desired.z - center.z); + F32 lToc = mSqrt((ltoc.x * ltoc.x) + (ltoc.y * ltoc.y)); + + Point3F ftol; + ftol.x = mFabs(front.x - desired.x); + ftol.y = mFabs(front.y - desired.y); + ftol.z = mFabs(front.z - desired.z); + F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); + + F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); + + F32 finalYaw = mRadToDeg(myAngle); + + F32 maxSteeringAngle = 0; + + VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); + maxSteeringAngle = vd->maxSteeringAngle; + + Point2F steering = wvo->getSteering(); + if (finalYaw < 5 && steering.x != 0.0f) + steerState = Straight; + else if (finalYaw < 5) + steerState = SteerNull; + else + {// Quickly Hack out left or right turn info + Point3F rotData = objFront - desired; + MatrixF leftM(-rot); + Point3F leftP; + leftM.mulV(rotData, &leftP); + leftP = leftP + desired; + + if (leftP.x < desired.x) + steerState = Right; else - { - if (mFabs(xDiff) > mFabs(yDiff)) - { - F32 value = mFabs(yDiff / xDiff); - movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -value : value; - movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -1.0f : 1.0f; - } - else - { - F32 value = mFabs(xDiff / yDiff); - movTarg.x = (location.x > obj->getNav()->getMoveDestination().x) ? -value : value; - movTarg.y = (location.y > obj->getNav()->getMoveDestination().y) ? -1.0f : 1.0f; - } - } + steerState = Left; } - // Rotate the move into object space (this really only needs - // a 2D matrix) - Point3F newMove; - MatrixF moveMatrix; - moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); - moveMatrix.mulV(Point3F(movTarg.x, movTarg.y, 0.0f), &newMove); - movTarg.y = newMove.y; - // Set Throttle. We'll slow down once we get close - // to try and stop on the spot... - if (obj->mMovement.mMoveSlowdown) + F32 throttle = wvo->getThrottle(); + if (throttle < 0.0f && steerState != Straight) { - F32 throttle = obj->mMovement.mMoveSpeed; - F32 dist = mSqrt(xDiff * xDiff + yDiff * yDiff); - F32 maxDist = mMoveTolerance * 2; - if (dist < maxDist) - throttle *= dist / maxDist; - movePtr->y *= throttle; + F32 reverseReduction = 0.25; + steering.x = steering.x * reverseReduction * throttle; } - else + F32 turnAdjust = myAngle - steering.x; + + F32 steer = 0; + switch (steerState) { - movePtr->y *= obj->mMovement.mMoveSpeed; + case Left: + steer = myAngle < maxSteeringAngle ? -turnAdjust : -maxSteeringAngle - steering.x; + break; + case Right: + steer = myAngle < maxSteeringAngle ? turnAdjust : maxSteeringAngle - steering.x; + break; + case Straight: + steer = -steering.x; + break; + default: + break; + }; + + // Con::printf("AI Steering : %f", steer); + return steer; +} + + +void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } + if (!wvo) return;//not a FlyingVehicle + + // Orient towards our destination. + if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { + movePtr->yaw = getSteeringAngle(obj, location); + } +}; + +void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a FlyingVehicle + + Parent::resolveSpeed(obj, location, movePtr); + + VehicleData* db = static_cast(wvo->getDataBlock()); + movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; + movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 9dba64129..06b5e7ff3 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -157,7 +157,7 @@ public: F32 mAttackRadius; // Distance to trigger weaponry calcs F32 mMoveStuckTolerance; // Distance tolerance on stuck check S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck - + F32 mHeightTolerance; // how high above the navmesh are we before we stop trying to repath struct Flocking { U32 mChance; // chance of flocking F32 mMin; // min flocking separation distance @@ -217,12 +217,37 @@ public: AIWheeledVehicleControllerData() { resolveYawPtr.bind(this, &AIWheeledVehicleControllerData::resolveYaw); - resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); + resolveSpeedPtr.bind(this, &AIWheeledVehicleControllerData::resolveSpeed); + mHeightTolerance = 2.0f; } F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); }; + +class AIFlyingVehicleControllerData : public AIControllerData +{ + typedef AIControllerData Parent; + + enum DrivingState { + SteerNull, + Left, + Right, + Straight + }; + +public: + AIFlyingVehicleControllerData() + { + resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw); + resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed); + mHeightTolerance = 200.0f; + } + F32 getSteeringAngle(AIController* obj, Point3F location); + void resolveYaw(AIController* obj, Point3F location, Move* movePtr); + void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); + DECLARE_CONOBJECT(AIFlyingVehicleControllerData); +}; #endif // TORQUE_NAVIGATION_ENABLED #endif //_AICONTROLLER_H_ diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index 6d9ee6067..bc7cc1101 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -179,3 +179,9 @@ datablock AIWheeledVehicleControllerData( aiCarControl ) { moveTolerance = 1.0; followTolerance = 2.0; mAttackRadius = 5.0; }; + +datablock AIFlyingVehicleControllerData( aiPlaneControl ) +{ + moveTolerance = 2.0; followTolerance = 5.0; mAttackRadius = 10.0; +}; + diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 159599ba6..7f7b01804 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -461,7 +461,7 @@ function NavEditorGui::onPlayerSelected(%this, %flags) { if (!isObject(%this.getPlayer().aiController) && (!(%this.getPlayer().isMemberOfClass("AIPlayer")))) { - %this.getPlayer().aiController = new AIController(){ ControllerData = aiPlayerControl; }; + %this.getPlayer().aiController = new AIController(){ ControllerData = %this.getPlayer().getDatablock().aiControllerData; }; %this.getPlayer().setAIController(%this.getPlayer().aiController); } NavMeshIgnore(%this.getPlayer(), true); From 584093f48dade42425c3cd76123c86308826d55c Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 21 Apr 2025 19:13:31 -0500 Subject: [PATCH 54/74] aiInfo getPosition now optionally takes a doCastray bool (off by default) AIFlyingVehicleControllerData add flightfloor and cieling resolvepitch from (portions of) the old aiflyingvehicle resource no reversing for flyingvehicles, so bottom out resolvespeed at 0 --- Engine/source/T3D/AI/AIController.cpp | 74 ++++++++++++++++++++++----- Engine/source/T3D/AI/AIController.h | 9 +++- Engine/source/T3D/AI/AIInfo.cpp | 22 +++++++- Engine/source/T3D/AI/AIInfo.h | 2 +- Engine/source/T3D/AI/AINavigation.cpp | 10 ++-- 5 files changed, 96 insertions(+), 21 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index c06983a72..b0653c513 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -158,7 +158,7 @@ bool AIController::getAIMove(Move* movePtr) if (getGoal()->mObj.isValid()) getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); else if (getGoal()->mPosSet) - getNav()->setPathDestination(getGoal()->getPosition()); + getNav()->setPathDestination(getGoal()->getPosition(true)); } } else @@ -271,7 +271,7 @@ DefineEngineMethod(AIController, setMoveSpeed, void, (F32 speed), , "@brief Sets the move speed for an AI object.\n\n" "@param speed A speed multiplier between 0.0 and 1.0. " - "This is multiplied by the AIPlayer's base movement rates (as defined in " + "This is multiplied by the AIController controlled object's base movement rates (as defined in " "its PlayerData datablock)\n\n" "@see getMoveDestination()\n") @@ -290,7 +290,7 @@ DefineEngineMethod(AIController, getMoveSpeed, F32, (), , } DefineEngineMethod(AIController, stop, void, (), , - "@brief Tells the AIPlayer to stop moving.\n\n") + "@brief Tells the AIController controlled object to stop moving.\n\n") { object->mMovement.stopMove(); } @@ -514,28 +514,35 @@ void AIControllerData::initPersistFields() addFieldV("moveTolerance", TypeRangedF32, Offset(mMoveTolerance, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance from destination before stopping.\n\n" - "When the AIPlayer is moving to a given destination it will move to within " + "When the AIController controlled object is moving to a given destination it will move to within " "this distance of the destination and then stop. By providing this tolerance " - "it helps the AIPlayer from never reaching its destination due to minor obstacles, " + "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance from destination before stopping.\n\n" - "When the AIPlayer is moving to a given destination it will move to within " + "When the AIController controlled object is moving to a given destination it will move to within " "this distance of the destination and then stop. By providing this tolerance " - "it helps the AIPlayer from never reaching its destination due to minor obstacles, " + "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); addFieldV("moveStuckTolerance", TypeRangedF32, Offset(mMoveStuckTolerance, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance tolerance on stuck check.\n\n" - "When the AIPlayer is moving to a given destination, if it ever moves less than " - "this tolerance during a single tick, the AIPlayer is considered stuck. At this point " + "When the AIController controlled object controlled object is moving to a given destination, if it ever moves less than " + "this tolerance during a single tick, the AIController controlled object is considered stuck. At this point " "the onMoveStuck() callback is called on the datablock.\n"); + addFieldV("HeightTolerance", TypeRangedF32, Offset(mHeightTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before stopping.\n\n" + "When the AIController controlled object is moving to a given destination it will move to within " + "this distance of the destination and then stop. By providing this tolerance " + "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " + "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); + addFieldV("moveStuckTestDelay", TypeRangedS32, Offset(mMoveStuckTestDelay, AIControllerData), &CommonValidators::PositiveInt, - "@brief The number of ticks to wait before testing if the AIPlayer is stuck.\n\n" - "When the AIPlayer is asked to move, this property is the number of ticks to wait " - "before the AIPlayer starts to check if it is stuck. This delay allows the AIPlayer " + "@brief The number of ticks to wait before testing if the AIController controlled object is stuck.\n\n" + "When the AIController controlled object is asked to move, this property is the number of ticks to wait " + "before the AIController controlled object starts to check if it is stuck. This delay allows the AIController controlled object " "to accelerate to full speed without its initial slow start being considered as stuck.\n" "@note Set to zero to have the stuck test start immediately.\n"); @@ -796,6 +803,22 @@ void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F loc //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIFlyingVehicleControllerData); + +void AIFlyingVehicleControllerData::initPersistFields() +{ + docsURL; + addGroup("AI"); + + addFieldV("FlightFloor", TypeRangedF32, Offset(mFlightFloor, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, + "@brief Max height we can target."); + + addFieldV("FlightCeiling", TypeRangedF32, Offset(mFlightCeiling, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, + "@brief Max height we can target."); + + endGroup("AI"); + + Parent::initPersistFields(); +} // Build a Triangle .. calculate angle of rotation required to meet target.. // man there has to be a better way! >:) F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) @@ -927,6 +950,31 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati } }; +void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) +{ + FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!wvo) + { + //cover the case of a connection controling an object in turn controlling another + if (obj->getAIInfo()->mObj->getObjectMount()) + wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + } + if (!wvo) return;//not a FlyingVehicle + + Point3F up = wvo->getTransform().getUpVector(); + up.normalize(); + Point3F aimLoc = obj->mMovement.mAimLocation; + aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); + + // Get the Target to AI vector and normalize it. + Point3F toTarg = aimLoc - location; + toTarg.normalize(); + + F32 dotPitch = mDot(up, toTarg); + if (mFabs(dotPitch) > 0.05f) + movePtr->pitch = -dotPitch; +} + void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); @@ -942,6 +990,6 @@ void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F loca VehicleData* db = static_cast(wvo->getDataBlock()); movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; - movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; + movePtr->y = mMax(movePtr->y*1.1 - wvo->getSteering().y / db->maxSteeringAngle, 0.0f); } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 06b5e7ff3..eeaa13a94 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -236,17 +236,24 @@ class AIFlyingVehicleControllerData : public AIControllerData Right, Straight }; - + F32 mFlightFloor; + F32 mFlightCeiling; public: AIFlyingVehicleControllerData() { resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIFlyingVehicleControllerData::resolvePitch); resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed); mHeightTolerance = 200.0f; + mFlightCeiling = 200.0f; + mFlightFloor = 1.0; } + static void initPersistFields(); F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); + void resolvePitch(AIController* obj, Point3F location, Move* movePtr); + DECLARE_CONOBJECT(AIFlyingVehicleControllerData); }; #endif // TORQUE_NAVIGATION_ENABLED diff --git a/Engine/source/T3D/AI/AIInfo.cpp b/Engine/source/T3D/AI/AIInfo.cpp index 2f0022658..f4a02e47f 100644 --- a/Engine/source/T3D/AI/AIInfo.cpp +++ b/Engine/source/T3D/AI/AIInfo.cpp @@ -52,10 +52,30 @@ AIInfo::AIInfo(AIController* controller, Point3F pointIn, F32 radIn) mPosSet = true; }; +Point3F AIInfo::getPosition(bool doCastray) +{ + Point3F pos = (mObj.isValid()) ? mObj->getPosition() : mPosition; + if (doCastray) + { + RayInfo info; + if (gServerContainer.castRay(pos, pos - Point3F(0, 0, getCtrl()->mControllerData->mHeightTolerance), StaticShapeObjectType, &info)) + { + pos = info.point; + } + } + + return pos; +} + F32 AIInfo::getDist() { AIInfo* controlObj = getCtrl()->getAIInfo(); - F32 ret = VectorF(controlObj->getPosition() - getPosition()).len(); + Point3F targPos = getPosition(); + + if (mFabs(targPos.z - controlObj->getPosition().z) < getCtrl()->mControllerData->mHeightTolerance) + targPos.z = controlObj->getPosition().z; + + F32 ret = VectorF(controlObj->getPosition() - targPos).len(); ret -= controlObj->mRadius + mRadius; return ret; } diff --git a/Engine/source/T3D/AI/AIInfo.h b/Engine/source/T3D/AI/AIInfo.h index e64bd38d3..8aede0cae 100644 --- a/Engine/source/T3D/AI/AIInfo.h +++ b/Engine/source/T3D/AI/AIInfo.h @@ -36,7 +36,7 @@ struct AIInfo Point3F mPosition, mLastPos; bool mPosSet; F32 mRadius; - Point3F getPosition() { return (mObj.isValid()) ? mObj->getPosition() : mPosition; } + Point3F getPosition(bool doCastray = false); F32 getDist(); AIInfo() = delete; AIInfo(AIController* controller); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 0cb42d998..ff3e25b22 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -134,11 +134,11 @@ void AINavigation::repath() else { // If we're following, get their position. - mPathData.path->mTo = getCtrl()->getGoal()->getPosition(); + mPathData.path->mTo = getCtrl()->getGoal()->getPosition(true); } // Update from position and replan. - mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(); + mPathData.path->mFrom = getCtrl()->getAIInfo()->getPosition(true); mPathData.path->plan(); // Move to first node (skip start pos). @@ -215,8 +215,8 @@ bool AINavigation::setPathDestination(const Point3F& pos, bool replace) NavPath* path = new NavPath(); path->mMesh = mNavMesh; - path->mFrom = getCtrl()->getAIInfo()->getPosition(); - path->mTo = getCtrl()->getGoal()->getPosition(); + path->mFrom = getCtrl()->getAIInfo()->getPosition(true); + path->mTo = getCtrl()->getGoal()->getPosition(true); path->mFromSet = path->mToSet = true; path->mAlwaysRender = true; path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; @@ -257,7 +257,7 @@ void AINavigation::followObject() if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) return; - if (setPathDestination(getCtrl()->getGoal()->getPosition())) + if (setPathDestination(getCtrl()->getGoal()->getPosition(true))) { getCtrl()->clearCover(); } From 71b8046bb1ff28a69dd7d4f9ea92f99aa6e6ce68 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 22 Apr 2025 00:34:32 -0500 Subject: [PATCH 55/74] shift mode stop and stuck below modeslowing so we can just do basic math filtering fix a few bits of eroneous sidestepping attempts by flying and wheeled vehicles. more work on flyingvehicle resolvepitch and resolvespeed --- Engine/source/T3D/AI/AIController.cpp | 73 +++++++++++++++------------ Engine/source/T3D/AI/AIController.h | 2 +- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index b0653c513..38c0660b8 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -455,7 +455,7 @@ void AIControllerData::resolveTriggerState(AIController* obj, Move* movePtr) void AIControllerData::resolveStuck(AIController* obj) { - if (obj->mMovement.mMoveState == AIController::ModeStuck) return; + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; if (!obj->getGoal()) return; ShapeBase* sbo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); // Don't check for ai stuckness if animation during @@ -766,6 +766,7 @@ F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); if (!wvo) { @@ -783,6 +784,7 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); if (!wvo) { @@ -795,7 +797,7 @@ void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F loc Parent::resolveSpeed(obj, location, movePtr); VehicleData* db = static_cast(wvo->getDataBlock()); - movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; + movePtr->x = 0; movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; } @@ -823,14 +825,14 @@ void AIFlyingVehicleControllerData::initPersistFields() // man there has to be a better way! >:) F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) { - FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) + FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!fvo) { //cover the case of a connection controling an object in turn controlling another if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } - if (!wvo) return 0;//not a FlyingVehicle + if (!fvo) return 0;//not a FlyingVehicle DrivingState steerState = SteerNull; @@ -838,10 +840,10 @@ F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F l Point3F desired; desired = obj->getNav()->getMoveDestination(); - MatrixF mat = wvo->getTransform(); + MatrixF mat = fvo->getTransform(); Point3F center, front; Point3F wFront; - Box3F box = wvo->getObjBox(); + Box3F box = fvo->getObjBox(); box.getCenter(¢er); front = center; @@ -882,10 +884,10 @@ F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F l F32 maxSteeringAngle = 0; - VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); + VehicleData* vd = (VehicleData*)(fvo->getDataBlock()); maxSteeringAngle = vd->maxSteeringAngle; - Point2F steering = wvo->getSteering(); + Point2F steering = fvo->getSteering(); if (finalYaw < 5 && steering.x != 0.0f) steerState = Straight; else if (finalYaw < 5) @@ -904,7 +906,7 @@ F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F l steerState = Left; } - F32 throttle = wvo->getThrottle(); + F32 throttle = fvo->getThrottle(); if (throttle < 0.0f && steerState != Straight) { F32 reverseReduction = 0.25; @@ -935,61 +937,66 @@ F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F l void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { - FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; + FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!fvo) { //cover the case of a connection controling an object in turn controlling another if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } - if (!wvo) return;//not a FlyingVehicle + if (!fvo) return;//not a FlyingVehicle // Orient towards our destination. - if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { - movePtr->yaw = getSteeringAngle(obj, location); - } + movePtr->yaw = getSteeringAngle(obj, location); }; void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) { - FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; + FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!fvo) { //cover the case of a connection controling an object in turn controlling another if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } - if (!wvo) return;//not a FlyingVehicle + if (!fvo) return;//not a FlyingVehicle - Point3F up = wvo->getTransform().getUpVector(); + F32 lastPitch = fvo->getSteering().y* (1.0f - fvo->getThrottle()); + + Point3F up = fvo->getTransform().getUpVector(); up.normalize(); Point3F aimLoc = obj->mMovement.mAimLocation; aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); - // Get the Target to AI vector and normalize it. - Point3F toTarg = aimLoc - location; + // Get the AI to Target vector and normalize it. + Point3F toTarg = location-aimLoc; toTarg.normalize(); F32 dotPitch = mDot(up, toTarg); + if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = -dotPitch; + movePtr->pitch = dotPitch - lastPitch; + else + movePtr->pitch = -lastPitch; } void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) { - FlyingVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) + if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; + FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); + if (!fvo) { //cover the case of a connection controling an object in turn controlling another if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); + fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); } - if (!wvo) return;//not a FlyingVehicle + if (!fvo) return;//not a FlyingVehicle Parent::resolveSpeed(obj, location, movePtr); - VehicleData* db = static_cast(wvo->getDataBlock()); - movePtr->x = 0;// 1.1 - wvo->getSteering().x / db->maxSteeringAngle; - movePtr->y = mMax(movePtr->y*1.1 - wvo->getSteering().y / db->maxSteeringAngle, 0.0f); + movePtr->x = 0; + movePtr->y = mMax(movePtr->y, 0.0f); } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index eeaa13a94..b1ee5d485 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -42,9 +42,9 @@ protected: public: enum MoveState { ModeStop, // AI has stopped moving. - ModeMove, // AI is currently moving. ModeStuck, // AI is stuck, but wants to move. ModeSlowing, // AI is slowing down as it reaches it's destination. + ModeMove, // AI is currently moving. ModeReverse // AI is reversing }; From 7b7de1f10fdb0b3d9052202c875008b790b4e51d Mon Sep 17 00:00:00 2001 From: JeffR Date: Tue, 22 Apr 2025 18:20:12 -0500 Subject: [PATCH 56/74] Adjusts the handling of the disconnect call to not utilize package overrides, but instead a callOnModules invoke like most other cross-module calls. This allows the editor to issue a disconnect call properly again. --- .../scripts/client/connectionToServer.tscript | 6 ++- Templates/BaseGame/game/tools/tools.tscript | 9 +++++ .../worldEditor/scripts/editor.ed.tscript | 37 ------------------- 3 files changed, 14 insertions(+), 38 deletions(-) diff --git a/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript b/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript index cbd4b46cc..94f30857f 100644 --- a/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript +++ b/Templates/BaseGame/game/core/clientServer/scripts/client/connectionToServer.tscript @@ -111,8 +111,12 @@ function handleConnectionErrorMessage(%msgType, %msgString, %msgError) //----------------------------------------------------------------------------- // Disconnect //----------------------------------------------------------------------------- - function disconnect() +{ + callOnModules("disconnect"); +} + +function Core_ClientServer::disconnect(%this) { // We need to stop the client side simulation // else physics resources will not cleanup properly. diff --git a/Templates/BaseGame/game/tools/tools.tscript b/Templates/BaseGame/game/tools/tools.tscript index 47101f55a..04ce56cad 100644 --- a/Templates/BaseGame/game/tools/tools.tscript +++ b/Templates/BaseGame/game/tools/tools.tscript @@ -23,4 +23,13 @@ function ToolsModule::onCreate(%this) function ToolsModule::onDestroy(%this) { +} + +function ToolsModule::disconnect(%this) +{ + if ( isObject( Editor ) && Editor.isEditorEnabled() ) + { + EditorGui.saveAs = false; //whatever edits we were doing are irrelevent now + Editor.close(MainMenuGui); + } } \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript index ff5696dd5..9f0bc5b77 100644 --- a/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.tscript @@ -164,40 +164,3 @@ function toggleEditor(%make) //------------------------------------------------------------------------------ // The editor action maps are defined in editor.bind.tscript GlobalActionMap.bind(keyboard, "f11", fastLoadWorldEdit); - - -// The scenario: -// The editor is open and the user closes the level by any way other than -// the file menu ( exit level ), eg. typing disconnect() in the console. -// -// The problem: -// Editor::close() is not called in this scenario which means onEditorDisable -// is not called on objects which hook into it and also gEditingMission will no -// longer be valid. -// -// The solution: -// Override the stock disconnect() function which is in game scripts from here -// in tools so we avoid putting our code in there. -// -// Disclaimer: -// If you think of a better way to do this feel free. The thing which could -// be dangerous about this is that no one will ever realize this code overriding -// a fairly standard and core game script from a somewhat random location. -// If it 'did' have unforscene sideeffects who would ever find it? - -package EditorDisconnectOverride -{ - function disconnect() - { - if ( isObject( Editor ) && Editor.isEditorEnabled() ) - { - EditorGui.saveAs = false; //whatever edits we were doing are irrelevent now - %mainMenuGUI = ProjectSettings.value("UI/mainMenuName"); - if (isObject( %mainMenuGUI )) - Editor.close( %mainMenuGUI ); - } - - Parent::disconnect(); - } -}; -activatePackage( EditorDisconnectOverride ); From d8ea8803c38ab6d89b14e9f0f8d41e58402183e9 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 22 Apr 2025 22:06:32 -0500 Subject: [PATCH 57/74] simplify calcs by leveraging pre-existing matrix methods and dot product properties --- Engine/source/T3D/AI/AIController.cpp | 286 +++++--------------------- Engine/source/T3D/AI/AIController.h | 15 -- 2 files changed, 46 insertions(+), 255 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 38c0660b8..598dc6d5f8 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -650,119 +650,6 @@ void AIPlayerControllerData::resolveTriggerState(AIController* obj, Move* movePt //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIWheeledVehicleControllerData); -// Build a Triangle .. calculate angle of rotation required to meet target.. -// man there has to be a better way! >:) -F32 AIWheeledVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) -{ - WheeledVehicle* wvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!wvo) - { - //cover the case of a connection controling an object in turn controlling another - if (obj->getAIInfo()->mObj->getObjectMount()) - wvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); - } - if (!wvo) return 0;//not a WheeledVehicle - - DrivingState steerState = SteerNull; - - // What is our target - Point3F desired; - desired = obj->getNav()->getMoveDestination(); - - MatrixF mat = wvo->getTransform(); - Point3F center, front; - Point3F wFront; - Box3F box = wvo->getObjBox(); - - box.getCenter(¢er); - front = center; - front.y = box.maxExtents.y; // should be true for all these objects - - obj->getAIInfo()->mObj->getWorldBox().getCenter(¢er); - front = center + front; - - Point3F objFront = front; - Point3F offset = front - center; - EulerF rot; - rot = mat.toEuler(); - MatrixF transform(rot); - transform.mulV(offset, &wFront); - front = wFront + center; - - Point3F ftoc; - ftoc.x = mFabs(front.x - center.x); - ftoc.y = mFabs(front.y - center.y); - ftoc.z = mFabs(front.z - center.z); - F32 fToc = mSqrt((ftoc.x * ftoc.x) + (ftoc.y * ftoc.y)); - - Point3F ltoc; - ltoc.x = mFabs(desired.x - center.x); - ltoc.y = mFabs(desired.y - center.y); - ltoc.z = mFabs(desired.z - center.z); - F32 lToc = mSqrt((ltoc.x * ltoc.x) + (ltoc.y * ltoc.y)); - - Point3F ftol; - ftol.x = mFabs(front.x - desired.x); - ftol.y = mFabs(front.y - desired.y); - ftol.z = mFabs(front.z - desired.z); - F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); - - F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); - - F32 finalYaw = mRadToDeg(myAngle); - - F32 maxSteeringAngle = 0; - - VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); - maxSteeringAngle = vd->maxSteeringAngle; - - Point2F steering = wvo->getSteering(); - if (finalYaw < 5 && steering.x != 0.0f) - steerState = Straight; - else if (finalYaw < 5) - steerState = SteerNull; - else - {// Quickly Hack out left or right turn info - Point3F rotData = objFront - desired; - MatrixF leftM(-rot); - Point3F leftP; - leftM.mulV(rotData, &leftP); - leftP = leftP + desired; - - if (leftP.x < desired.x) - steerState = Right; - else - steerState = Left; - } - - F32 throttle = wvo->getThrottle(); - if (throttle < 0.0f && steerState != Straight) - { - F32 reverseReduction = 0.25; - steering.x = steering.x * reverseReduction * throttle; - } - F32 turnAdjust = myAngle - steering.x; - - F32 steer = 0; - switch (steerState) - { - case Left: - steer = myAngle < maxSteeringAngle ? -turnAdjust : -maxSteeringAngle - steering.x; - break; - case Right: - steer = myAngle < maxSteeringAngle ? turnAdjust : maxSteeringAngle - steering.x; - break; - case Straight: - steer = -steering.x; - break; - default: - break; - }; - - // Con::printf("AI Steering : %f", steer); - return steer; -} - void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { @@ -776,10 +663,29 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat } if (!wvo) return;//not a WheeledVehicle - // Orient towards our destination. - if (obj->mMovement.mMoveState == AIController::ModeMove || obj->mMovement.mMoveState == AIController::ModeReverse) { - movePtr->yaw = getSteeringAngle(obj, location); - } + F32 lastYaw = wvo->getSteering().x; + + Point3F right = wvo->getTransform().getRightVector(); + right.normalize(); + Point3F aimLoc = obj->mMovement.mAimLocation; + + // Get the Target to AI vector and normalize it. + Point3F toTarg = aimLoc - location; + toTarg.normalize(); + + F32 dotYaw = mDot(right, toTarg); + movePtr->yaw = -lastYaw; + + VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); + F32 maxSteeringAngle = vd->maxSteeringAngle; + if (mFabs(dotYaw) > maxSteeringAngle * 1.5 && wvo->getThrottle() < 0.0f) + dotYaw *= -1.0f; + + if (dotYaw > maxSteeringAngle) dotYaw = maxSteeringAngle; + if (dotYaw < -maxSteeringAngle) dotYaw = -maxSteeringAngle; + + if (mFabs(dotYaw) > 0.05f) + movePtr->yaw = dotYaw - lastYaw; }; void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) @@ -798,7 +704,7 @@ void AIWheeledVehicleControllerData::resolveSpeed(AIController* obj, Point3F loc VehicleData* db = static_cast(wvo->getDataBlock()); movePtr->x = 0; - movePtr->y *= 1.1 - wvo->getSteering().y / db->maxSteeringAngle; + movePtr->y *= mMax((db->maxSteeringAngle-mFabs(movePtr->yaw) / db->maxSteeringAngle),0.75f); } //----------------------------------------------------------------------------- @@ -821,119 +727,6 @@ void AIFlyingVehicleControllerData::initPersistFields() Parent::initPersistFields(); } -// Build a Triangle .. calculate angle of rotation required to meet target.. -// man there has to be a better way! >:) -F32 AIFlyingVehicleControllerData::getSteeringAngle(AIController* obj, Point3F location) -{ - FlyingVehicle* fvo = dynamic_cast(obj->getAIInfo()->mObj.getPointer()); - if (!fvo) - { - //cover the case of a connection controling an object in turn controlling another - if (obj->getAIInfo()->mObj->getObjectMount()) - fvo = dynamic_cast(obj->getAIInfo()->mObj->getObjectMount()); - } - if (!fvo) return 0;//not a FlyingVehicle - - DrivingState steerState = SteerNull; - - // What is our target - Point3F desired; - desired = obj->getNav()->getMoveDestination(); - - MatrixF mat = fvo->getTransform(); - Point3F center, front; - Point3F wFront; - Box3F box = fvo->getObjBox(); - - box.getCenter(¢er); - front = center; - front.y = box.maxExtents.y; // should be true for all these objects - - obj->getAIInfo()->mObj->getWorldBox().getCenter(¢er); - front = center + front; - - Point3F objFront = front; - Point3F offset = front - center; - EulerF rot; - rot = mat.toEuler(); - MatrixF transform(rot); - transform.mulV(offset, &wFront); - front = wFront + center; - - Point3F ftoc; - ftoc.x = mFabs(front.x - center.x); - ftoc.y = mFabs(front.y - center.y); - ftoc.z = mFabs(front.z - center.z); - F32 fToc = mSqrt((ftoc.x * ftoc.x) + (ftoc.y * ftoc.y)); - - Point3F ltoc; - ltoc.x = mFabs(desired.x - center.x); - ltoc.y = mFabs(desired.y - center.y); - ltoc.z = mFabs(desired.z - center.z); - F32 lToc = mSqrt((ltoc.x * ltoc.x) + (ltoc.y * ltoc.y)); - - Point3F ftol; - ftol.x = mFabs(front.x - desired.x); - ftol.y = mFabs(front.y - desired.y); - ftol.z = mFabs(front.z - desired.z); - F32 fTol = mSqrt((ftol.x * ftol.x) + (ftol.y * ftol.y)); - - F32 myAngle = mAcos(((lToc * lToc) + (fToc * fToc) - (fTol * fTol)) / (2 * lToc * fToc)); - - F32 finalYaw = mRadToDeg(myAngle); - - F32 maxSteeringAngle = 0; - - VehicleData* vd = (VehicleData*)(fvo->getDataBlock()); - maxSteeringAngle = vd->maxSteeringAngle; - - Point2F steering = fvo->getSteering(); - if (finalYaw < 5 && steering.x != 0.0f) - steerState = Straight; - else if (finalYaw < 5) - steerState = SteerNull; - else - {// Quickly Hack out left or right turn info - Point3F rotData = objFront - desired; - MatrixF leftM(-rot); - Point3F leftP; - leftM.mulV(rotData, &leftP); - leftP = leftP + desired; - - if (leftP.x < desired.x) - steerState = Right; - else - steerState = Left; - } - - F32 throttle = fvo->getThrottle(); - if (throttle < 0.0f && steerState != Straight) - { - F32 reverseReduction = 0.25; - steering.x = steering.x * reverseReduction * throttle; - } - F32 turnAdjust = myAngle - steering.x; - - F32 steer = 0; - switch (steerState) - { - case Left: - steer = myAngle < maxSteeringAngle ? -turnAdjust : -maxSteeringAngle - steering.x; - break; - case Right: - steer = myAngle < maxSteeringAngle ? turnAdjust : maxSteeringAngle - steering.x; - break; - case Straight: - steer = -steering.x; - break; - default: - break; - }; - - // Con::printf("AI Steering : %f", steer); - return steer; -} - void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { @@ -947,8 +740,19 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati } if (!fvo) return;//not a FlyingVehicle - // Orient towards our destination. - movePtr->yaw = getSteeringAngle(obj, location); + Point3F right = fvo->getTransform().getRightVector(); + right.normalize(); + Point3F aimLoc = obj->mMovement.mAimLocation; + + // Get the Target to AI vector and normalize it. + Point3F toTarg = aimLoc - location; + toTarg.normalize(); + + F32 dotYaw = mDot(right, toTarg); + movePtr->yaw = 0; + + if (mFabs(dotYaw) > 0.05f) + movePtr->yaw = dotYaw; }; void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F location, Move* movePtr) @@ -963,8 +767,6 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca } if (!fvo) return;//not a FlyingVehicle - F32 lastPitch = fvo->getSteering().y* (1.0f - fvo->getThrottle()); - Point3F up = fvo->getTransform().getUpVector(); up.normalize(); Point3F aimLoc = obj->mMovement.mAimLocation; @@ -974,12 +776,16 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca Point3F toTarg = location-aimLoc; toTarg.normalize(); - F32 dotPitch = mDot(up, toTarg); + movePtr->pitch = 0.0f; + Point3F forward = fvo->getTransform().getForwardVector(); + if (mDot(forward, toTarg)>0.0f) + { + F32 dotPitch = mDot(up, toTarg); - if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = dotPitch - lastPitch; - else - movePtr->pitch = -lastPitch; + if (mFabs(dotPitch) > 0.05f) + movePtr->pitch = dotPitch; + } + } void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F location, Move* movePtr) diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index b1ee5d485..b2fe8ffb7 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -206,13 +206,6 @@ class AIWheeledVehicleControllerData : public AIControllerData { typedef AIControllerData Parent; - enum DrivingState { - SteerNull, - Left, - Right, - Straight - }; - public: AIWheeledVehicleControllerData() { @@ -220,7 +213,6 @@ public: resolveSpeedPtr.bind(this, &AIWheeledVehicleControllerData::resolveSpeed); mHeightTolerance = 2.0f; } - F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); DECLARE_CONOBJECT(AIWheeledVehicleControllerData); @@ -230,12 +222,6 @@ class AIFlyingVehicleControllerData : public AIControllerData { typedef AIControllerData Parent; - enum DrivingState { - SteerNull, - Left, - Right, - Straight - }; F32 mFlightFloor; F32 mFlightCeiling; public: @@ -249,7 +235,6 @@ public: mFlightFloor = 1.0; } static void initPersistFields(); - F32 getSteeringAngle(AIController* obj, Point3F location); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); void resolvePitch(AIController* obj, Point3F location, Move* movePtr); From c1d32a9fd69ef070ad5cd65404c35a9eeade3345 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 23 Apr 2025 00:05:57 -0500 Subject: [PATCH 58/74] more cleanups and standardizations --- Engine/source/T3D/AI/AIController.cpp | 30 ++++++++++++--------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 598dc6d5f8..9408dea9c 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -669,16 +669,17 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat right.normalize(); Point3F aimLoc = obj->mMovement.mAimLocation; - // Get the Target to AI vector and normalize it. - Point3F toTarg = aimLoc - location; + // Get the AI to Target vector and normalize it. + Point3F toTarg = location - aimLoc; toTarg.normalize(); - F32 dotYaw = mDot(right, toTarg); + F32 dotYaw = -mDot(right, toTarg); movePtr->yaw = -lastYaw; VehicleData* vd = (VehicleData*)(wvo->getDataBlock()); F32 maxSteeringAngle = vd->maxSteeringAngle; - if (mFabs(dotYaw) > maxSteeringAngle * 1.5 && wvo->getThrottle() < 0.0f) + + if (mFabs(dotYaw) > maxSteeringAngle*1.5f) dotYaw *= -1.0f; if (dotYaw > maxSteeringAngle) dotYaw = maxSteeringAngle; @@ -745,10 +746,10 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati Point3F aimLoc = obj->mMovement.mAimLocation; // Get the Target to AI vector and normalize it. - Point3F toTarg = aimLoc - location; + Point3F toTarg = location - aimLoc; toTarg.normalize(); - F32 dotYaw = mDot(right, toTarg); + F32 dotYaw = -mDot(right, toTarg); movePtr->yaw = 0; if (mFabs(dotYaw) > 0.05f) @@ -772,19 +773,14 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca Point3F aimLoc = obj->mMovement.mAimLocation; aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); - // Get the AI to Target vector and normalize it. - Point3F toTarg = location-aimLoc; + // Get the Target to AI vector and normalize it. + Point3F toTarg = location - aimLoc; toTarg.normalize(); - + F32 lastPitch = fvo->getSteering().y; movePtr->pitch = 0.0f; - Point3F forward = fvo->getTransform().getForwardVector(); - if (mDot(forward, toTarg)>0.0f) - { - F32 dotPitch = mDot(up, toTarg); - - if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = dotPitch; - } + F32 dotPitch = -mDot(up, toTarg); + if (mFabs(dotPitch) > 0.05f) + movePtr->pitch = dotPitch - lastPitch; } From 6efb3843f6d63002d6db37a77a71e8936a12482d Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Wed, 23 Apr 2025 16:25:35 -0500 Subject: [PATCH 59/74] scale flyingvehicle ai pitch by the amount yaw forces it to roll --- Engine/source/T3D/AI/AIController.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 9408dea9c..2cf2c50e5 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -779,6 +779,13 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca F32 lastPitch = fvo->getSteering().y; movePtr->pitch = 0.0f; F32 dotPitch = -mDot(up, toTarg); + + FlyingVehicleData* db = static_cast(fvo->getDataBlock()); + + F32 rollAmt = mFabs(fvo->getThrottle()* movePtr->yaw * db->steeringRollForce); + dotPitch *= 1.0-(mClampF(rollAmt, 0.0,1.0)); // reduce pitch by how much we're rolling + dotPitch *= M_PI_F; + if (mFabs(dotPitch) > 0.05f) movePtr->pitch = dotPitch - lastPitch; From f31acf774ed5a614c3a4f625b4d3e0c3a9931495 Mon Sep 17 00:00:00 2001 From: JeffR Date: Thu, 24 Apr 2025 00:58:20 -0500 Subject: [PATCH 60/74] Adds handling for datablocks to be reloaded if the assets they utilize have their files directly edited. --- Engine/source/T3D/assets/ShapeAsset.h | 8 +++++- Engine/source/T3D/debris.h | 5 +++- Engine/source/T3D/fx/explosion.h | 5 +++- Engine/source/T3D/fx/groundCover.h | 4 ++- Engine/source/T3D/fx/particle.h | 5 +++- Engine/source/T3D/fx/precipitation.h | 10 +++++-- Engine/source/T3D/fx/splash.h | 5 +++- Engine/source/T3D/lightFlareData.h | 5 +++- Engine/source/T3D/physics/physicsDebris.h | 5 +++- Engine/source/T3D/physics/physicsShape.h | 5 +++- Engine/source/T3D/player.h | 7 ++++- Engine/source/T3D/projectile.h | 5 +++- Engine/source/T3D/shapeBase.cpp | 12 ++++++++- Engine/source/T3D/shapeBase.h | 11 +++++--- Engine/source/T3D/vehicles/wheeledVehicle.h | 5 +++- Engine/source/afx/afxMagicMissile.h | 5 +++- Engine/source/afx/ce/afxBillboard.h | 5 +++- Engine/source/afx/ce/afxModel.h | 5 +++- Engine/source/afx/ce/afxZodiac.h | 5 +++- Engine/source/afx/ce/afxZodiacPlane.h | 5 +++- Engine/source/console/simDatablock.cpp | 29 ++++++++++++--------- Engine/source/console/simDatablock.h | 2 ++ Engine/source/forest/forestItem.h | 5 +++- 23 files changed, 121 insertions(+), 37 deletions(-) diff --git a/Engine/source/T3D/assets/ShapeAsset.h b/Engine/source/T3D/assets/ShapeAsset.h index afa39ca57..2d186346d 100644 --- a/Engine/source/T3D/assets/ShapeAsset.h +++ b/Engine/source/T3D/assets/ShapeAsset.h @@ -370,7 +370,7 @@ public: \ #pragma region Arrayed Asset Macros -#define DECLARE_SHAPEASSET_ARRAY(className,name,max) public: \ +#define DECLARE_SHAPEASSET_ARRAY(className,name,max,changeFunc) public: \ static const U32 sm##name##Count = max;\ Resourcem##name[max];\ StringTableEntry m##name##Name[max]; \ @@ -384,6 +384,10 @@ public: \ \ bool _set##name(StringTableEntry _in, const U32& index)\ {\ + if (m##name##Asset[index].notNull())\ + {\ + m##name##Asset[index]->getChangedSignal().remove(this, &className::changeFunc);\ + }\ if(m##name##AssetId[index] != _in || m##name##Name[index] != _in)\ {\ if(index >= sm##name##Count || index < 0)\ @@ -430,6 +434,8 @@ public: \ if (get##name(index) != StringTable->EmptyString() && m##name##Asset[index].notNull())\ {\ m##name[index] = m##name##Asset[index]->getShapeResource();\ + \ + m##name##Asset[index]->getChangedSignal().notify(this, &className::changeFunc);\ }\ else\ {\ diff --git a/Engine/source/T3D/debris.h b/Engine/source/T3D/debris.h index a0ff99520..1d1116031 100644 --- a/Engine/source/T3D/debris.h +++ b/Engine/source/T3D/debris.h @@ -111,7 +111,10 @@ public: void onPerformSubstitutions() override; bool allowSubstitutions() const override { return true; } - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; //************************************************************************** diff --git a/Engine/source/T3D/fx/explosion.h b/Engine/source/T3D/fx/explosion.h index 447b7747d..0ea4c63d7 100644 --- a/Engine/source/T3D/fx/explosion.h +++ b/Engine/source/T3D/fx/explosion.h @@ -143,7 +143,10 @@ public: ExplosionData* cloneAndPerformSubstitutions(const SimObject*, S32 index=0); bool allowSubstitutions() const override { return true; } - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; diff --git a/Engine/source/T3D/fx/groundCover.h b/Engine/source/T3D/fx/groundCover.h index b2ef23e05..dcc159096 100644 --- a/Engine/source/T3D/fx/groundCover.h +++ b/Engine/source/T3D/fx/groundCover.h @@ -341,7 +341,7 @@ protected: RectF mBillboardRects[MAX_COVERTYPES]; /// The cover shape filenames. - DECLARE_SHAPEASSET_ARRAY(GroundCover, Shape, MAX_COVERTYPES); + DECLARE_SHAPEASSET_ARRAY(GroundCover, Shape, MAX_COVERTYPES, onShapeChanged); DECLARE_ASSET_ARRAY_NET_SETGET(GroundCover, Shape, -1); /// The cover shape instances. @@ -409,6 +409,8 @@ protected: S32 randSeed ); void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void onShapeChanged(){} }; #endif // _GROUNDCOVER_H_ diff --git a/Engine/source/T3D/fx/particle.h b/Engine/source/T3D/fx/particle.h index 12e6a242a..0f328801a 100644 --- a/Engine/source/T3D/fx/particle.h +++ b/Engine/source/T3D/fx/particle.h @@ -92,7 +92,10 @@ class ParticleData : public SimDataBlock static bool protectedSetSizes(void* object, const char* index, const char* data); static bool protectedSetTimes(void* object, const char* index, const char* data); - void onImageChanged() {} + void onImageChanged() + { + reloadOnLocalClient(); + } public: ParticleData(); diff --git a/Engine/source/T3D/fx/precipitation.h b/Engine/source/T3D/fx/precipitation.h index 25aad8e60..a3ca97c40 100644 --- a/Engine/source/T3D/fx/precipitation.h +++ b/Engine/source/T3D/fx/precipitation.h @@ -69,8 +69,14 @@ class PrecipitationData : public GameBaseData void packData(BitStream* stream) override; void unpackData(BitStream* stream) override; - void onDropChanged() {} - void onSplashChanged() {} + void onDropChanged() + { + reloadOnLocalClient(); + } + void onSplashChanged() + { + reloadOnLocalClient(); + } }; struct Raindrop diff --git a/Engine/source/T3D/fx/splash.h b/Engine/source/T3D/fx/splash.h index de98a5bca..0a055ee2f 100644 --- a/Engine/source/T3D/fx/splash.h +++ b/Engine/source/T3D/fx/splash.h @@ -124,7 +124,10 @@ public: DECLARE_IMAGEASSET_ARRAY(SplashData, Texture, NUM_TEX, onTextureChanged); DECLARE_IMAGEASSET_ARRAY_SETGET(SplashData, Texture) - void onTextureChanged() {} + void onTextureChanged() + { + reloadOnLocalClient(); + } ExplosionData* explosion; S32 explosionId; diff --git a/Engine/source/T3D/lightFlareData.h b/Engine/source/T3D/lightFlareData.h index 5239bf3c1..baa74c39b 100644 --- a/Engine/source/T3D/lightFlareData.h +++ b/Engine/source/T3D/lightFlareData.h @@ -106,7 +106,10 @@ protected: void _makePrimBuffer( GFXPrimitiveBufferHandle *pb, U32 count ); void _renderCorona( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); - void onImageChanged() {} + void onImageChanged() + { + reloadOnLocalClient(); + } protected: diff --git a/Engine/source/T3D/physics/physicsDebris.h b/Engine/source/T3D/physics/physicsDebris.h index 2626300e9..9b9ae6861 100644 --- a/Engine/source/T3D/physics/physicsDebris.h +++ b/Engine/source/T3D/physics/physicsDebris.h @@ -97,7 +97,10 @@ public: void packData( BitStream *stream ) override; void unpackData( BitStream *stream ) override; - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } DECLARE_CONOBJECT( PhysicsDebrisData ); diff --git a/Engine/source/T3D/physics/physicsShape.h b/Engine/source/T3D/physics/physicsShape.h index 144580c31..466e39ebf 100644 --- a/Engine/source/T3D/physics/physicsShape.h +++ b/Engine/source/T3D/physics/physicsShape.h @@ -135,7 +135,10 @@ public: SimObjectRef< ExplosionData > explosion; SimObjectRef< PhysicsShapeData > destroyedShape; - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; typedef PhysicsShapeData::SimType PhysicsSimType; diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index b80b3f0e5..ab242d862 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -76,7 +76,7 @@ struct PlayerData: public ShapeBaseData { /// that we don't create a TSThread on the player if we don't /// need to. - DECLARE_SHAPEASSET_ARRAY(PlayerData, ShapeFP, ShapeBase::MaxMountedImages); ///< Used to render with mounted images in first person [optional] + DECLARE_SHAPEASSET_ARRAY(PlayerData, ShapeFP, ShapeBase::MaxMountedImages, onShapeChanged); ///< Used to render with mounted images in first person [optional] DECLARE_ASSET_ARRAY_SETGET(PlayerData, ShapeFP); StringTableEntry imageAnimPrefixFP; ///< Passed along to mounted images to modify @@ -366,6 +366,11 @@ struct PlayerData: public ShapeBaseData { void packData(BitStream* stream) override; void unpackData(BitStream* stream) override; + void onShapeChanged() + { + reloadOnLocalClient(); + } + /// @name Callbacks /// @{ DECLARE_CALLBACK( void, onPoseChange, ( Player* obj, const char* oldPose, const char* newPose ) ); diff --git a/Engine/source/T3D/projectile.h b/Engine/source/T3D/projectile.h index 04e91aa60..2ca827ea7 100644 --- a/Engine/source/T3D/projectile.h +++ b/Engine/source/T3D/projectile.h @@ -154,7 +154,10 @@ public: ProjectileData(const ProjectileData&, bool = false); bool allowSubstitutions() const override { return true; } - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 968e8fb9b..e164c8394 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -345,7 +345,7 @@ bool ShapeBaseData::preload(bool server, String &errorStr) S32 i; U32 assetStatus = ShapeAsset::getAssetErrCode(mShapeAsset); - if (assetStatus == AssetBase::Ok|| assetStatus == AssetBase::UsingFallback) + if (assetStatus == AssetBase::Ok || assetStatus == AssetBase::UsingFallback) { if (!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded()) shapeError = true; @@ -904,7 +904,17 @@ void ShapeBaseData::unpackData(BitStream* stream) silent_bbox_check = stream->readFlag(); } +// +// +void ShapeBaseData::onShapeChanged() +{ + reloadOnLocalClient(); +} +void ShapeBaseData::onDebrisChanged() +{ + reloadOnLocalClient(); +} //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 706f54b72..131992fa9 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -378,7 +378,7 @@ struct ShapeBaseImageData: public GameBaseData { F32 scriptAnimTransitionTime; ///< The amount of time to transition between the previous sequence and new sequence ///< when the script prefix has changed. - DECLARE_SHAPEASSET_ARRAY(ShapeBaseImageData, Shape, MaxShapes); ///< Name of shape to render. + DECLARE_SHAPEASSET_ARRAY(ShapeBaseImageData, Shape, MaxShapes, onShapeChanged); ///< Name of shape to render. DECLARE_ASSET_ARRAY_SETGET(ShapeBaseImageData, Shape); //DECLARE_SHAPEASSET(ShapeBaseImageData, ShapeFP); ///< Name of shape to render in first person (optional). @@ -505,6 +505,11 @@ struct ShapeBaseImageData: public GameBaseData { void handleStateSoundTrack(const U32& stateId); + void onShapeChanged() + { + reloadOnLocalClient(); + } + /// @} /// @name Callbacks @@ -681,8 +686,8 @@ public: Vector txr_tag_remappings; bool silent_bbox_check; - void onShapeChanged() {} - void onDebrisChanged() {} + void onShapeChanged(); + void onDebrisChanged(); public: ShapeBaseData(const ShapeBaseData&, bool = false); }; diff --git a/Engine/source/T3D/vehicles/wheeledVehicle.h b/Engine/source/T3D/vehicles/wheeledVehicle.h index 45161db12..f208fbf5b 100644 --- a/Engine/source/T3D/vehicles/wheeledVehicle.h +++ b/Engine/source/T3D/vehicles/wheeledVehicle.h @@ -74,7 +74,10 @@ struct WheeledVehicleTire: public SimDataBlock void packData(BitStream* stream) override; void unpackData(BitStream* stream) override; - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; diff --git a/Engine/source/afx/afxMagicMissile.h b/Engine/source/afx/afxMagicMissile.h index e3f6ee755..8aa8f8084 100644 --- a/Engine/source/afx/afxMagicMissile.h +++ b/Engine/source/afx/afxMagicMissile.h @@ -66,7 +66,10 @@ protected: public: enum { MaxLifetimeTicks = 4095 }; - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } public: // variables set in datablock definition: diff --git a/Engine/source/afx/ce/afxBillboard.h b/Engine/source/afx/ce/afxBillboard.h index 8918e2f26..81542f716 100644 --- a/Engine/source/afx/ce/afxBillboard.h +++ b/Engine/source/afx/ce/afxBillboard.h @@ -71,7 +71,10 @@ public: static void initPersistFields(); - void onChangeTexture() {} + void onChangeTexture() + { + reloadOnLocalClient(); + } DECLARE_CONOBJECT(afxBillboardData); }; diff --git a/Engine/source/afx/ce/afxModel.h b/Engine/source/afx/ce/afxModel.h index 521409735..de2656b8f 100644 --- a/Engine/source/afx/ce/afxModel.h +++ b/Engine/source/afx/ce/afxModel.h @@ -94,7 +94,10 @@ public: static void initPersistFields(); - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } void onSequenceChanged() {} DECLARE_CONOBJECT(afxModelData); diff --git a/Engine/source/afx/ce/afxZodiac.h b/Engine/source/afx/ce/afxZodiac.h index a7d14c2f1..16f4a8d79 100644 --- a/Engine/source/afx/ce/afxZodiac.h +++ b/Engine/source/afx/ce/afxZodiac.h @@ -56,7 +56,10 @@ public: static void convertGradientRangeFromDegrees(Point2F& gradrange, const Point2F& gradrange_deg); - void onImageChanged() {} + void onImageChanged() + { + reloadOnLocalClient(); + } public: DECLARE_IMAGEASSET(afxZodiacData, Texture, onImageChanged, AFX_GFXZodiacTextureProfile); diff --git a/Engine/source/afx/ce/afxZodiacPlane.h b/Engine/source/afx/ce/afxZodiacPlane.h index 13c9879b5..a32a14d26 100644 --- a/Engine/source/afx/ce/afxZodiacPlane.h +++ b/Engine/source/afx/ce/afxZodiacPlane.h @@ -56,7 +56,10 @@ public: FACES_BITS = 3 }; - void onImageChanged() {} + void onImageChanged() + { + reloadOnLocalClient(); + } public: DECLARE_IMAGEASSET(afxZodiacPlaneData, Texture, onImageChanged, AFX_GFXZodiacTextureProfile); diff --git a/Engine/source/console/simDatablock.cpp b/Engine/source/console/simDatablock.cpp index d3969a069..fc5070224 100644 --- a/Engine/source/console/simDatablock.cpp +++ b/Engine/source/console/simDatablock.cpp @@ -425,39 +425,42 @@ void SimDataBlock::write(Stream &stream, U32 tabStop, U32 flags) // MARK: ---- API ---- //----------------------------------------------------------------------------- - -DefineEngineMethod( SimDataBlock, reloadOnLocalClient, void, (),, - "Reload the datablock. This can only be used with a local client configuration." ) +void SimDataBlock::reloadOnLocalClient() { // Make sure we're running a local client. GameConnection* localClient = GameConnection::getLocalClientConnection(); - if( !localClient ) + if (!localClient) return; // Do an in-place pack/unpack/preload. - if( !object->preload( true, NetConnection::getErrorBuffer() ) ) + if (!preload(true, NetConnection::getErrorBuffer())) { - Con::errorf( NetConnection::getErrorBuffer() ); + Con::errorf(NetConnection::getErrorBuffer()); return; } - U8 buffer[ 16384 ]; - BitStream stream( buffer, 16384 ); + U8 buffer[16384]; + BitStream stream(buffer, 16384); - object->packData( &stream ); + packData(&stream); stream.setPosition(0); - object->unpackData( &stream ); + unpackData(&stream); - if( !object->preload( false, NetConnection::getErrorBuffer() ) ) + if (!preload(false, NetConnection::getErrorBuffer())) { - Con::errorf( NetConnection::getErrorBuffer() ); + Con::errorf(NetConnection::getErrorBuffer()); return; } // Trigger a post-apply so that change notifications respond. - object->inspectPostApply(); + inspectPostApply(); +} +DefineEngineMethod( SimDataBlock, reloadOnLocalClient, void, (),, + "Reload the datablock. This can only be used with a local client configuration." ) +{ + object->reloadOnLocalClient(); } //----------------------------------------------------------------------------- diff --git a/Engine/source/console/simDatablock.h b/Engine/source/console/simDatablock.h index 1b61822d7..2f5c8efcc 100644 --- a/Engine/source/console/simDatablock.h +++ b/Engine/source/console/simDatablock.h @@ -176,6 +176,8 @@ public: /// Used by the console system to automatically tell datablock classes apart /// from non-datablock classes. static const bool __smIsDatablock = true; + + void reloadOnLocalClient(); protected: struct SubstitutionStatement { diff --git a/Engine/source/forest/forestItem.h b/Engine/source/forest/forestItem.h index 42bf56a5f..64e6eb611 100644 --- a/Engine/source/forest/forestItem.h +++ b/Engine/source/forest/forestItem.h @@ -143,7 +143,10 @@ public: return theSignal; } - void onShapeChanged() {} + void onShapeChanged() + { + reloadOnLocalClient(); + } }; typedef Vector ForestItemDataVector; From 24db0305f64c491da382d375e558af9c49c97160 Mon Sep 17 00:00:00 2001 From: JeffR Date: Thu, 24 Apr 2025 13:24:26 -0500 Subject: [PATCH 61/74] Modified callback to ensure that when a shapeAsset changes, groundCovers properly re-initialize to reflect the change --- Engine/source/T3D/fx/groundCover.cpp | 5 ++++- Engine/source/T3D/fx/groundCover.h | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/fx/groundCover.cpp b/Engine/source/T3D/fx/groundCover.cpp index 377f8bb63..072ef6463 100644 --- a/Engine/source/T3D/fx/groundCover.cpp +++ b/Engine/source/T3D/fx/groundCover.cpp @@ -852,8 +852,11 @@ void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream ) // It's sloppy, but it works for now. _freeCells(); - if ( isProperlyAdded() ) + if (isProperlyAdded()) + { _initMaterial(); + _initShapes(); + } } } diff --git a/Engine/source/T3D/fx/groundCover.h b/Engine/source/T3D/fx/groundCover.h index dcc159096..af5d02858 100644 --- a/Engine/source/T3D/fx/groundCover.h +++ b/Engine/source/T3D/fx/groundCover.h @@ -410,7 +410,11 @@ protected: void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); - void onShapeChanged(){} + void onShapeChanged() + { + _initShapes(); + setMaskBits(U32(-1)); + } }; #endif // _GROUNDCOVER_H_ From 675bdfe6b31c83fb8110bb4def04c33fb9e6765f Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 25 Apr 2025 18:50:16 -0500 Subject: [PATCH 62/74] fix pack/unpack data for AIControllerData's (though we still send nothing, we do need to mark it clientside as false) more pitchwork for flying vehicle drivers when flocking is irrelevant just path to next node --- Engine/source/T3D/AI/AIController.cpp | 13 ++++++------- Engine/source/T3D/AI/AIController.h | 4 ++-- Engine/source/T3D/AI/AINavigation.cpp | 18 +++++++++++------- Engine/source/T3D/AI/AINavigation.h | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 2cf2c50e5..7e383a5e8 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -777,17 +777,18 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca Point3F toTarg = location - aimLoc; toTarg.normalize(); F32 lastPitch = fvo->getSteering().y; + movePtr->pitch = 0.0f; - F32 dotPitch = -mDot(up, toTarg); + F32 dotPitch = mDot(up, toTarg); FlyingVehicleData* db = static_cast(fvo->getDataBlock()); - F32 rollAmt = mFabs(fvo->getThrottle()* movePtr->yaw * db->steeringRollForce); + F32 rollAmt = mFabs(fvo->getThrottle()* movePtr->yaw / (db->steeringRollForce+1.0)); dotPitch *= 1.0-(mClampF(rollAmt, 0.0,1.0)); // reduce pitch by how much we're rolling - dotPitch *= M_PI_F; + dotPitch *= M_2PI_F; if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = dotPitch - lastPitch; + movePtr->pitch = dotPitch; } @@ -803,9 +804,7 @@ void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F loca } if (!fvo) return;//not a FlyingVehicle - Parent::resolveSpeed(obj, location, movePtr); - movePtr->x = 0; - movePtr->y = mMax(movePtr->y, 0.0f); + movePtr->y = obj->mMovement.mMoveSpeed; } #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index b2fe8ffb7..92aa481ea 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -147,8 +147,8 @@ public: AIControllerData(); ~AIControllerData() {}; - void packData(BitStream* stream) override {}; - void unpackData(BitStream* stream) override {}; + void packData(BitStream* stream) override { Parent::packData(stream); }; + void unpackData(BitStream* stream) override { Parent::unpackData(stream); }; static void initPersistFields(); DECLARE_CONOBJECT(AIControllerData); diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index ff3e25b22..9534313ea 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -126,9 +126,8 @@ void AINavigation::repath() if (mPathData.path.isNull() || !mPathData.owned) return; - if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance) + if (mRandI(0, 100) < getCtrl()->mControllerData->mFlocking.mChance && flock()) { - flock(); mPathData.path->mTo = mMoveDestination; } else @@ -296,7 +295,7 @@ void AINavigation::clearPath() mPathData = PathData(); } -void AINavigation::flock() +bool AINavigation::flock() { AIControllerData::Flocking flockingData = getCtrl()->mControllerData->mFlocking; SimObjectPtr obj = getCtrl()->getAIInfo()->mObj; @@ -306,7 +305,7 @@ void AINavigation::flock() Point3F searchArea = Point3F(flockingData.mMin / 2, flockingData.mMax / 2, getCtrl()->getAIInfo()->mObj->getObjBox().maxExtents.z / 2); F32 maxFlocksq = flockingData.mMax * flockingData.mMax; - + bool flocking = false; if (getCtrl()->getGoal()) { Point3F dest = mMoveDestination; @@ -403,17 +402,22 @@ void AINavigation::flock() //if we're not jumping... if (mJump == None) { - dest.z -= obj->getObjBox().len_z()*0.5; + dest.z = obj->getPosition().z; //make sure we don't run off a cliff - Point3F zlen(0, 0, getCtrl()->getAIInfo()->mRadius); + Point3F zlen(0, 0, getCtrl()->mControllerData->mHeightTolerance); if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) { - mMoveDestination = dest; + if ((mMoveDestination - dest).len() > getCtrl()->mControllerData->mMoveTolerance) + { + mMoveDestination = dest; + flocking = true; + } } } } } obj->enableCollision(); + return flocking; } DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index acc0aadf2..39b1276a4 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -97,7 +97,7 @@ struct AINavigation /// Move to the specified node in the current path. void moveToNode(S32 node); - void flock(); + bool flock(); }; #endif From b2021caa6d49d7e18fa208ee11f9452205ec3ee9 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Fri, 25 Apr 2025 20:36:22 -0500 Subject: [PATCH 63/74] skip sidestepping off a cliff raycast if we're not sidestepping take current velocity into account for vehicles since some degree of momentum will be maintained --- Engine/source/T3D/AI/AIController.cpp | 6 ++-- Engine/source/T3D/AI/AINavigation.cpp | 40 ++++++++++++++------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 7e383a5e8..237333f09 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -670,7 +670,7 @@ void AIWheeledVehicleControllerData::resolveYaw(AIController* obj, Point3F locat Point3F aimLoc = obj->mMovement.mAimLocation; // Get the AI to Target vector and normalize it. - Point3F toTarg = location - aimLoc; + Point3F toTarg = (location + wvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); F32 dotYaw = -mDot(right, toTarg); @@ -746,7 +746,7 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati Point3F aimLoc = obj->mMovement.mAimLocation; // Get the Target to AI vector and normalize it. - Point3F toTarg = location - aimLoc; + Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); F32 dotYaw = -mDot(right, toTarg); @@ -774,7 +774,7 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); // Get the Target to AI vector and normalize it. - Point3F toTarg = location - aimLoc; + Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); F32 lastPitch = fvo->getSteering().y; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 9534313ea..f5d3fd2cd 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -306,6 +306,7 @@ bool AINavigation::flock() F32 maxFlocksq = flockingData.mMax * flockingData.mMax; bool flocking = false; + U32 found = 0; if (getCtrl()->getGoal()) { Point3F dest = mMoveDestination; @@ -327,7 +328,6 @@ bool AINavigation::flock() sql.mList.remove(obj); Point3F avoidanceOffset = Point3F::Zero; - U32 found = 0; //avoid objects in the way RayInfo info; @@ -390,27 +390,29 @@ bool AINavigation::flock() } } } - - avoidanceOffset.z = 0; - avoidanceOffset.x = (mRandF() * avoidanceOffset.x) * 0.5 + avoidanceOffset.x * 0.75; - avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75; - if (avoidanceOffset.lenSquared() < (maxFlocksq)) + if (found > 0) { - dest += avoidanceOffset; - } - - //if we're not jumping... - if (mJump == None) - { - dest.z = obj->getPosition().z; - //make sure we don't run off a cliff - Point3F zlen(0, 0, getCtrl()->mControllerData->mHeightTolerance); - if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) + avoidanceOffset.z = 0; + avoidanceOffset.x = (mRandF() * avoidanceOffset.x) * 0.5 + avoidanceOffset.x * 0.75; + avoidanceOffset.y = (mRandF() * avoidanceOffset.y) * 0.5 + avoidanceOffset.y * 0.75; + if (avoidanceOffset.lenSquared() < (maxFlocksq)) { - if ((mMoveDestination - dest).len() > getCtrl()->mControllerData->mMoveTolerance) + dest += avoidanceOffset; + } + + //if we're not jumping... + if (mJump == None) + { + dest.z = obj->getPosition().z; + //make sure we don't run off a cliff + Point3F zlen(0, 0, getCtrl()->mControllerData->mHeightTolerance); + if (obj->getContainer()->castRay(dest + zlen, dest - zlen, TerrainObjectType | StaticShapeObjectType | StaticObjectType, &info)) { - mMoveDestination = dest; - flocking = true; + if ((mMoveDestination - dest).len() > getCtrl()->mControllerData->mMoveTolerance) + { + mMoveDestination = dest; + flocking = true; + } } } } From 8fa132707d8904b94d0ce7e330fd0043ba07c799 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 26 Apr 2025 10:37:42 -0500 Subject: [PATCH 64/74] fix stop button --- Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui index 18646fe1a..92505f4e7 100644 --- a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui @@ -485,7 +485,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) { VertSizing = "bottom"; Extent = "90 18"; text = "Stop"; - command = "NavEditorGui.stop();"; + command = "NavEditorGui.getPlayer().stop();"; }; }; }; From b643aa41a2e7585f82738758c049b78a8572c208 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sat, 26 Apr 2025 21:04:06 -0500 Subject: [PATCH 65/74] take flight floor and ceiling into account for yaw too ditch roll compensation as it was causing more problems than it was solving --- Engine/source/T3D/AI/AIController.cpp | 27 +++++++------------ .../datablocks/defaultDatablocks.tscript | 1 + 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 237333f09..e4fa0882c 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -718,10 +718,10 @@ void AIFlyingVehicleControllerData::initPersistFields() docsURL; addGroup("AI"); - addFieldV("FlightFloor", TypeRangedF32, Offset(mFlightFloor, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, - "@brief Max height we can target."); + addFieldV("FlightFloor", TypeRangedF32, Offset(mFlightFloor, AIFlyingVehicleControllerData), &CommonValidators::F32Range, + "@brief Min height we can target."); - addFieldV("FlightCeiling", TypeRangedF32, Offset(mFlightCeiling, AIFlyingVehicleControllerData), &CommonValidators::PositiveFloat, + addFieldV("FlightCeiling", TypeRangedF32, Offset(mFlightCeiling, AIFlyingVehicleControllerData), &CommonValidators::F32Range, "@brief Max height we can target."); endGroup("AI"); @@ -743,15 +743,15 @@ void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F locati Point3F right = fvo->getTransform().getRightVector(); right.normalize(); - Point3F aimLoc = obj->mMovement.mAimLocation; // Get the Target to AI vector and normalize it. + Point3F aimLoc = obj->mMovement.mAimLocation; + aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); - F32 dotYaw = -mDot(right, toTarg); movePtr->yaw = 0; - + F32 dotYaw = -mDot(right, toTarg); if (mFabs(dotYaw) > 0.05f) movePtr->yaw = dotYaw; }; @@ -770,25 +770,18 @@ void AIFlyingVehicleControllerData::resolvePitch(AIController* obj, Point3F loca Point3F up = fvo->getTransform().getUpVector(); up.normalize(); - Point3F aimLoc = obj->mMovement.mAimLocation; - aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); + // Get the Target to AI vector and normalize it. + Point3F aimLoc = obj->mMovement.mAimLocation; + aimLoc.z = mClampF(aimLoc.z, mFlightFloor, mFlightCeiling); Point3F toTarg = (location + fvo->getVelocity() * TickSec) - aimLoc; toTarg.normalize(); - F32 lastPitch = fvo->getSteering().y; movePtr->pitch = 0.0f; F32 dotPitch = mDot(up, toTarg); - - FlyingVehicleData* db = static_cast(fvo->getDataBlock()); - - F32 rollAmt = mFabs(fvo->getThrottle()* movePtr->yaw / (db->steeringRollForce+1.0)); - dotPitch *= 1.0-(mClampF(rollAmt, 0.0,1.0)); // reduce pitch by how much we're rolling - dotPitch *= M_2PI_F; - if (mFabs(dotPitch) > 0.05f) - movePtr->pitch = dotPitch; + movePtr->pitch = dotPitch * M_2PI_F; } diff --git a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript index bc7cc1101..512acd273 100644 --- a/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript +++ b/Templates/BaseGame/game/core/gameObjects/datablocks/defaultDatablocks.tscript @@ -183,5 +183,6 @@ datablock AIWheeledVehicleControllerData( aiCarControl ) datablock AIFlyingVehicleControllerData( aiPlaneControl ) { moveTolerance = 2.0; followTolerance = 5.0; mAttackRadius = 10.0; + FlightFloor = 15; FlightCeiling = 150; }; From 2ae10c7ce13aa58127a513c4c429f5e4d56f39f8 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Sun, 27 Apr 2025 19:49:13 -0500 Subject: [PATCH 66/74] add reload status to onNewDataBlock callback and skip calling onremove/onadd if we're not reloading --- Engine/source/T3D/camera.cpp | 2 +- Engine/source/T3D/debris.cpp | 2 +- Engine/source/T3D/fx/explosion.cpp | 2 +- Engine/source/T3D/fx/lightning.cpp | 2 +- Engine/source/T3D/fx/particleEmitter.cpp | 2 +- Engine/source/T3D/fx/particleEmitterNode.cpp | 2 +- Engine/source/T3D/fx/precipitation.cpp | 2 +- Engine/source/T3D/fx/ribbonNode.cpp | 2 +- Engine/source/T3D/fx/splash.cpp | 2 +- Engine/source/T3D/gameBase/gameBase.cpp | 6 +++--- Engine/source/T3D/gameBase/gameBase.h | 4 ++-- Engine/source/T3D/item.cpp | 2 +- Engine/source/T3D/missionMarker.cpp | 2 +- Engine/source/T3D/pathCamera.cpp | 2 +- Engine/source/T3D/pathShape.cpp | 2 +- Engine/source/T3D/player.cpp | 2 +- Engine/source/T3D/proximityMine.cpp | 2 +- Engine/source/T3D/rigidShape.cpp | 2 +- Engine/source/T3D/staticShape.cpp | 2 +- Engine/source/T3D/trigger.cpp | 2 +- Engine/source/T3D/turret/aiTurretShape.cpp | 2 +- Engine/source/T3D/turret/turretShape.cpp | 2 +- Engine/source/T3D/vehicles/flyingVehicle.cpp | 2 +- Engine/source/T3D/vehicles/hoverVehicle.cpp | 2 +- Engine/source/T3D/vehicles/wheeledVehicle.cpp | 2 +- Engine/source/afx/afxSpellBook.cpp | 2 +- Engine/source/afx/ce/afxParticleEmitter.cpp | 8 ++++---- .../core/utility/scripts/gameObjectManagement.tscript | 6 ++---- 28 files changed, 35 insertions(+), 37 deletions(-) diff --git a/Engine/source/T3D/camera.cpp b/Engine/source/T3D/camera.cpp index 41056f833..3e2408583 100644 --- a/Engine/source/T3D/camera.cpp +++ b/Engine/source/T3D/camera.cpp @@ -345,7 +345,7 @@ bool Camera::onNewDataBlock( GameBaseData *dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/debris.cpp b/Engine/source/T3D/debris.cpp index 7c05e3ec5..78a1e53cc 100644 --- a/Engine/source/T3D/debris.cpp +++ b/Engine/source/T3D/debris.cpp @@ -601,7 +601,7 @@ bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload ) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index f10543198..02676be26 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -1127,7 +1127,7 @@ bool Explosion::onNewDataBlock( GameBaseData *dptr, bool reload ) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/lightning.cpp b/Engine/source/T3D/fx/lightning.cpp index 10447fa6f..44c94b1a8 100644 --- a/Engine/source/T3D/fx/lightning.cpp +++ b/Engine/source/T3D/fx/lightning.cpp @@ -474,7 +474,7 @@ bool Lightning::onNewDataBlock( GameBaseData *dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/particleEmitter.cpp b/Engine/source/T3D/fx/particleEmitter.cpp index c9887f619..00470eb8a 100644 --- a/Engine/source/T3D/fx/particleEmitter.cpp +++ b/Engine/source/T3D/fx/particleEmitter.cpp @@ -1131,7 +1131,7 @@ bool ParticleEmitter::onNewDataBlock( GameBaseData *dptr, bool reload ) return true; } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/particleEmitterNode.cpp b/Engine/source/T3D/fx/particleEmitterNode.cpp index 26e8a71b4..5197872a2 100644 --- a/Engine/source/T3D/fx/particleEmitterNode.cpp +++ b/Engine/source/T3D/fx/particleEmitterNode.cpp @@ -254,7 +254,7 @@ bool ParticleEmitterNode::onNewDataBlock( GameBaseData *dptr, bool reload ) return false; // Todo: Uncomment if this is a "leaf" class - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/precipitation.cpp b/Engine/source/T3D/fx/precipitation.cpp index 805e87e43..b56893ea1 100644 --- a/Engine/source/T3D/fx/precipitation.cpp +++ b/Engine/source/T3D/fx/precipitation.cpp @@ -614,7 +614,7 @@ bool Precipitation::onNewDataBlock( GameBaseData *dptr, bool reload ) initMaterials(); } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/ribbonNode.cpp b/Engine/source/T3D/fx/ribbonNode.cpp index adc846017..fb8af2db6 100644 --- a/Engine/source/T3D/fx/ribbonNode.cpp +++ b/Engine/source/T3D/fx/ribbonNode.cpp @@ -159,7 +159,7 @@ bool RibbonNode::onNewDataBlock( GameBaseData *dptr, bool reload ) return false; // Todo: Uncomment if this is a "leaf" class - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/fx/splash.cpp b/Engine/source/T3D/fx/splash.cpp index 68bbee0c3..77edaae76 100644 --- a/Engine/source/T3D/fx/splash.cpp +++ b/Engine/source/T3D/fx/splash.cpp @@ -451,7 +451,7 @@ bool Splash::onNewDataBlock( GameBaseData *dptr, bool reload ) if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/gameBase/gameBase.cpp b/Engine/source/T3D/gameBase/gameBase.cpp index 8775f94b3..f16dafc06 100644 --- a/Engine/source/T3D/gameBase/gameBase.cpp +++ b/Engine/source/T3D/gameBase/gameBase.cpp @@ -95,7 +95,7 @@ IMPLEMENT_CALLBACK( GameBaseData, onAdd, void, ( GameBase* obj ), ( obj ), "}\n\n" "@endtsexample\n" ); -IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj ), ( obj ), +IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj, bool reload), ( obj, reload), "@brief Called when the object has a new datablock assigned.\n\n" "@param obj the GameBase object\n\n" "@see onAdd for an example\n" ); @@ -512,12 +512,12 @@ void GameBase::scriptOnAdd() mDataBlock->onAdd_callback( this ); } -void GameBase::scriptOnNewDataBlock() +void GameBase::scriptOnNewDataBlock(bool reload) { // Script onNewDataBlock() must be called by the leaf class // after everything is loaded. if (mDataBlock && !isGhost()) - mDataBlock->onNewDataBlock_callback( this ); + mDataBlock->onNewDataBlock_callback( this, reload); } void GameBase::scriptOnRemove() diff --git a/Engine/source/T3D/gameBase/gameBase.h b/Engine/source/T3D/gameBase/gameBase.h index 7c175f71e..c18b30196 100644 --- a/Engine/source/T3D/gameBase/gameBase.h +++ b/Engine/source/T3D/gameBase/gameBase.h @@ -115,7 +115,7 @@ public: /// @{ DECLARE_CALLBACK( void, onAdd, ( GameBase* obj ) ); DECLARE_CALLBACK( void, onRemove, ( GameBase* obj ) ); - DECLARE_CALLBACK( void, onNewDataBlock, ( GameBase* obj ) ); + DECLARE_CALLBACK( void, onNewDataBlock, ( GameBase* obj, bool reload) ); DECLARE_CALLBACK( void, onMount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) ); DECLARE_CALLBACK( void, onUnmount, ( SceneObject* obj, SceneObject* mountObj, S32 node ) ); /// @} @@ -299,7 +299,7 @@ public: /// Executes the 'onNewDataBlock' script function for this object. /// /// @note This must be called after everything is loaded. - void scriptOnNewDataBlock(); + void scriptOnNewDataBlock(bool reload = false); /// Executes the 'onRemove' script function for this object. /// @note This must be called while the object is still valid diff --git a/Engine/source/T3D/item.cpp b/Engine/source/T3D/item.cpp index 6c5395452..3681774da 100644 --- a/Engine/source/T3D/item.cpp +++ b/Engine/source/T3D/item.cpp @@ -422,7 +422,7 @@ bool Item::onNewDataBlock( GameBaseData *dptr, bool reload ) return false; if (!mSubclassItemHandlesScene) - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); if ( isProperlyAdded() ) _updatePhysics(); diff --git a/Engine/source/T3D/missionMarker.cpp b/Engine/source/T3D/missionMarker.cpp index 5fac601ce..a18523ef7 100644 --- a/Engine/source/T3D/missionMarker.cpp +++ b/Engine/source/T3D/missionMarker.cpp @@ -142,7 +142,7 @@ bool MissionMarker::onNewDataBlock( GameBaseData *dptr, bool reload ) mDataBlock = dynamic_cast( dptr ); if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return(false); - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return(true); } diff --git a/Engine/source/T3D/pathCamera.cpp b/Engine/source/T3D/pathCamera.cpp index 3a4ccd578..bb14db972 100644 --- a/Engine/source/T3D/pathCamera.cpp +++ b/Engine/source/T3D/pathCamera.cpp @@ -181,7 +181,7 @@ bool PathCamera::onNewDataBlock( GameBaseData *dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/pathShape.cpp b/Engine/source/T3D/pathShape.cpp index 846e7bf18..1a903e555 100644 --- a/Engine/source/T3D/pathShape.cpp +++ b/Engine/source/T3D/pathShape.cpp @@ -134,7 +134,7 @@ bool PathShape::onNewDataBlock(GameBaseData* dptr, bool reload) if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 801df58df..bd008d516 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -1923,7 +1923,7 @@ bool Player::onNewDataBlock( GameBaseData *dptr, bool reload ) onScaleChanged(); resetWorldBox(); - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/proximityMine.cpp b/Engine/source/T3D/proximityMine.cpp index 8ce924ea1..fd02fc091 100644 --- a/Engine/source/T3D/proximityMine.cpp +++ b/Engine/source/T3D/proximityMine.cpp @@ -350,7 +350,7 @@ bool ProximityMine::onNewDataBlock( GameBaseData* dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/rigidShape.cpp b/Engine/source/T3D/rigidShape.cpp index e216e7473..8a453b065 100644 --- a/Engine/source/T3D/rigidShape.cpp +++ b/Engine/source/T3D/rigidShape.cpp @@ -906,7 +906,7 @@ bool RigidShape::onNewDataBlock(GameBaseData* dptr, bool reload) else mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents); - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/staticShape.cpp b/Engine/source/T3D/staticShape.cpp index de47194f5..66e9ec454 100644 --- a/Engine/source/T3D/staticShape.cpp +++ b/Engine/source/T3D/staticShape.cpp @@ -219,7 +219,7 @@ bool StaticShape::onNewDataBlock(GameBaseData* dptr, bool reload) if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp index dcccd0778..9df293ba5 100644 --- a/Engine/source/T3D/trigger.cpp +++ b/Engine/source/T3D/trigger.cpp @@ -465,7 +465,7 @@ bool Trigger::onNewDataBlock( GameBaseData *dptr, bool reload ) if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/turret/aiTurretShape.cpp b/Engine/source/T3D/turret/aiTurretShape.cpp index 0e5ce0248..5ad3df020 100644 --- a/Engine/source/T3D/turret/aiTurretShape.cpp +++ b/Engine/source/T3D/turret/aiTurretShape.cpp @@ -545,7 +545,7 @@ bool AITurretShape::onNewDataBlock(GameBaseData* dptr, bool reload) mShapeInstance->setTimeScale(mStateAnimThread,0); } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/turret/turretShape.cpp b/Engine/source/T3D/turret/turretShape.cpp index 8c523588e..b23445294 100644 --- a/Engine/source/T3D/turret/turretShape.cpp +++ b/Engine/source/T3D/turret/turretShape.cpp @@ -440,7 +440,7 @@ bool TurretShape::onNewDataBlock(GameBaseData* dptr, bool reload) if (!mSubclassTurretShapeHandlesScene) { - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); } return true; diff --git a/Engine/source/T3D/vehicles/flyingVehicle.cpp b/Engine/source/T3D/vehicles/flyingVehicle.cpp index efe611b9a..24cf33490 100644 --- a/Engine/source/T3D/vehicles/flyingVehicle.cpp +++ b/Engine/source/T3D/vehicles/flyingVehicle.cpp @@ -407,7 +407,7 @@ bool FlyingVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) mJetThread[i] = 0; } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/vehicles/hoverVehicle.cpp b/Engine/source/T3D/vehicles/hoverVehicle.cpp index 7e7f748a5..482364bd3 100644 --- a/Engine/source/T3D/vehicles/hoverVehicle.cpp +++ b/Engine/source/T3D/vehicles/hoverVehicle.cpp @@ -549,7 +549,7 @@ bool HoverVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) } // Todo: Uncomment if this is a "leaf" class - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/T3D/vehicles/wheeledVehicle.cpp b/Engine/source/T3D/vehicles/wheeledVehicle.cpp index 88d7f67ce..9f3febd13 100644 --- a/Engine/source/T3D/vehicles/wheeledVehicle.cpp +++ b/Engine/source/T3D/vehicles/wheeledVehicle.cpp @@ -711,7 +711,7 @@ bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) mJetSound = SFX->createSource( mDataBlock->getWheeledVehicleSoundsProfile(WheeledVehicleData::JetSound), &getTransform() ); } - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/afx/afxSpellBook.cpp b/Engine/source/afx/afxSpellBook.cpp index 35d207c57..d902d36ad 100644 --- a/Engine/source/afx/afxSpellBook.cpp +++ b/Engine/source/afx/afxSpellBook.cpp @@ -206,7 +206,7 @@ bool afxSpellBook::onNewDataBlock(GameBaseData* dptr, bool reload) if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) return false; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Engine/source/afx/ce/afxParticleEmitter.cpp b/Engine/source/afx/ce/afxParticleEmitter.cpp index cadf0d3ed..a3bb9e448 100644 --- a/Engine/source/afx/ce/afxParticleEmitter.cpp +++ b/Engine/source/afx/ce/afxParticleEmitter.cpp @@ -1064,7 +1064,7 @@ bool afxParticleEmitter::onNewDataBlock(GameBaseData* dptr, bool reload) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } @@ -1108,7 +1108,7 @@ bool afxParticleEmitterVector::onNewDataBlock(GameBaseData* dptr, bool reload) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } @@ -1177,7 +1177,7 @@ bool afxParticleEmitterCone::onNewDataBlock(GameBaseData* dptr, bool reload) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } @@ -1294,7 +1294,7 @@ bool afxParticleEmitterPath::onNewDataBlock(GameBaseData* dptr, bool reload) if (mDataBlock->isTempClone()) return true; - scriptOnNewDataBlock(); + scriptOnNewDataBlock(reload); return true; } diff --git a/Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript b/Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript index 64b75e331..51a4566fa 100644 --- a/Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript +++ b/Templates/BaseGame/game/core/utility/scripts/gameObjectManagement.tscript @@ -78,17 +78,15 @@ function spawnGameObject(%name, %addToScene) return 0; } -function GameBaseData::onNewDataBlock(%this, %obj) +function GameBaseData::onNewDataBlock(%this, %obj, %reload) { - if (%obj.firstDataCheck) + if (%reload) { if(%this.isMethod("onRemove")) %this.onRemove(%obj); if(%this.isMethod("onAdd")) %this.onAdd(%obj); } - else - %obj.firstDataCheck = true; } function saveGameObject(%name, %tamlPath, %scriptPath) From 7ed3f11c8ee8c57be0d9b7df44b1f96297bb5ef8 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 00:26:45 -0500 Subject: [PATCH 67/74] might as well go ahead and allow substitution statement support --- Engine/source/T3D/AI/AIController.cpp | 75 +++++++++++++++++++++++++++ Engine/source/T3D/AI/AIController.h | 6 ++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index e4fa0882c..18b5c2d4e 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -507,6 +507,30 @@ AIControllerData::AIControllerData() resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); } +AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clone) : SimDataBlock(other, temp_clone) +{ + mMoveTolerance = other.mMoveTolerance; + mFollowTolerance = other.mFollowTolerance; + mAttackRadius = other.mAttackRadius; + mMoveStuckTolerance = other.mMoveStuckTolerance; + mMoveStuckTestDelay = other.mMoveStuckTestDelay; + mLinkTypes = other.mLinkTypes; + mNavSize = other.mNavSize; + mHeightTolerance = other.mHeightTolerance; + + mFlocking.mChance = other.mFlocking.mChance; + mFlocking.mMin = other.mFlocking.mMin; + mFlocking.mMax = other.mFlocking.mMax; + mFlocking.mSideStep = other.mFlocking.mSideStep; + + resolveYawPtr.bind(this, &AIControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); + resolveRollPtr.bind(this, &AIControllerData::resolveRoll); + resolveSpeedPtr.bind(this, &AIControllerData::resolveSpeed); + resolveTriggerStatePtr.bind(this, &AIControllerData::resolveTriggerState); + resolveStuckPtr.bind(this, &AIControllerData::resolveStuck); +} + void AIControllerData::initPersistFields() { docsURL; @@ -583,6 +607,48 @@ void AIControllerData::initPersistFields() Parent::initPersistFields(); } +void AIControllerData::packData(BitStream* stream) +{ + Parent::packData(stream); + stream->write(mMoveTolerance); + stream->write(mFollowTolerance); + stream->write(mAttackRadius); + stream->write(mMoveStuckTolerance); + stream->write(mMoveStuckTestDelay); + //enums + stream->write(mLinkTypes.getFlags()); + stream->write((U32)mNavSize); + // end enums + stream->write(mHeightTolerance); + stream->write(mFlocking.mChance); + stream->write(mFlocking.mMin); + stream->write(mFlocking.mMax); + stream->write(mFlocking.mSideStep); +}; + +void AIControllerData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&mMoveTolerance); + stream->read(&mFollowTolerance); + stream->read(&mAttackRadius); + stream->read(&mMoveStuckTolerance); + stream->read(&mMoveStuckTestDelay); + //enums + U16 linkFlags; + stream->read(&linkFlags); + mLinkTypes = LinkData(linkFlags); + U32 navSize; + stream->read(&navSize); + mNavSize = (AINavigation::NavSize)(navSize); + // end enums + stream->read(&mHeightTolerance); + stream->read(&(mFlocking.mChance)); + stream->read(&(mFlocking.mMin)); + stream->read(&(mFlocking.mMax)); + stream->read(&(mFlocking.mSideStep)); +}; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- IMPLEMENT_CO_DATABLOCK_V1(AIPlayerControllerData); @@ -729,6 +795,15 @@ void AIFlyingVehicleControllerData::initPersistFields() Parent::initPersistFields(); } +AIFlyingVehicleControllerData::AIFlyingVehicleControllerData(const AIFlyingVehicleControllerData& other, bool temp_clone) : AIControllerData(other, temp_clone) +{ + mFlightCeiling = other.mFlightCeiling; + mFlightFloor = other.mFlightFloor; + resolveYawPtr.bind(this, &AIFlyingVehicleControllerData::resolveYaw); + resolvePitchPtr.bind(this, &AIFlyingVehicleControllerData::resolvePitch); + resolveSpeedPtr.bind(this, &AIFlyingVehicleControllerData::resolveSpeed); +} + void AIFlyingVehicleControllerData::resolveYaw(AIController* obj, Point3F location, Move* movePtr) { if (obj->mMovement.mMoveState < AIController::ModeSlowing) return; diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 92aa481ea..14bddeccd 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -146,9 +146,10 @@ class AIControllerData : public SimDataBlock { public: AIControllerData(); + AIControllerData(const AIControllerData&, bool = false); ~AIControllerData() {}; - void packData(BitStream* stream) override { Parent::packData(stream); }; - void unpackData(BitStream* stream) override { Parent::unpackData(stream); }; + void packData(BitStream* stream) override; + void unpackData(BitStream* stream) override; static void initPersistFields(); DECLARE_CONOBJECT(AIControllerData); @@ -234,6 +235,7 @@ public: mFlightCeiling = 200.0f; mFlightFloor = 1.0; } + AIFlyingVehicleControllerData(const AIFlyingVehicleControllerData&, bool = false); static void initPersistFields(); void resolveYaw(AIController* obj, Point3F location, Move* movePtr); void resolveSpeed(AIController* obj, Point3F location, Move* movePtr); From c6f19e5cae906dd42cdc69ad565760e8d8233516 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 12:49:48 -0500 Subject: [PATCH 68/74] fix nav editor stop comand --- Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui index 92505f4e7..18646fe1a 100644 --- a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui @@ -485,7 +485,7 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) { VertSizing = "bottom"; Extent = "90 18"; text = "Stop"; - command = "NavEditorGui.getPlayer().stop();"; + command = "NavEditorGui.stop();"; }; }; }; From f2781501857b7613f9166e665ff9b6e7086e7148 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 14:31:27 -0500 Subject: [PATCH 69/74] TORQUE_NAVIGATION_ENABLED filtering --- Engine/source/T3D/AI/AIController.cpp | 92 ++++--- Engine/source/T3D/AI/AIController.h | 8 +- Engine/source/T3D/AI/AINavigation.cpp | 332 +++++++++++++------------- Engine/source/T3D/AI/AINavigation.h | 19 +- 4 files changed, 249 insertions(+), 202 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 18b5c2d4e..acd7063f6 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -110,8 +110,6 @@ void AIController::setAim(SimObjectPtr objIn, F32 rad, Point3F offs mAimTarget->mAimOffset = offset; } -#ifdef TORQUE_NAVIGATION_ENABLED - bool AIController::getAIMove(Move* movePtr) { *movePtr = NullMove; @@ -145,9 +143,9 @@ bool AIController::getAIMove(Move* movePtr) } } -#ifdef TORQUE_NAVIGATION_ENABLED if (sbo->getDamageState() == ShapeBase::Enabled && getGoal()) { +#ifdef TORQUE_NAVIGATION_ENABLED if (mMovement.mMoveState != ModeStop) getNav()->updateNavMesh(); @@ -202,8 +200,39 @@ bool AIController::getAIMove(Move* movePtr) else getGoal()->mInFiringRange = false; } } - } +#else + if (getGoal()->getDist() > mControllerData->mMoveTolerance) + { + if (getGoal()->mPosSet) + getNav()->setPathDestination(getGoal()->getPosition(true)); + + getGoal()->mInRange = false; + } + if (getGoal()->getDist() < mControllerData->mMoveTolerance) + { + mMovement.mMoveState = ModeStop; + + if (!getGoal()->mInRange) + { + getGoal()->mInRange = true; + throwCallback("onTargetInRange"); + } + else getGoal()->mInRange = false; + } + else + { + if (getGoal()->getDist() < mControllerData->mAttackRadius) + { + if (!getGoal()->mInFiringRange) + { + getGoal()->mInFiringRange = true; + throwCallback("onTargetInFiringRange"); + } + } + else getGoal()->mInFiringRange = false; + } #endif // TORQUE_NAVIGATION_ENABLED + } // Orient towards the aim point, aim object, or towards // our destination. if (getAim() || mMovement.mMoveState != ModeStop) @@ -486,19 +515,20 @@ void AIControllerData::resolveStuck(AIController* obj) AIControllerData::AIControllerData() { mMoveTolerance = 0.25f; - mFollowTolerance = 1.0f; mAttackRadius = 2.0f; mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30; - mLinkTypes = LinkData(AllFlags); - mNavSize = AINavigation::Regular; mHeightTolerance = 0.001f; +#ifdef TORQUE_NAVIGATION_ENABLED + mFollowTolerance = 1.0f; + mLinkTypes = LinkData(AllFlags); + mNavSize = AINavigation::Regular; mFlocking.mChance = 90; mFlocking.mMin = 1.0f; mFlocking.mMax = 3.0f; mFlocking.mSideStep = 0.01f; - +#endif resolveYawPtr.bind(this, &AIControllerData::resolveYaw); resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); resolveRollPtr.bind(this, &AIControllerData::resolveRoll); @@ -510,18 +540,20 @@ AIControllerData::AIControllerData() AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clone) : SimDataBlock(other, temp_clone) { mMoveTolerance = other.mMoveTolerance; - mFollowTolerance = other.mFollowTolerance; mAttackRadius = other.mAttackRadius; mMoveStuckTolerance = other.mMoveStuckTolerance; mMoveStuckTestDelay = other.mMoveStuckTestDelay; - mLinkTypes = other.mLinkTypes; - mNavSize = other.mNavSize; mHeightTolerance = other.mHeightTolerance; +#ifdef TORQUE_NAVIGATION_ENABLED + mFollowTolerance = other.mFollowTolerance; + mLinkTypes = other.mLinkTypes; + mNavSize = other.mNavSize; mFlocking.mChance = other.mFlocking.mChance; mFlocking.mMin = other.mFlocking.mMin; mFlocking.mMax = other.mFlocking.mMax; mFlocking.mSideStep = other.mFlocking.mSideStep; +#endif resolveYawPtr.bind(this, &AIControllerData::resolveYaw); resolvePitchPtr.bind(this, &AIControllerData::resolvePitch); @@ -543,12 +575,8 @@ void AIControllerData::initPersistFields() "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); - addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, - "@brief Distance from destination before stopping.\n\n" - "When the AIController controlled object is moving to a given destination it will move to within " - "this distance of the destination and then stop. By providing this tolerance " - "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " - "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); + addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance considered in firing range for callback purposes."); addFieldV("moveStuckTolerance", TypeRangedF32, Offset(mMoveStuckTolerance, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance tolerance on stuck check.\n\n" @@ -569,10 +597,16 @@ void AIControllerData::initPersistFields() "before the AIController controlled object starts to check if it is stuck. This delay allows the AIController controlled object " "to accelerate to full speed without its initial slow start being considered as stuck.\n" "@note Set to zero to have the stuck test start immediately.\n"); + endGroup("AI"); - addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, - "@brief Distance considered in firing range for callback purposes."); - +#ifdef TORQUE_NAVIGATION_ENABLED + addGroup("Pathfinding"); + addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before stopping.\n\n" + "When the AIController controlled object is moving to a given destination it will move to within " + "this distance of the destination and then stop. By providing this tolerance " + "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " + "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); addFieldV("FlockChance", TypeRangedS32, Offset(mFlocking.mChance, AIControllerData), &CommonValidators::S32Percent, "@brief chance of flocking."); addFieldV("FlockMin", TypeRangedF32, Offset(mFlocking.mMin, AIControllerData), &CommonValidators::PositiveFloat, @@ -581,10 +615,6 @@ void AIControllerData::initPersistFields() "@brief max flocking clustering distance."); addFieldV("FlockSideStep", TypeRangedF32, Offset(mFlocking.mSideStep, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance from destination before we stop moving out of the way."); - endGroup("AI"); - -#ifdef TORQUE_NAVIGATION_ENABLED - addGroup("Pathfinding"); addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIControllerData), "Allow the character to walk on dry land."); @@ -611,19 +641,21 @@ void AIControllerData::packData(BitStream* stream) { Parent::packData(stream); stream->write(mMoveTolerance); - stream->write(mFollowTolerance); stream->write(mAttackRadius); stream->write(mMoveStuckTolerance); stream->write(mMoveStuckTestDelay); + stream->write(mHeightTolerance); +#ifdef TORQUE_NAVIGATION_ENABLED + stream->write(mFollowTolerance); //enums stream->write(mLinkTypes.getFlags()); stream->write((U32)mNavSize); // end enums - stream->write(mHeightTolerance); stream->write(mFlocking.mChance); stream->write(mFlocking.mMin); stream->write(mFlocking.mMax); stream->write(mFlocking.mSideStep); +#endif // TORQUE_NAVIGATION_ENABLED }; void AIControllerData::unpackData(BitStream* stream) @@ -631,10 +663,13 @@ void AIControllerData::unpackData(BitStream* stream) Parent::unpackData(stream); stream->read(&mMoveTolerance); - stream->read(&mFollowTolerance); stream->read(&mAttackRadius); stream->read(&mMoveStuckTolerance); stream->read(&mMoveStuckTestDelay); + stream->read(&mHeightTolerance); + +#ifdef TORQUE_NAVIGATION_ENABLED + stream->read(&mFollowTolerance); //enums U16 linkFlags; stream->read(&linkFlags); @@ -643,11 +678,11 @@ void AIControllerData::unpackData(BitStream* stream) stream->read(&navSize); mNavSize = (AINavigation::NavSize)(navSize); // end enums - stream->read(&mHeightTolerance); stream->read(&(mFlocking.mChance)); stream->read(&(mFlocking.mMin)); stream->read(&(mFlocking.mMax)); stream->read(&(mFlocking.mSideStep)); +#endif // TORQUE_NAVIGATION_ENABLED }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -875,4 +910,3 @@ void AIFlyingVehicleControllerData::resolveSpeed(AIController* obj, Point3F loca movePtr->x = 0; movePtr->y = obj->mMovement.mMoveSpeed; } -#endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 14bddeccd..3180c4213 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -21,7 +21,6 @@ //----------------------------------------------------------------------------- #ifndef _AICONTROLLER_H_ #define _AICONTROLLER_H_ -#ifdef TORQUE_NAVIGATION_ENABLED #include "navigation/coverPoint.h" #include "AIInfo.h" #include "AIGoal.h" @@ -154,11 +153,12 @@ public: DECLARE_CONOBJECT(AIControllerData); F32 mMoveTolerance; // Distance from destination point before we stop - F32 mFollowTolerance; // Distance from destination object before we stop F32 mAttackRadius; // Distance to trigger weaponry calcs - F32 mMoveStuckTolerance; // Distance tolerance on stuck check S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck + F32 mMoveStuckTolerance; // Distance tolerance on stuck check F32 mHeightTolerance; // how high above the navmesh are we before we stop trying to repath +#ifdef TORQUE_NAVIGATION_ENABLED + F32 mFollowTolerance; // Distance from destination object before we stop struct Flocking { U32 mChance; // chance of flocking F32 mMin; // min flocking separation distance @@ -169,6 +169,7 @@ public: /// Types of link we can use. LinkData mLinkTypes; AINavigation::NavSize mNavSize; +#endif Delegate resolveYawPtr; void resolveYaw(AIController* obj, Point3F location, Move* movePtr); @@ -243,5 +244,4 @@ public: DECLARE_CONOBJECT(AIFlyingVehicleControllerData); }; -#endif // TORQUE_NAVIGATION_ENABLED #endif //_AICONTROLLER_H_ diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index f5d3fd2cd..ceb9d668e 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -28,8 +28,10 @@ static U32 sAILoSMask = TerrainObjectType | StaticShapeObjectType | StaticObject AINavigation::AINavigation(AIController* controller) { mControllerRef = controller; +#ifdef TORQUE_NAVIGATION_ENABLED mJump = None; mNavSize = Regular; +#endif } AINavigation::~AINavigation() @@ -40,6 +42,175 @@ AINavigation::~AINavigation() #endif } +void AINavigation::setMoveDestination(const Point3F& location, bool slowdown) +{ + mMoveDestination = location; + getCtrl()->mMovement.mMoveState = AIController::ModeMove; + getCtrl()->mMovement.mMoveSlowdown = slowdown; + getCtrl()->mMovement.mMoveStuckTestCountdown = getCtrl()->mControllerData->mMoveStuckTestDelay; +} + +bool AINavigation::setPathDestination(const Point3F& pos, bool replace) +{ +#ifdef TORQUE_NAVIGATION_ENABLED + if (replace) + getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); + + if (!mNavMesh) + updateNavMesh(); + + // If we can't find a mesh, just move regularly. + if (!mNavMesh) + { + //setMoveDestination(pos); + getCtrl()->throwCallback("onPathFailed"); + return false; + } + + // Create a new path. + NavPath* path = new NavPath(); + + path->mMesh = mNavMesh; + path->mFrom = getCtrl()->getAIInfo()->getPosition(true); + path->mTo = getCtrl()->getGoal()->getPosition(true); + path->mFromSet = path->mToSet = true; + path->mAlwaysRender = true; + path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; + path->mXray = true; + // Paths plan automatically upon being registered. + if (!path->registerObject()) + { + delete path; + return false; + } + + if (path->success()) + { + // Clear any current path we might have. + clearPath(); + getCtrl()->clearCover(); + // Store new path. + mPathData.path = path; + mPathData.owned = true; + // Skip node 0, which we are currently standing on. + moveToNode(1); + getCtrl()->throwCallback("onPathSuccess"); + return true; + } + else + { + // Just move normally if we can't path. + //setMoveDestination(pos, true); + //return; + getCtrl()->throwCallback("onPathFailed"); + path->deleteObject(); + return false; + } +#else + setMoveDestination(pos, false); + return true; +#endif +} + +Point3F AINavigation::getPathDestination() const +{ +#ifdef TORQUE_NAVIGATION_ENABLED + if (!mPathData.path.isNull()) + return mPathData.path->mTo; + return Point3F(0, 0, 0); +#else + return getMoveDestination(); +#endif +} +void AINavigation::onReachDestination() +{ + +#ifdef TORQUE_NAVIGATION_ENABLED + if (!getPath().isNull()) + { + if (mPathData.index == getPath()->size() - 1) + { + // Handle looping paths. + if (getPath()->mIsLooping) + moveToNode(0); + // Otherwise end path. + else + { + clearPath(); + getCtrl()->throwCallback("onReachDestination"); + } + } + else + { + moveToNode(mPathData.index + 1); + // Throw callback every time if we're on a looping path. + //if(mPathData.path->mIsLooping) + //throwCallback("onReachDestination"); + } + } + else +#endif + { + getCtrl()->throwCallback("onReachDestination"); + getCtrl()->mMovement.mMoveState = AIController::ModeStop; + } +} + +DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), + "@brief Tells the AI to move to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@param slowDown A boolean value. If set to true, the bot will slow down " + "when it gets within 5-meters of its move destination. If false, the bot " + "will stop abruptly when it reaches the move destination. By default, this is true.\n\n" + + "@note Upon reaching a move destination, the bot will clear its move destination and " + "calls to getMoveDestination will return \"0 0 0\"." + + "@see getMoveDestination()\n") +{ + object->getNav()->setMoveDestination(goal, slowDown); +} + + +DefineEngineMethod(AIController, getMoveDestination, Point3F, (), , + "@brief Get the AIPlayer's current destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current move destination. If no move destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setMoveDestination()\n") +{ + return object->getNav()->getMoveDestination(); +} + +DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), , + "@brief Tells the AI to find a path to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@return True if a path was found.\n\n" + + "@see getPathDestination()\n" + "@see setMoveDestination()\n") +{ + return object->getNav()->setPathDestination(goal, true); +} + + +DefineEngineMethod(AIController, getPathDestination, Point3F, (), , + "@brief Get the AIPlayer's current pathfinding destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current path destination. If no path destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setPathDestination()\n") +{ + return object->getNav()->getPathDestination(); +} + +#ifdef TORQUE_NAVIGATION_ENABLED NavMesh* AINavigation::findNavMesh() const { GameBase* gbo = dynamic_cast(mControllerRef->getAIInfo()->mObj.getPointer()); @@ -144,113 +315,6 @@ void AINavigation::repath() moveToNode(1); } -Point3F AINavigation::getPathDestination() const -{ - if (!mPathData.path.isNull()) - return mPathData.path->mTo; - return Point3F(0, 0, 0); - -} - -void AINavigation::setMoveDestination(const Point3F& location, bool slowdown) -{ - mMoveDestination = location; - getCtrl()->mMovement.mMoveState = AIController::ModeMove; - getCtrl()->mMovement.mMoveSlowdown = slowdown; - getCtrl()->mMovement.mMoveStuckTestCountdown = getCtrl()->mControllerData->mMoveStuckTestDelay; -} - -void AINavigation::onReachDestination() -{ - -#ifdef TORQUE_NAVIGATION_ENABLED - if (!getPath().isNull()) - { - if (mPathData.index == getPath()->size() - 1) - { - // Handle looping paths. - if (getPath()->mIsLooping) - moveToNode(0); - // Otherwise end path. - else - { - clearPath(); - getCtrl()->throwCallback("onReachDestination"); - } - } - else - { - moveToNode(mPathData.index + 1); - // Throw callback every time if we're on a looping path. - //if(mPathData.path->mIsLooping) - //throwCallback("onReachDestination"); - } - } - else -#endif - { - getCtrl()->throwCallback("onReachDestination"); - getCtrl()->mMovement.mMoveState = AIController::ModeStop; - } -} - -bool AINavigation::setPathDestination(const Point3F& pos, bool replace) -{ - if (replace) - getCtrl()->setGoal(pos, getCtrl()->mControllerData->mMoveTolerance); - - if (!mNavMesh) - updateNavMesh(); - - // If we can't find a mesh, just move regularly. - if (!mNavMesh) - { - //setMoveDestination(pos); - getCtrl()->throwCallback("onPathFailed"); - return false; - } - - // Create a new path. - NavPath* path = new NavPath(); - - path->mMesh = mNavMesh; - path->mFrom = getCtrl()->getAIInfo()->getPosition(true); - path->mTo = getCtrl()->getGoal()->getPosition(true); - path->mFromSet = path->mToSet = true; - path->mAlwaysRender = true; - path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes; - path->mXray = true; - // Paths plan automatically upon being registered. - if (!path->registerObject()) - { - delete path; - return false; - } - - if (path->success()) - { - // Clear any current path we might have. - clearPath(); - getCtrl()->clearCover(); - // Store new path. - mPathData.path = path; - mPathData.owned = true; - // Skip node 0, which we are currently standing on. - moveToNode(1); - getCtrl()->throwCallback("onPathSuccess"); - return true; - } - else - { - // Just move normally if we can't path. - //setMoveDestination(pos, true); - //return; - getCtrl()->throwCallback("onPathFailed"); - path->deleteObject(); - return false; - } -} - void AINavigation::followObject() { if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) @@ -422,59 +486,6 @@ bool AINavigation::flock() return flocking; } -DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), - "@brief Tells the AI to move to the location provided\n\n" - - "@param goal Coordinates in world space representing location to move to.\n" - "@param slowDown A boolean value. If set to true, the bot will slow down " - "when it gets within 5-meters of its move destination. If false, the bot " - "will stop abruptly when it reaches the move destination. By default, this is true.\n\n" - - "@note Upon reaching a move destination, the bot will clear its move destination and " - "calls to getMoveDestination will return \"0 0 0\"." - - "@see getMoveDestination()\n") -{ - object->getNav()->setMoveDestination(goal, slowDown); -} - - -DefineEngineMethod(AIController, getMoveDestination, Point3F, (), , - "@brief Get the AIPlayer's current destination.\n\n" - - "@return Returns a point containing the \"x y z\" position " - "of the AIPlayer's current move destination. If no move destination " - "has yet been set, this returns \"0 0 0\"." - - "@see setMoveDestination()\n") -{ - return object->getNav()->getMoveDestination(); -} - -DefineEngineMethod(AIController, setPathDestination, bool, (Point3F goal), , - "@brief Tells the AI to find a path to the location provided\n\n" - - "@param goal Coordinates in world space representing location to move to.\n" - "@return True if a path was found.\n\n" - - "@see getPathDestination()\n" - "@see setMoveDestination()\n") -{ - return object->getNav()->setPathDestination(goal,true); -} - - -DefineEngineMethod(AIController, getPathDestination, Point3F, (), , - "@brief Get the AIPlayer's current pathfinding destination.\n\n" - - "@return Returns a point containing the \"x y z\" position " - "of the AIPlayer's current path destination. If no path destination " - "has yet been set, this returns \"0 0 0\"." - - "@see setPathDestination()\n") -{ - return object->getNav()->getPathDestination(); -} DefineEngineMethod(AIController, followNavPath, void, (SimObjectId obj), , "@brief Tell the AIPlayer to follow a path.\n\n" @@ -555,3 +566,4 @@ DefineEngineMethod(AIController, getNavSize, const char*, (), , } return ""; } +#endif diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index 39b1276a4..e93871691 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -36,6 +36,15 @@ struct AINavigation AINavigation() = delete; AINavigation(AIController* controller); ~AINavigation(); + Point3F mMoveDestination; + void setMoveDestination(const Point3F& location, bool slowdown); + Point3F getMoveDestination() const { return mMoveDestination; }; + bool setPathDestination(const Point3F& pos, bool replace = false); + Point3F getPathDestination() const; + + void onReachDestination(); + +#ifdef TORQUE_NAVIGATION_ENABLED /// Stores information about a path. struct PathData { /// Pointer to path object. @@ -67,12 +76,6 @@ struct AINavigation void setNavSize(NavSize size) { mNavSize = size; updateNavMesh(); } NavSize getNavSize() const { return mNavSize; } - Point3F mMoveDestination; - void setMoveDestination(const Point3F& location, bool slowdown); - Point3F getMoveDestination() { return mMoveDestination; }; - - void onReachDestination(); - /// NavMesh we pathfind on. SimObjectPtr mNavMesh; NavMesh* findNavMesh() const; @@ -83,8 +86,6 @@ struct AINavigation /// Clear out the current path. void clearPath(); - bool setPathDestination(const Point3F& pos, bool replace = false); - Point3F getPathDestination() const; void repath(); /// Get the current path we're following. @@ -96,8 +97,8 @@ struct AINavigation void clearFollow(); /// Move to the specified node in the current path. void moveToNode(S32 node); - bool flock(); +#endif }; #endif From 9e2666ed09bb544298fa512379da13dbac112e46 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 14:35:26 -0500 Subject: [PATCH 70/74] nav editor compliance --- Engine/source/navigation/guiNavEditorCtrl.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index 90a6e7f78..eae204447 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -227,6 +227,7 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) missionCleanup->addObject(obj); } mPlayer = obj; +#ifdef TORQUE_NAVIGATION_ENABLED AIPlayer* asAIPlayer = dynamic_cast(obj); if (asAIPlayer) //try direct { @@ -242,9 +243,12 @@ void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos) } else { +#endif Con::executef(this, "onPlayerSelected"); +#ifdef TORQUE_NAVIGATION_ENABLED } } +#endif } } @@ -406,6 +410,7 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) if(ri.object) { mPlayer = ri.object; +#ifdef TORQUE_NAVIGATION_ENABLED AIPlayer* asAIPlayer = dynamic_cast(mPlayer.getPointer()); if (asAIPlayer) //try direct { @@ -421,17 +426,24 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) } else { +#endif Con::executef(this, "onPlayerSelected"); } +#ifdef TORQUE_NAVIGATION_ENABLED } } +#endif } else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri)) { AIPlayer* asAIPlayer = dynamic_cast(mPlayer.getPointer()); if (asAIPlayer) //try direct { +#ifdef TORQUE_NAVIGATION_ENABLED asAIPlayer->setPathDestination(ri.point); +#else + asAIPlayer->setMoveDestination(ri.point,false); +#endif } else { From a05ff4f351da7711b99491b4e0d7ec9eb28fae0d Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 14:38:51 -0500 Subject: [PATCH 71/74] kill dupe controlmap tracking var define --- Engine/source/T3D/vehicles/vehicle.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 9ea2c9289..ba7bf8e68 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -71,7 +71,6 @@ struct VehicleData : public RigidShapeData F32 damageLevelTolerance[ VC_NUM_DAMAGE_LEVELS ]; F32 numDmgEmitterAreas; - StringTableEntry mControlMap; bool enablePhysicsRep; StringTableEntry mControlMap; From e489e0cd18221f7d9d8aeac805b4ecb56ba83132 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Mon, 28 Apr 2025 16:01:24 -0500 Subject: [PATCH 72/74] go ahead and allow follow without needing a navmesh --- Engine/source/T3D/AI/AIController.cpp | 30 ++++++----- Engine/source/T3D/AI/AIController.h | 2 +- Engine/source/T3D/AI/AINavigation.cpp | 77 ++++++++++++++------------- Engine/source/T3D/AI/AINavigation.h | 7 +-- 4 files changed, 63 insertions(+), 53 deletions(-) diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index acd7063f6..e54b173bb 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -201,14 +201,16 @@ bool AIController::getAIMove(Move* movePtr) } } #else - if (getGoal()->getDist() > mControllerData->mMoveTolerance) + if (getGoal()->getDist() > mControllerData->mFollowTolerance) { - if (getGoal()->mPosSet) + if (getGoal()->mObj.isValid()) + getNav()->followObject(getGoal()->mObj, mControllerData->mFollowTolerance); + else if (getGoal()->mPosSet) getNav()->setPathDestination(getGoal()->getPosition(true)); getGoal()->mInRange = false; } - if (getGoal()->getDist() < mControllerData->mMoveTolerance) + if (getGoal()->getDist() < mControllerData->mFollowTolerance) { mMovement.mMoveState = ModeStop; @@ -519,9 +521,9 @@ AIControllerData::AIControllerData() mMoveStuckTolerance = 0.01f; mMoveStuckTestDelay = 30; mHeightTolerance = 0.001f; + mFollowTolerance = 1.0f; #ifdef TORQUE_NAVIGATION_ENABLED - mFollowTolerance = 1.0f; mLinkTypes = LinkData(AllFlags); mNavSize = AINavigation::Regular; mFlocking.mChance = 90; @@ -544,9 +546,9 @@ AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clon mMoveStuckTolerance = other.mMoveStuckTolerance; mMoveStuckTestDelay = other.mMoveStuckTestDelay; mHeightTolerance = other.mHeightTolerance; + mFollowTolerance = other.mFollowTolerance; #ifdef TORQUE_NAVIGATION_ENABLED - mFollowTolerance = other.mFollowTolerance; mLinkTypes = other.mLinkTypes; mNavSize = other.mNavSize; mFlocking.mChance = other.mFlocking.mChance; @@ -575,6 +577,13 @@ void AIControllerData::initPersistFields() "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); + addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, + "@brief Distance from destination before stopping.\n\n" + "When the AIController controlled object is moving to a given destination it will move to within " + "this distance of the destination and then stop. By providing this tolerance " + "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " + "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); + addFieldV("AttackRadius", TypeRangedF32, Offset(mAttackRadius, AIControllerData), &CommonValidators::PositiveFloat, "@brief Distance considered in firing range for callback purposes."); @@ -601,12 +610,6 @@ void AIControllerData::initPersistFields() #ifdef TORQUE_NAVIGATION_ENABLED addGroup("Pathfinding"); - addFieldV("followTolerance", TypeRangedF32, Offset(mFollowTolerance, AIControllerData), &CommonValidators::PositiveFloat, - "@brief Distance from destination before stopping.\n\n" - "When the AIController controlled object is moving to a given destination it will move to within " - "this distance of the destination and then stop. By providing this tolerance " - "it helps the AIController controlled object from never reaching its destination due to minor obstacles, " - "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); addFieldV("FlockChance", TypeRangedS32, Offset(mFlocking.mChance, AIControllerData), &CommonValidators::S32Percent, "@brief chance of flocking."); addFieldV("FlockMin", TypeRangedF32, Offset(mFlocking.mMin, AIControllerData), &CommonValidators::PositiveFloat, @@ -645,8 +648,9 @@ void AIControllerData::packData(BitStream* stream) stream->write(mMoveStuckTolerance); stream->write(mMoveStuckTestDelay); stream->write(mHeightTolerance); -#ifdef TORQUE_NAVIGATION_ENABLED stream->write(mFollowTolerance); + +#ifdef TORQUE_NAVIGATION_ENABLED //enums stream->write(mLinkTypes.getFlags()); stream->write((U32)mNavSize); @@ -667,9 +671,9 @@ void AIControllerData::unpackData(BitStream* stream) stream->read(&mMoveStuckTolerance); stream->read(&mMoveStuckTestDelay); stream->read(&mHeightTolerance); + stream->read(&mFollowTolerance); #ifdef TORQUE_NAVIGATION_ENABLED - stream->read(&mFollowTolerance); //enums U16 linkFlags; stream->read(&linkFlags); diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 3180c4213..18d95e210 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -153,12 +153,12 @@ public: DECLARE_CONOBJECT(AIControllerData); F32 mMoveTolerance; // Distance from destination point before we stop + F32 mFollowTolerance; // Distance from destination object before we stop F32 mAttackRadius; // Distance to trigger weaponry calcs S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck F32 mMoveStuckTolerance; // Distance tolerance on stuck check F32 mHeightTolerance; // how high above the navmesh are we before we stop trying to repath #ifdef TORQUE_NAVIGATION_ENABLED - F32 mFollowTolerance; // Distance from destination object before we stop struct Flocking { U32 mChance; // chance of flocking F32 mMin; // min flocking separation distance diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index ceb9d668e..258939f06 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -156,6 +156,30 @@ void AINavigation::onReachDestination() } } +void AINavigation::followObject() +{ + if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) + return; + + if (setPathDestination(getCtrl()->getGoal()->getPosition(true))) + { +#ifdef TORQUE_NAVIGATION_ENABLED + getCtrl()->clearCover(); +#endif + } +} + +void AINavigation::followObject(SceneObject* obj, F32 radius) +{ + getCtrl()->setGoal(obj, radius); + followObject(); +} + +void AINavigation::clearFollow() +{ + getCtrl()->clearGoal(); +} + DefineEngineMethod(AIController, setMoveDestination, void, (Point3F goal, bool slowDown), (true), "@brief Tells the AI to move to the location provided\n\n" @@ -210,6 +234,23 @@ DefineEngineMethod(AIController, getPathDestination, Point3F, (), , return object->getNav()->getPathDestination(); } +DefineEngineMethod(AIController, followObject, void, (SimObjectId obj, F32 radius), , + "@brief Tell the AIPlayer to follow another object.\n\n" + + "@param obj ID of the object to follow.\n" + "@param radius Maximum distance we let the target escape to.") +{ + SceneObject* follow; +#ifdef TORQUE_NAVIGATION_ENABLED + object->getNav()->clearPath(); + object->clearCover(); +#endif + object->getNav()->clearFollow(); + + if (Sim::findObject(obj, follow)) + object->getNav()->followObject(follow, radius); +} + #ifdef TORQUE_NAVIGATION_ENABLED NavMesh* AINavigation::findNavMesh() const { @@ -315,27 +356,6 @@ void AINavigation::repath() moveToNode(1); } -void AINavigation::followObject() -{ - if (getCtrl()->getGoal()->getDist() < getCtrl()->mControllerData->mMoveTolerance) - return; - - if (setPathDestination(getCtrl()->getGoal()->getPosition(true))) - { - getCtrl()->clearCover(); - } -} - -void AINavigation::followObject(SceneObject* obj, F32 radius) -{ - getCtrl()->setGoal(obj, radius); - followObject(); -} - -void AINavigation::clearFollow() -{ - getCtrl()->clearGoal(); -} void AINavigation::followNavPath(NavPath* path) { @@ -497,21 +517,6 @@ DefineEngineMethod(AIController, followNavPath, void, (SimObjectId obj), , object->getNav()->followNavPath(path); } -DefineEngineMethod(AIController, followObject, void, (SimObjectId obj, F32 radius), , - "@brief Tell the AIPlayer to follow another object.\n\n" - - "@param obj ID of the object to follow.\n" - "@param radius Maximum distance we let the target escape to.") -{ - SceneObject* follow; - object->getNav()->clearPath(); - object->clearCover(); - object->getNav()->clearFollow(); - - if (Sim::findObject(obj, follow)) - object->getNav()->followObject(follow, radius); -} - DefineEngineMethod(AIController, repath, void, (), , "@brief Tells the AI to re-plan its path. Does nothing if the character " diff --git a/Engine/source/T3D/AI/AINavigation.h b/Engine/source/T3D/AI/AINavigation.h index e93871691..4d4f1b966 100644 --- a/Engine/source/T3D/AI/AINavigation.h +++ b/Engine/source/T3D/AI/AINavigation.h @@ -44,6 +44,10 @@ struct AINavigation void onReachDestination(); + void followObject(); + void followObject(SceneObject* obj, F32 radius); + void clearFollow(); + #ifdef TORQUE_NAVIGATION_ENABLED /// Stores information about a path. struct PathData { @@ -92,9 +96,6 @@ struct AINavigation SimObjectPtr getPath() { return mPathData.path; }; void followNavPath(NavPath* path); - void followObject(); - void followObject(SceneObject* obj, F32 radius); - void clearFollow(); /// Move to the specified node in the current path. void moveToNode(S32 node); bool flock(); From c04eaa5862d50a3fdbdd173661c5b85f37b60ab0 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 29 Apr 2025 15:45:39 -0500 Subject: [PATCH 73/74] fix PopupMenu::checkItem filter reminder size is the total element count of the vector --- Engine/source/gui/editor/popupMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/gui/editor/popupMenu.cpp b/Engine/source/gui/editor/popupMenu.cpp index 783453f02..1f9de53fb 100644 --- a/Engine/source/gui/editor/popupMenu.cpp +++ b/Engine/source/gui/editor/popupMenu.cpp @@ -230,7 +230,7 @@ void PopupMenu::enableItem(S32 pos, bool enable) void PopupMenu::checkItem(S32 pos, bool checked) { - if (mMenuItems.empty() || mMenuItems.size() < pos || pos < 0) + if (mMenuItems.empty() || mMenuItems.size() <= pos || pos < 0) return; if (checked && mMenuItems[pos].mCheckGroup != -1 && mRadioSelection) From 62aadf911481db3e7049d8391c8927fca998bf36 Mon Sep 17 00:00:00 2001 From: AzaezelX Date: Tue, 29 Apr 2025 16:45:25 -0500 Subject: [PATCH 74/74] isobject and obj.delete safties isobject isn't actually one if it's yet to be properlyAdded, or already marked with isRemoved likewise don't try and delete a deleted thing --- Engine/source/console/sim.cpp | 10 +++++++++- Engine/source/console/simObject.cpp | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Engine/source/console/sim.cpp b/Engine/source/console/sim.cpp index 48e3b627f..439f85ea9 100644 --- a/Engine/source/console/sim.cpp +++ b/Engine/source/console/sim.cpp @@ -102,7 +102,15 @@ DefineEngineFunction( isObject, bool, (const char * objectName), ,"isObject(obje if (!String::compare(objectName, "0") || !String::compare(objectName, "")) return false; else - return (Sim::findObject(objectName) != NULL); + { + SimObject* obj= Sim::findObject(objectName); + if (obj) + { + if (!obj->isProperlyAdded() || obj->isRemoved()) + obj = NULL; + } + return obj != NULL; + } } ConsoleDocFragment _spawnObject1( diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index fdbc13d72..1f6e28af5 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -3296,6 +3296,9 @@ DefineEngineMethod( SimObject, getGroup, SimGroup*, (),, DefineEngineMethod( SimObject, delete, void, (),, "Delete and remove the object." ) { + if (!object->isProperlyAdded() || object->isRemoved()) + return; + object->deleteObject(); }