838 lines
26 KiB
Plaintext
Executable File
838 lines
26 KiB
Plaintext
Executable File
|
|
-- Memory Resident Zones:
|
|
-- Functions for building/exporting/loading files for setting up memory-resident model-lists
|
|
--
|
|
-- Neal D Corbett (R* Leeds) 04/2014
|
|
|
|
-- Struct will be instanced to this global:
|
|
global gRsMemResidentLists = undefined
|
|
|
|
struct RsMemResidentLists
|
|
(
|
|
-- Used to tag metafiles with format-version:
|
|
VersionNum = 1,
|
|
|
|
-- Exported prop-lists will a maximum of this many model-names:
|
|
MaxPropCount = 200,
|
|
|
|
-- Area-zone metafiles are given this prefix:
|
|
AreaFilePrefix = "AreaZone_",
|
|
|
|
-- Data loaded from scenexmls will be cached to here:
|
|
SceneXmlCachePath = (RsMakeSafeSlashes ((RsConfigGetStreamDir core:True) + "/MemResCache/")),
|
|
|
|
-- Returns the path where Memory Resident lists are stored
|
|
fn GetMetaFilePath dlc: =
|
|
(
|
|
if (dlc == Unsupplied) do
|
|
dlc = RsIsDLCProj()
|
|
|
|
return (RsMakeSafePath (RsConfigGetCommonDir core:(not dlc) + "/data/levels/" + (RsConfigGetProjectName core:(not dlc)) + "/resident/"))
|
|
),
|
|
|
|
-- Returns folder that current project is exported to
|
|
fn GetProjExportRoot dlc: =
|
|
(
|
|
if (dlc == Unsupplied) do
|
|
dlc = RsIsDLCProj()
|
|
|
|
return (RsMakeSafePath ((RsConfigGetExportLevelsDir core:(not dlc)) + ((RsConfigGetProjectName core:(not dlc))) ))
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Returns list of Xml-paths for Exterior maps:
|
|
------------------------------------------------------------------------------------------
|
|
fn GetMapXmlPaths dlc: =
|
|
(
|
|
-- Get content-tree nodes for all scenes in current project:
|
|
local mapFileNodes = RsContentTree.ContentTreeHelper.GetAllMapExportNodes()
|
|
|
|
local projExportRoot = this.GetProjExportRoot dlc:dlc
|
|
|
|
-- Get list of map-metafiles:
|
|
local MapXmlPaths = for mapMaxNode in mapFileNodes collect
|
|
(
|
|
local metafilepath = dontCollect
|
|
|
|
-- Only read certain maptypes:
|
|
local mapType = (RsContentTree.GetMapType mapMaxNode) as name
|
|
|
|
if (mapType == #map_container) do
|
|
(
|
|
local ExportZipNode = (RsContentTree.GetMapExportZipNode MapMaxNode)
|
|
local ProcessedZipNode = (RsContentTree.GetMapExportZipNode ExportZipNode)
|
|
|
|
if (ProcessedZipNode != undefined) do
|
|
(
|
|
local XmlNode = RsContentTree.GetMapExportSceneXMLNode ExportZipNode
|
|
local XmlPath = XmlNode.AbsolutePath
|
|
|
|
--if (not matchPattern XmlPath pattern:"*_prologue*") do
|
|
(
|
|
-- Find relative path from ProjExportRoot to this xml-file:
|
|
local XmlRelPath = pathConfig.convertPathToRelativeTo XmlPath ProjExportRoot
|
|
|
|
-- This pattern matches if xml is under ProjExportRoot, and the xml-file actually exists:
|
|
if (matchPattern XmlRelPath pattern:".\\*") and (doesFileExist XmlPath) do
|
|
(
|
|
-- Collect matching xml:
|
|
metafilepath = XmlPath
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
metafilepath
|
|
)
|
|
|
|
return MapXmlPaths
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Load per-map position-lists for each prop used in game:
|
|
------------------------------------------------------------------------------------------
|
|
fn LoadPropData force:False dlc: =
|
|
(
|
|
if (dlc == Unsupplied) do
|
|
dlc = RsIsDLCProj()
|
|
|
|
-- Get list of 'Exterior' map-xmls:
|
|
local mapXmlPaths = this.GetMapXmlPaths()
|
|
if (mapXmlPaths.Count == 0) do
|
|
return #()
|
|
|
|
PushPrompt "Checking Perforce for updated XMLs..."
|
|
local ChangedXmls = gRsPerforce.ChangedFiles MapXmlPaths
|
|
PopPrompt()
|
|
|
|
-- Sync latest xml-files:
|
|
if (ChangedXmls.Count != 0) do
|
|
(
|
|
local queryText = stringstream ""
|
|
local singular = (ChangedXmls.count == 1)
|
|
format "% a newer version on Perforce.\n\nWould you like to sync % now?" (if singular then "This file has" else "These files have") (if singular then "it" else "them") to:queryText
|
|
|
|
local SyncChecked = #{1..ChangedXmls.Count}
|
|
|
|
local queryVal = RsQueryBoxMultiBtn (queryText as string) title:"Updated RSref-files found on Perforce:" timeout:15 width:540 \
|
|
labels:#("Yes", "No", "Cancel Process") defaultBtn:2 cancelBtn:3 \
|
|
tooltips:#("Update SceneXml files from Perforce", "Don't update SceneXml files from Perforce", "Cancel load/generation process") \
|
|
listCheckStates:SyncChecked listItems:ChangedXmls listTitles:#("Sync", "Filename")
|
|
|
|
case queryVal of
|
|
(
|
|
-- Sync checked filenames:
|
|
1:
|
|
(
|
|
local SyncFiles = for n = SyncChecked collect ChangedXmls[n]
|
|
|
|
if (SyncFiles.Count != 0) do
|
|
(
|
|
local success = gRsPerforce.Sync ChangedXmls clobberAsk:True silent:True showProgress:True progressTitle:"Syncing exterior-map sceneXml files..."
|
|
|
|
-- Abort if sync was cancelled:
|
|
if (not success) do
|
|
return False
|
|
)
|
|
)
|
|
-- Abort if 'Cancel' was pressed:
|
|
3:(return False)
|
|
)
|
|
)
|
|
|
|
local MapDataList
|
|
|
|
-- Load scenexml data (or cachefiles if valid)
|
|
(
|
|
local TimeStart = TimeStamp()
|
|
local Success = True
|
|
|
|
ProgressStart "Loading Prop Positions..."
|
|
local FileCount = MapXmlPaths.Count
|
|
local MapDataList = for n = 1 to FileCount while (Success = ProgressUpdate (100.0 * n / FileCount)) collect
|
|
(
|
|
local XmlPath = MapXmlPaths[n]
|
|
::RsMemRes_MapData Filename:XmlPath isDlc:dlc
|
|
)
|
|
ProgressEnd()
|
|
|
|
-- Return False if load was cancelled:
|
|
if (not Success) do
|
|
return False
|
|
)
|
|
|
|
format "Time taken to parse prop-positions: %s\n" (FormattedPrint ((TimeStamp() - TimeStart) / (1000.0)) Format:".3g")
|
|
return MapDataList
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Returns wildcard-descriptor for area-zone metafiles:
|
|
------------------------------------------------------------------------------------------
|
|
fn GetAreaZoneMetaPath dlc: =
|
|
(
|
|
(this.GetMetafilePath dlc:dlc) + this.areaFilePrefix + "*.meta"
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Get position-lists for each map-area:
|
|
------------------------------------------------------------------------------------------
|
|
fn GetPropsByArea =
|
|
(
|
|
-- Load prop-position data from SceneXmls:
|
|
local mapDataList = this.LoadPropData()
|
|
if (not IsKindOf mapDataList Array) do
|
|
return False
|
|
|
|
if (mapDataList.count == 0) do
|
|
return #()
|
|
|
|
PushPrompt "Compiling props per area..."
|
|
local AreaNames = #()
|
|
local AreaDataList = #()
|
|
struct MapAreaData (AreaName, PropNames = #(), PropPositions = #())
|
|
|
|
for MapItem in MapDataList do
|
|
(
|
|
local MapArea = MapItem.AreaName
|
|
|
|
local AreaIdx = FindItem AreaNames MapItem.AreaName
|
|
|
|
if (AreaIdx == 0) do
|
|
(
|
|
append AreaNames MapItem.AreaName
|
|
append AreaDataList (MapAreaData AreaName:MapItem.AreaName)
|
|
AreaIdx = AreaNames.Count
|
|
)
|
|
|
|
local AreaPropNames = AreaDataList[AreaIdx].PropNames
|
|
local AreaPropPositions = AreaDataList[AreaIdx].PropPositions
|
|
|
|
local MapPropNames = MapItem.PropNames
|
|
local MapPropPositions = MapItem.PropPositions
|
|
|
|
for n = 1 to MapPropNames.Count do
|
|
(
|
|
local PropIdx = FindItem AreaPropNames MapPropNames[n]
|
|
|
|
if (PropIdx == 0) do
|
|
(
|
|
append AreaPropNames MapPropNames[n]
|
|
append AreaPropPositions #()
|
|
PropIdx = AreaPropNames.Count
|
|
)
|
|
|
|
join AreaPropPositions[PropIdx] MapPropPositions[n]
|
|
)
|
|
)
|
|
|
|
PopPrompt()
|
|
|
|
return AreaDataList
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Update Memory Resident area-zone metafiles, based on props found within:
|
|
------------------------------------------------------------------------------------------
|
|
fn UpdateAreaZones dlc: =
|
|
(
|
|
-- Load prop-position data from SceneXmls:
|
|
local mapDataList = this.LoadPropData()
|
|
if (not IsKindOf mapDataList Array) do
|
|
return False
|
|
|
|
if (mapDataList.count == 0) do
|
|
return #()
|
|
|
|
-- Compile master-list of prop-positions:
|
|
PushPrompt "Compiling prop-position lists..."
|
|
local PropNames = #()
|
|
local PropPositions = #()
|
|
for MapData in MapDataList do
|
|
(
|
|
for MapPropIdx = 1 to MapData.PropNames.Count do
|
|
(
|
|
local PropName = MapData.PropNames[MapPropIdx]
|
|
local PropIdx = FindItem PropNames PropName
|
|
|
|
if (PropIdx == 0) do
|
|
(
|
|
append PropNames PropName
|
|
append PropPositions #()
|
|
PropIdx = PropNames.Count
|
|
)
|
|
|
|
join PropPositions[PropIdx] MapData.PropPositions[MapPropIdx]
|
|
)
|
|
)
|
|
local PropDataList = for n = 1 to PropNames.Count collect (DataPair Name:PropNames[n] Positions:PropPositions[n])
|
|
PropNames.Count = 0
|
|
PropPositions.Count = 0
|
|
PopPrompt()
|
|
|
|
local metafilePath = (this.GetAreaZoneMetaPath dlc:dlc)
|
|
|
|
-- Sync latest Area-zone metafiles:
|
|
gRsPerforce.SyncChanged #(MetafilePath) clobberAsk:False silent:True
|
|
|
|
-- Checkout area-zone metafiles:
|
|
local AreaZoneFiles = GetFiles MetafilePath
|
|
gRsPerforce.Add_Or_Edit AreaZoneFiles
|
|
|
|
-- Import metafile boxes:
|
|
-- (don't both loading model-lists)
|
|
local AreaZones = for Filename in AreaZoneFiles collect
|
|
(
|
|
local LoadedArea = RsMemRes_Zone.Import Filename Models:False
|
|
if (LoadedArea == undefined) then DontCollect else LoadedArea
|
|
)
|
|
|
|
local AreaPropCounts = for AreaZone in AreaZones collect
|
|
(
|
|
DataPair Zone:AreaZone Props:#()
|
|
)
|
|
|
|
-- Run through each prop-list in turn, work out which prop is in each area, and how many of each:
|
|
for ThisProp in PropDataList do
|
|
(
|
|
local PropName = ThisProp.Name
|
|
|
|
for AreaItem in AreaPropCounts do
|
|
(
|
|
local AreaPropItem = (DataPair Name:PropName Count:0)
|
|
append AreaItem.Props AreaPropItem
|
|
|
|
local ThisZone = AreaItem.Zone
|
|
|
|
-- Check all positions to see if they're in the box:
|
|
for ThisPos in ThisProp.Positions do
|
|
(
|
|
if (ThisZone.IsInsideBox ThisPos) do
|
|
(
|
|
AreaPropItem.Count += 1
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
fn Sorter v1 v2 = (v2.Count - v1.Count)
|
|
|
|
-- Update area-metafiles:
|
|
for AreaItem in AreaPropCounts do
|
|
(
|
|
-- Filter out props with fewer than minimum number of instances in area:
|
|
AreaItem.Props = for PropItem in AreaItem.Props where (PropItem.Count > 1) collect PropItem
|
|
|
|
-- Sort props in ascending order of instance-counts:
|
|
qsort AreaItem.Props Sorter
|
|
|
|
-- Cut list down to maximum-allowed length:
|
|
if (AreaItem.Props.Count > MaxPropCount) do
|
|
(
|
|
AreaItem.Props = for n = 1 to MaxPropCount collect
|
|
(
|
|
AreaItem.Props[n]
|
|
)
|
|
)
|
|
|
|
format "%'s residents-list has % model-names:\n" AreaItem.Zone.ZoneName AreaItem.Props.Count
|
|
for PropItem in AreaItem.Props do
|
|
(
|
|
format " %: %\n" PropItem.Name PropItem.Count
|
|
)
|
|
|
|
-- Update zone's propname-list:
|
|
AreaItem.Zone.PropNames = for PropItem in AreaItem.Props collect PropItem.Name
|
|
)
|
|
|
|
-- Re-export area-metafiles with their updated model-lists:
|
|
for AreaItem in AreaPropCounts do
|
|
(
|
|
AreaItem.Zone.Export()
|
|
)
|
|
|
|
return AreaPropCounts
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Finds exported Area zones that intersect BoxMin/BoxMax,
|
|
-- and returns list of resident models combined from each zone found:
|
|
------------------------------------------------------------------------------------------
|
|
fn GetAreaPropsForBox BoxMin BoxMax =
|
|
(
|
|
PushPrompt "Memory Residents: Searching for intersecting Area zones..."
|
|
|
|
-- Sync Area zone-metafiles:
|
|
local CheckPath = (GetAreaZoneMetaPath())
|
|
gRsPerforce.SyncChanged #(CheckPath) clobberAsk:True silent:True showProgress:True progressTitle:"Syncing Area Resident Lists..."
|
|
|
|
-- Get list of metafiles, and import zones:
|
|
local LoadFiles = (getFiles CheckPath)
|
|
local AreaZones = for ThisFilename in LoadFiles collect (::RsMemRes_Zone.Import ThisFilename)
|
|
|
|
local PropNames = #()
|
|
|
|
for ThisZone in AreaZones where (ThisZone != undefined) do
|
|
(
|
|
local BoxIntersects = True
|
|
|
|
-- Compare x/y/z values:
|
|
for n = 1 to 3 while BoxIntersects do
|
|
(
|
|
MinA = BoxMin[n]
|
|
MaxA = BoxMax[n]
|
|
MinB = ThisZone.MinPos[n]
|
|
MaxB = ThisZone.MaxPos[n]
|
|
|
|
-- False if boxes don't coincide on this axis:
|
|
BoxIntersects = not ((MaxA < MinB) or (MinA > MaxB))
|
|
)
|
|
|
|
-- Collect props from intersecting area-zone:
|
|
if BoxIntersects do
|
|
(
|
|
local LogMsg = StringStream ""
|
|
format "Box intersects area-zone: % (% resident models)" ThisZone.ZoneName ThisZone.PropNames.Count To:LogMsg
|
|
gRsUlog.LogMessage (LogMsg as String) context:"MemoryResidents"
|
|
|
|
join PropNames ThisZone.PropNames
|
|
)
|
|
)
|
|
|
|
-- Remove duplicate prop-names:
|
|
PropNames = (MakeUniqueArray PropNames)
|
|
|
|
PopPrompt()
|
|
|
|
gRsUlog.LogMessage ("PropNames loaded from Area-Zone Memory Resident lists: " + (PropNames.Count as String)) context:"MemoryResidents"
|
|
|
|
return PropNames
|
|
)
|
|
)
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Defines information on props used in a particular map-scene:
|
|
-- (Must be instantiated with 'Filename' xml-path argument)
|
|
------------------------------------------------------------------------------------------
|
|
struct RsMemRes_MapData
|
|
(
|
|
PropNames = #(), PropPositions = #(),
|
|
Filename, AreaName, FileHash = 0,
|
|
isDlc = False,
|
|
|
|
fn GetCacheFilename =
|
|
(
|
|
gRsMemResidentLists.SceneXmlCachePath + (GetFilenameFile Filename) + ".txt"
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Store data to XML cache-file:
|
|
------------------------------------------------------------------------------------------
|
|
fn StoreDataCache =
|
|
(
|
|
makeDir gRsMemResidentLists.SceneXmlCachePath
|
|
local CacheFilename = (GetCacheFilename())
|
|
|
|
if (DoesFileExist CacheFilename) do (SetFileAttribute CacheFilename #readOnly False)
|
|
|
|
local SaveStream = stringStream ""
|
|
|
|
-- Store basic details: file-hash, source-filename, and version-number:
|
|
format "%\n%\nVersion:%\n" FileHash Filename gRsMemResidentLists.VersionNum to:SaveStream
|
|
|
|
-- Store names and positions of each prop:
|
|
for n = 1 to PropNames.Count do
|
|
(
|
|
local PropName = PropNames[n]
|
|
local PosList = PropPositions[n]
|
|
|
|
format "%\n" PropName to:SaveStream
|
|
|
|
for ThisPos in PosList do
|
|
(
|
|
format "%\n" ThisPos to:SaveStream
|
|
)
|
|
)
|
|
|
|
try
|
|
(
|
|
local Fstream = CreateFile CacheFilename
|
|
format "%" (SaveStream as string) to:Fstream
|
|
close Fstream
|
|
|
|
gRsUlog.LogMessage ("Successfully saved Memory Resident list: " + CacheFilename) context:"MemoryResidents"
|
|
)
|
|
catch
|
|
(
|
|
gRsUlog.LogWarning ("Failed to save Memory Resident list! " + CacheFilename) context:"MemoryResidents"
|
|
messageBox ("Failed to save Memory Resident list!\n\n" + CacheFilename) title:"XML file-save failure"
|
|
)
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Reload pre-processed data, if found:
|
|
------------------------------------------------------------------------------------------
|
|
fn LoadCachedData =
|
|
(
|
|
local CacheFilename = (GetCacheFilename())
|
|
|
|
if (not DoesFileExist CacheFilename) do return False
|
|
|
|
-- Reset lists:
|
|
PropNames.Count = 0
|
|
PropPositions.Count = 0
|
|
|
|
local FStream = openFile CacheFilename
|
|
|
|
local CachedHash = readLine FStream
|
|
|
|
-- Return if cached hash doesn't match file:
|
|
if (FileHash != CachedHash) do
|
|
(
|
|
return False
|
|
)
|
|
|
|
-- Skip sourcefile-path and cache's version-number, they're just intended for human debugging:
|
|
skipToNextLine FStream
|
|
skipToNextLine FStream
|
|
|
|
-- Return if file has no props named:
|
|
if (eof FStream) do return True
|
|
|
|
-- Get first prop-name...
|
|
local PropName = readLine FStream
|
|
|
|
-- Loop through the rest of the file:
|
|
while (not eof FStream) do
|
|
(
|
|
append PropNames PropName
|
|
|
|
local ThisPropPositions = #()
|
|
append PropPositions ThisPropPositions
|
|
|
|
PropName = undefined
|
|
|
|
while (not eof Fstream) and (PropName == undefined) do
|
|
(
|
|
local ThisLine = readLine Fstream
|
|
|
|
-- This line will either be a position for the current propname, or will be the next propname:
|
|
if (ThisLine[1] == "[") then
|
|
(
|
|
append ThisPropPositions (execute ThisLine)
|
|
)
|
|
else
|
|
(
|
|
PropName = ThisLine
|
|
)
|
|
)
|
|
)
|
|
close FStream
|
|
|
|
return True
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Load prop-positions for 'Filename'
|
|
------------------------------------------------------------------------------------------
|
|
fn LoadPropsFromXml =
|
|
(
|
|
-- Reset lists:
|
|
PropNames.Count = 0
|
|
PropPositions.Count = 0
|
|
|
|
-- Get hashvalue from file-data/version:
|
|
FileHash = (GetHashValue (GetFileModDate Filename) gRsMemResidentLists.VersionNum) as String
|
|
|
|
-- Attempt to load cached prop-list for this map:
|
|
local ValidCache = LoadCachedData()
|
|
|
|
-- Return if cache was valid:
|
|
if ValidCache do return True
|
|
|
|
-- If not, load the map's latest scenexml:
|
|
local ObjectsOnly = (dotNetClass "RSG.SceneXml.LoadOptions").Objects
|
|
local SceneXml = dotnetobject "RSG.SceneXml.Scene" Filename ObjectsOnly True
|
|
|
|
-- Get flat list of scene's object-elements:
|
|
local SceneObjs = #()
|
|
join SceneObjs SceneXml.Objects
|
|
for ThisObj in SceneXml.Objects do
|
|
(
|
|
join SceneObjs (ThisObj.GetChildrenRecursive())
|
|
)
|
|
|
|
-- Filter out non-ref objects:
|
|
local RefObjs = for Obj in SceneObjs where (Obj.IsRefObject() and (not Obj.DontExport())) collect Obj
|
|
|
|
-- Collect positions of suitable props:
|
|
for Obj in RefObjs do
|
|
(
|
|
local ObjName = ToLower (Obj.GetObjectName())
|
|
|
|
local PropNum = findItem PropNames ObjName
|
|
if (PropNum == 0) do
|
|
(
|
|
append PropNames ObjName
|
|
append PropPositions #()
|
|
PropNum = PropNames.Count
|
|
)
|
|
|
|
(
|
|
-- Get prop's position:
|
|
local ObjXformPos = Obj.ObjectTransform.D
|
|
local ObjPos = [ObjXformPos.X, ObjXformPos.Y, ObjXformPos.Z]
|
|
append PropPositions[PropNum] ObjPos
|
|
)
|
|
|
|
--format "%: %\n" ObjName ObjPos
|
|
)
|
|
|
|
-- Store newly-loaded data to cache-file:
|
|
StoreDataCache()
|
|
|
|
return True
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Automatically load data from Filename when this struct is instanced:
|
|
------------------------------------------------------------------------------------------
|
|
on Create do
|
|
(
|
|
if (Filename == undefined) or (not DoesFileExist Filename) do return False
|
|
|
|
-- Get AreaName from name of scenexml's off-the-root parent-folder:
|
|
(
|
|
-- Find xml's path relative to Project Export Root:
|
|
-- ('GetMapXmlPaths' will have already filtered path to ensure it's under here)
|
|
local projExportRoot = RsMemResidentLists.GetProjExportRoot dlc:this.isDlc
|
|
local XmlRelPath = (pathConfig.convertPathToRelativeTo Filename ProjExportRoot)
|
|
|
|
AreaName = pathConfig.stripPathToTopParent (pathConfig.removePathTopParent XmlRelPath)
|
|
)
|
|
|
|
LoadPropsFromXml()
|
|
)
|
|
)
|
|
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Defines a Memory Resident Props zone, which is used to export/import data:
|
|
------------------------------------------------------------------------------------------
|
|
struct RsMemRes_Zone
|
|
(
|
|
ZoneName, -- Name of zone
|
|
Filename, -- Filename for zone's metafile
|
|
PropNames = #(), -- Names of models that will be made memory-resident when player is in this zone
|
|
MinPos, MaxPos, -- Zone's bounding-box
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Returns 'True' if 'Pos' is within zone-box:
|
|
------------------------------------------------------------------------------------------
|
|
fn IsInsideBox Pos =
|
|
(
|
|
local IsInside = True
|
|
|
|
-- Test x/y/z values:
|
|
for n = 1 to 3 while IsInside do
|
|
(
|
|
IsInside = not ((Pos[n] < MinPos[n]) or (Pos[n] > MaxPos[n]))
|
|
)
|
|
|
|
return IsInside
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Create box-object to show area-zone:
|
|
------------------------------------------------------------------------------------------
|
|
fn CreateBoxObj =
|
|
(
|
|
-- Set random-seed based on ZoneName, so boxes with same name will be same colour:
|
|
Seed (GetHashValue ZoneName 1)
|
|
local Clr = RsGetRandomColour()
|
|
|
|
local BoxSize = (MaxPos - MinPos)
|
|
local BoxPos = minPos + ([BoxSize.X, BoxSize.Y, 0] / 2)
|
|
|
|
Box name:ZoneName pos:BoxPos width:BoxSize.X length:BoxSize.Y height:BoxSize.Z WireColor:Clr
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Initialise this zone-descriptor with values taken from 'MapAreaData' struct:
|
|
------------------------------------------------------------------------------------------
|
|
fn InitAreaBlock AreaData dlc:False =
|
|
(
|
|
-- Generate zone-name and metafile-export path from area-name:
|
|
ZoneName = (gRsMemResidentLists.AreaFilePrefix + AreaData.AreaName)
|
|
Filename = (gRsMemResidentLists.GetMetafilePath dlc:dlc + ZoneName + ".meta")
|
|
|
|
-- Pair off the prop-names/positions for models with enough instances:
|
|
local AreaPropNames = AreaData.PropNames
|
|
local AreaPropPositions = AreaData.PropPositions
|
|
local AreaProps = for n = 1 to AreaPropNames.Count collect
|
|
(
|
|
if (AreaPropPositions[n].Count < gRsMemResidentLists.MinInstances) then DontCollect else
|
|
(
|
|
DataPair Name:AreaPropNames[n] Positions:AreaPropPositions[n]
|
|
)
|
|
)
|
|
|
|
-- Sort props in ascending order of instance-counts:
|
|
fn Sorter v1 v2 = (v2.Positions.Count - v1.Positions.Count)
|
|
qsort AreaProps Sorter
|
|
|
|
format "%: % props\n" ZoneName AreaProps.Count
|
|
for item in AreaProps do
|
|
(
|
|
--format " %:%\n" Item.Name Item.Positions.Count
|
|
)
|
|
|
|
-- Find bounding-box for zone:
|
|
if (AreaProps.Count != 0) do
|
|
(
|
|
local PropPositions = #()
|
|
for Item in AreaProps do
|
|
(
|
|
append PropNames Item.Name
|
|
join PropPositions Item.Positions
|
|
)
|
|
|
|
RsGetBBox PropPositions &MinPos &MaxPos
|
|
|
|
-- Apply padding:
|
|
local Padding = 300
|
|
MinPos -= Padding
|
|
MaxPos += Padding
|
|
|
|
--CreateBoxObj()
|
|
)
|
|
|
|
return OK
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Export this memory-resident zone to metafile:
|
|
------------------------------------------------------------------------------------------
|
|
fn Export =
|
|
(
|
|
gRsUlog.LogMessage ("Storing Memory Resident list: " + Filename) context:"MemoryResidents" quiet:False
|
|
print This
|
|
|
|
-- Init xml doc:
|
|
local xmlDoc = XmlDocument()
|
|
xmlDoc.init()
|
|
|
|
local xmlRoot = xmlDoc.createElement "CZonedAssets" appendTo:xmlDoc.Document
|
|
|
|
local xmlElem = xmlDoc.createElement "Name" appendTo:xmlRoot
|
|
xmlElem.innerText = ZoneName
|
|
|
|
-- Add map-extents:
|
|
(
|
|
local extentsElem = xmlDoc.createElement "Extents" appendTo:xmlRoot
|
|
|
|
local xmlElem = RsCreateXmlElement "min" #(dataPair name:"x" value:(MinPos.X), dataPair name:"y" value:(MinPos.Y), dataPair name:"z" value:(MinPos.Z)) xmlDoc
|
|
extentsElem.AppendChild(xmlElem)
|
|
|
|
local xmlElem = RsCreateXmlElement "max" #(dataPair name:"x" value:(MaxPos.X), dataPair name:"y" value:(MaxPos.Y), dataPair name:"z" value:(MaxPos.Z)) xmlDoc
|
|
extentsElem.AppendChild(xmlElem)
|
|
)
|
|
|
|
-- Add model-names:
|
|
local filesElem = xmlDoc.createElement "Models" appendTo:xmlRoot
|
|
for PropName in PropNames do
|
|
(
|
|
local xmlElem = xmlDoc.createElement "Item" appendTo:filesElem
|
|
xmlElem.innerText = PropName
|
|
)
|
|
|
|
-- Set version-number:
|
|
local xmlElem = RsCreateXmlElement "Version" #(dataPair name:"value" value:gRsMemResidentLists.VersionNum) xmlDoc
|
|
xmlRoot.AppendChild(xmlElem)
|
|
|
|
try
|
|
(
|
|
xmlDoc.save Filename
|
|
gRsUlog.LogMessage ("Successfully saved Memory Resident list " + Filename) context:"MemoryResidents"
|
|
)
|
|
catch
|
|
(
|
|
gRsUlog.LogWarning ("Failed to save Memory Resident list! " + Filename) context:"MemoryResidents"
|
|
messageBox ("Failed to save Memory Resident list!\n\n" + Filename) title:"XML file-save failure"
|
|
)
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- Import 'RsMemRes_Zone' instance from a metafile:
|
|
------------------------------------------------------------------------------------------
|
|
fn Import ImportFilename Models:True =
|
|
(
|
|
if (not DoesFileExist ImportFilename) do return undefined
|
|
|
|
local NewZone = RsMemRes_Zone Filename:ImportFilename
|
|
|
|
try
|
|
(
|
|
local XmlReader = dotNetObject "System.Xml.XmlTextReader" ImportFilename
|
|
while (XmlReader.Read()) do
|
|
(
|
|
if (XmlReader.IsStartElement()) do
|
|
(
|
|
case (XmlReader.Name as Name) of
|
|
(
|
|
#Item:
|
|
(
|
|
if Models do
|
|
(
|
|
XmlReader.Read()
|
|
local PropName = XmlReader.Value
|
|
append NewZone.PropNames PropName
|
|
)
|
|
)
|
|
#Name:
|
|
(
|
|
XmlReader.Read()
|
|
NewZone.ZoneName = XmlReader.Value
|
|
)
|
|
#Min:
|
|
(
|
|
NewZone.MinPos = [0,0,0]
|
|
for n = 1 to 3 do
|
|
(
|
|
local Val = XmlReader.GetAttribute (n - 1)
|
|
NewZone.MinPos[n] = (Val as Float)
|
|
)
|
|
)
|
|
#Max:
|
|
(
|
|
NewZone.MaxPos = [0,0,0]
|
|
for n = 1 to 3 do
|
|
(
|
|
local Val = XmlReader.GetAttribute (n - 1)
|
|
NewZone.MaxPos[n] = (Val as Float)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
XmlReader.Close()
|
|
)
|
|
catch
|
|
(
|
|
return undefined
|
|
)
|
|
|
|
-- Return undefined if certain values weren't loaded:
|
|
if (NewZone.ZoneName == undefined) or (NewZone.MinPos == undefined) or (NewZone.MaxPos == undefined) do
|
|
(
|
|
NewZone = undefined
|
|
)
|
|
|
|
return NewZone
|
|
)
|
|
)
|
|
|
|
gRsMemResidentLists = RsMemResidentLists()
|
|
|
|
--gRsMemResidentLists.GetAreaPropsForBox [0,0,0] [100,100,100]
|
|
--global blah = gRsMemResidentLists.UpdateAreaZones() |