1226 lines
36 KiB
Plaintext
Executable File
1226 lines
36 KiB
Plaintext
Executable File
--
|
||
-- File:: pipeline/ui/ObjectPosChecker.ms
|
||
-- Description:: GTA Object Position Checker Import MAXScript
|
||
-- This imports the output of the Object Position Checker that runs in-game.
|
||
-- Once the text file has been read it provides a preview of the affected
|
||
-- objects and then object position can be automatically changed for the
|
||
-- loaded blocks.
|
||
--
|
||
-- 25 June 2007
|
||
-- by David Muir <david.muir@rockstarnorth.com>
|
||
--
|
||
-----------------------------------------------------------------------------
|
||
|
||
-----------------------------------------------------------------------------
|
||
-- Uses
|
||
-----------------------------------------------------------------------------
|
||
filein "pipeline/export/maps/globals.ms"
|
||
filein "pipeline/helpers/animation/hierarchyBB.ms"
|
||
filein "pipeline/util/RSProcess.ms"
|
||
|
||
-----------------------------------------------------------------------------
|
||
-- Rollouts
|
||
-----------------------------------------------------------------------------
|
||
rollout GtaObjectPosCheckerInitiatorRoll "Object Position Checker Initiator"
|
||
(
|
||
-------------------------------------------------------------------------------
|
||
-- constants
|
||
-------------------------------------------------------------------------------
|
||
local debug = true
|
||
local connectionTimerInterval = 5000 --Tick every 5 seconds
|
||
local sweepTimerInterval = 2500 --Tick every 2.5 seconds
|
||
|
||
local RagSectorToolsStart = "Sector Tools/Start X Y Z World Coordinates:"
|
||
local RagSectorToolsEnd = "Sector Tools/End X Y Z World Coordinates:"
|
||
|
||
local RagObjPosCheckerBank = "Sector Tools/Physical Object Position Checker"
|
||
local RagObjPosCheckerOutputDir = "Sector Tools/Physical Object Position Checker/Output directory:"
|
||
local RagObjPosCheckerRestrictToEntities = "Sector Tools/Physical Object Position Checker/Restrict to Specific Entities"
|
||
local RagObjPosCheckerEntityFile = "Sector Tools/Physical Object Position Checker/Entity Restriction File:"
|
||
local RagObjPosCheckerScanArea = "Sector Tools/Physical Object Position Checker/Scan and Export Area"
|
||
local RagObjPosCheckerScanInProgress = "Sector Tools/Physical Object Position Checker/Scan In Progress (read-only)"
|
||
local RagObjPosCheckerAbort = "Sector Tools/Physical Object Position Checker/Abort Running Scan"
|
||
|
||
local ps3BatchFile = "game_psn_bankrelease_snc.bat"
|
||
local xboxBatchFile = "game_xenon_bankrelease.bat"
|
||
local defaultCommandlineArgs = "-rag –nopeds –nocars –novehs -maponly -invincible -sectortools"
|
||
local inputFilename = "object_position_sectors.txt"
|
||
local entitiesFilename = "object_position_entities.txt"
|
||
local responseFilename = "additional_game_args.txt"
|
||
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- Data
|
||
-------------------------------------------------------------------------------
|
||
local connectionTimer = undefined
|
||
local sweepTimer = undefined
|
||
local buildDir = undefined
|
||
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- UI
|
||
-------------------------------------------------------------------------------
|
||
dropDownList lstPlatforms "Platform:" items:#("PS3","Xbox360") width:200 across:2
|
||
dropDownList lstSweepMode "Sweep Mode:" items:#("Open Containers","Selected Containers","Selected Entities") width:200
|
||
|
||
label lblOutputDirHeader "Output Directory:" across:3 align:#left
|
||
label lblOutputDir "c:\\temp" offset:[-90,0] align:#left
|
||
button btnBrowse "Browse" width:80 align:#right
|
||
|
||
label lblAdditionalArgsHeader "Commandline Args:" width:120 across:3 align:#left
|
||
edittext edtAdditionalArgs text:defaultCommandlineArgs offset:[-90,0] width:400 align:#left
|
||
label lblDummy width:1 align:#right
|
||
|
||
button btnStart "Start In-Game Checker" width:200
|
||
|
||
label lblConnectionHeader "Game Connection Status:" across:2 align:#left
|
||
label lblConnectionStatus "Not connected" align:#left offset:[-100,0]
|
||
|
||
label lblSweepHeader "Sweep Status:" across:3 align:#left
|
||
label lblSweepStatus "" width:300 align:#left
|
||
button btnAbort "Abort" width:100 align:#right
|
||
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- Functions
|
||
-------------------------------------------------------------------------------
|
||
-- Helper method for printing debug messages.
|
||
fn PrintDebug message =
|
||
(
|
||
if (debug == true) then
|
||
(
|
||
print message
|
||
)
|
||
)
|
||
|
||
-- Retrieves list of selected entity guids.
|
||
fn GetSelectedEntityGuids =
|
||
(
|
||
local selectedObjectGuids = #()
|
||
local mapContainers = RsMapGetMapContainers()
|
||
|
||
local maps = for map in RsMapGetMapContainers doingExport:true where map.is_exportable() and (map.selCount() != 0) collect map
|
||
|
||
|
||
if (maps.count > 0) then
|
||
(
|
||
for map in maps do
|
||
(
|
||
local mapSectionMapNode = RsProjectContentFind (toLower map.name) type:"map"
|
||
local containerAttrGuid = getattrguid map.cont
|
||
containerAttrGuid = trimLeft containerAttrGuid "{"
|
||
containerAttrGuid = trimRight containerAttrGuid "}"
|
||
|
||
local containerHash32 = atStringHash containerAttrGuid seed:mapSectionMapNode.Seed
|
||
PrintDebug (format "map section seed:%, guid:%, hash:%" mapSectionMapNode.Seed containerAttrGuid containerHash32)
|
||
|
||
RsMapSetupGlobals map
|
||
for obj in map.selObjects do
|
||
(
|
||
local objAttrGuid = getattrguid obj
|
||
objAttrGuid = trimLeft objAttrGuid "{"
|
||
objAttrGuid = trimRight objAttrGuid "}"
|
||
|
||
local objectHash32 = atStringHash objAttrGuid seed:containerHash32
|
||
PrintDebug (format "object:%, guid:%, hash:%" obj.name objAttrGuid objectHash32)
|
||
append selectedObjectGuids objectHash32
|
||
)
|
||
)
|
||
)
|
||
|
||
return selectedObjectGuids
|
||
)
|
||
|
||
-- Returns a list of export objects for a particular map container.
|
||
fn GetExportObjects maps =
|
||
(
|
||
local objs = #()
|
||
|
||
for map in maps do
|
||
(
|
||
for obj in map.exportObjects do
|
||
(
|
||
if (GetAttrClass obj) == "Gta Object" then
|
||
(
|
||
append objs obj
|
||
)
|
||
)
|
||
)
|
||
|
||
return objs
|
||
)
|
||
|
||
-- Returns the bounds that we need to send to the game.
|
||
fn GetBounds =
|
||
(
|
||
local bbox = undefined
|
||
|
||
if (lstSweepMode.selected == "Open Containers") then
|
||
(
|
||
local maps = for map in RsMapGetMapContainers doingExport:true where map.is_exportable() collect map
|
||
|
||
if (maps.count > 0) then
|
||
(
|
||
local expObjs = GetExportObjects maps
|
||
|
||
if (expObjs.count > 0) then
|
||
(
|
||
bbox = RsGetHierarchyBB expObjs[1] restrictToClass:"Gta Object"
|
||
|
||
for obj in expObjs do
|
||
(
|
||
RsExpandBB bbox (::RsGetHierarchyBB obj restrictToClass:"Gta Object")
|
||
)
|
||
)
|
||
)
|
||
)
|
||
else if (lstSweepMode.selected == "Selected Containers") then
|
||
(
|
||
local maps = for map in RsMapGetMapContainers doingExport:true where map.is_exportable() and (map.selCount() != 0) collect map
|
||
|
||
if (maps.count > 0) then
|
||
(
|
||
local expObjs = GetExportObjects maps
|
||
|
||
if (expObjs.count > 0) then
|
||
(
|
||
bbox = RsGetHierarchyBB expObjs[1] restrictToClass:"Gta Object"
|
||
|
||
for obj in expObjs do
|
||
(
|
||
RsExpandBB bbox (::RsGetHierarchyBB obj restrictToClass:"Gta Object")
|
||
)
|
||
)
|
||
)
|
||
)
|
||
else if (lstSweepMode.selected == "Selected Entities") then
|
||
(
|
||
local maps = for map in RsMapGetMapContainers doingExport:true where map.is_exportable() and (map.selCount() != 0) collect map
|
||
|
||
if (maps.count > 0) then
|
||
(
|
||
local expObjs = #()
|
||
for map in maps do
|
||
(
|
||
for obj in map.selObjects do
|
||
(
|
||
if (GetAttrClass obj) == "Gta Object" then
|
||
(
|
||
append expObjs obj
|
||
)
|
||
)
|
||
)
|
||
|
||
if (expObjs.count > 0) then
|
||
(
|
||
bbox = RsGetHierarchyBB expObjs[1] restrictToClass:"Gta Object"
|
||
|
||
for obj in expObjs do
|
||
(
|
||
RsExpandBB bbox (::RsGetHierarchyBB obj restrictToClass:"Gta Object")
|
||
)
|
||
)
|
||
)
|
||
)
|
||
|
||
PrintDebug bbox
|
||
return bbox
|
||
)
|
||
|
||
-- Helper method that checks whether the game is already running with the sector tools enabled
|
||
fn IsGameRunningWithSectorTools =
|
||
(
|
||
local retVal = false
|
||
if (RemoteConnection.IsConnected() == true) then
|
||
(
|
||
retVal = RemoteConnection.WidgetExists(RagObjPosCheckerBank)
|
||
)
|
||
return retVal
|
||
)
|
||
|
||
-- Helper method that checks whether a object position sweep is already in progress.
|
||
fn IsSweepInProgress =
|
||
(
|
||
local retVal = false
|
||
if (RemoteConnection.IsConnected() == true) then
|
||
(
|
||
retVal = RemoteConnection.ReadBoolWidget(RagObjPosCheckerScanInProgress)
|
||
)
|
||
return retVal
|
||
)
|
||
|
||
-- Writes a file containing a single line per entity guid.
|
||
fn CreateEntityGuidFile guids filepath =
|
||
(
|
||
local guidStream = stringStream ""
|
||
for guid in guids do
|
||
(
|
||
local guidString = formattedPrint guid
|
||
format "%\n" guidString to:guidStream
|
||
)
|
||
PrintDebug guidStream
|
||
|
||
local file = fopen filepath "wt"
|
||
WriteString file guidStream
|
||
fclose file
|
||
)
|
||
|
||
-- Writes a file containing the start/end coords.
|
||
fn CreateSectorInputFile bounds filepath =
|
||
(
|
||
local coords = stringStream ""
|
||
format "% % % %" bounds[1].x bounds[1].y bounds[2].x bounds[2].y to:coords
|
||
PrintDebug coords
|
||
|
||
local file = fopen filepath "wt"
|
||
WriteString file coords
|
||
fclose file
|
||
)
|
||
|
||
-- Writes a file containing the additional arguments the game should use when booting.
|
||
fn CreateResponseFile arguments filepath =
|
||
(
|
||
local file = fopen filepath "wt"
|
||
WriteString file arguments
|
||
fclose file
|
||
)
|
||
|
||
-- Starts the object position checker when the game is already running.
|
||
fn StartObjectPositionChecker =
|
||
(
|
||
-- Make sure we can determine some valid bounds to work with.
|
||
local bounds = GetBounds()
|
||
if (bounds == undefined) then
|
||
(
|
||
messageBox "Nothing to do for the selected sweep mode. Have you selected some exportable entities?"
|
||
)
|
||
else
|
||
(
|
||
-- Set all the appropriate RAG widget values.
|
||
local outputDir = lblOutputDir.text
|
||
RemoteConnection.WriteStringWidget RagObjPosCheckerOutputDir outputDir
|
||
|
||
local startStream = stringStream ""
|
||
format "% % %" bounds[1].x bounds[1].y bounds[1].z to:startStream
|
||
RemoteConnection.WriteStringWidget RagSectorToolsStart (startStream as string)
|
||
|
||
local endStream = stringStream ""
|
||
format "% % %" bounds[2].x bounds[2].y bounds[2].z to:endStream
|
||
RemoteConnection.WriteStringWidget RagSectorToolsEnd (endStream as string)
|
||
RemoteConnection.WriteBoolWidget RagObjPosCheckerRestrictToEntities (lstSweepMode.selected == "Selected Entities")
|
||
|
||
-- Create the entities file (if required).
|
||
if (lstSweepMode.selected == "Selected Entities") then
|
||
(
|
||
local entitiesFilepath = buildDir + entitiesFilename
|
||
CreateEntityGuidFile (GetSelectedEntityGuids()) entitiesFilepath
|
||
RemoteConnection.WriteStringWidget RagObjPosCheckerEntityFile entitiesFilepath
|
||
)
|
||
|
||
-- Start the object position checker process.
|
||
RemoteConnection.SendCommand("widget \"" + RagObjPosCheckerScanArea + "\"")
|
||
|
||
sweepTimer.Start()
|
||
btnStart.Enabled = false
|
||
lblSweepStatus.Text = "Sweep in Progress"
|
||
)
|
||
)
|
||
|
||
-- Boots the game with the object position checker commandline parameters.
|
||
fn BootAndStartObjectPositionChecker =
|
||
(
|
||
-- Make sure we can determine some valid bounds to work with.
|
||
local bounds = GetBounds()
|
||
|
||
if (bounds == undefined) then
|
||
(
|
||
messageBox "Nothing to do for the selected sweep mode. Have you selected some exportable entities?"
|
||
)
|
||
else
|
||
(
|
||
-- Use the appropriate batch file
|
||
local batFilename = buildDir
|
||
if (lstPlatforms.selected == "PS3") then
|
||
(
|
||
batFilename += ps3BatchFile
|
||
)
|
||
else
|
||
(
|
||
batFilename += xboxBatchFile
|
||
)
|
||
|
||
-- Build up the commandline args
|
||
local arguments = defaultCommandlineArgs
|
||
|
||
-- Create the intput sector tool files.
|
||
local inputFilepath = buildDir + inputFilename
|
||
CreateSectorInputFile (GetBounds()) inputFilepath
|
||
|
||
local outputDir = lblOutputDir.text
|
||
arguments += " -sectortools_input \"" + inputFilepath + "\" -sectortools_output \"" + outputDir + "\""
|
||
|
||
-- Create the entities file (if required).
|
||
if (lstSweepMode.selected == "Selected Entities") then
|
||
(
|
||
local entitiesFilepath = buildDir + entitiesFilename
|
||
CreateEntityGuidFile (GetSelectedEntityGuids()) entitiesFilepath
|
||
|
||
arguments += " -sectortools_entities \"" + entitiesFilepath + "\""
|
||
)
|
||
|
||
-- Create the arugments response file.
|
||
local responseFilepath = buildDir + responseFilename
|
||
CreateResponseFile arguments responseFilepath
|
||
|
||
-- If it's 360, check to see whether watson is running and kill it.
|
||
if (lstPlatforms.selected == "Xbox360") then
|
||
(
|
||
local xboxSDKDir = "%xedk%\bin\win32"
|
||
local cmd = ( "TASKKILL /F /IM \"xbwatson.exe\"" )
|
||
doscommand cmd
|
||
)
|
||
|
||
-- Kick off the game with those files as inputs.
|
||
PrintDebug (format "Running: % %" batFilename arguments)
|
||
local gameBatchProcess = RsProcess()
|
||
gameBatchProcess.RunProcess batFilename ("@" + responseFilepath) silent:false waitForExit:false workingDir:buildDir
|
||
|
||
btnStart.Enabled = false
|
||
|
||
-- Inform the user of what is happening
|
||
progressStart "Establishing game connection (waiting for game to boot)."
|
||
|
||
-- Times in seconds
|
||
local timePassed = 0
|
||
local maxTimeToWait = 30
|
||
local checkFrequency = 0.25
|
||
local waitingForConnection = true
|
||
|
||
while (waitingForConnection == true) do
|
||
(
|
||
-- Offer to cancel if the user has been waiting too long
|
||
if (timePassed >= maxTimeToWait) then
|
||
(
|
||
keepGoing = queryBox "Unable to establish a game connection\n\nContinue waiting?" title:"Warning: Connection Error"
|
||
|
||
-- If the user chooses to continue to wait, reset the timer.
|
||
if keepGoing then
|
||
(
|
||
timePassed = 0
|
||
)
|
||
else
|
||
(
|
||
btnStart.Enabled = true
|
||
waitingForConnection = false
|
||
)
|
||
)
|
||
|
||
if waitingForConnection then
|
||
(
|
||
if (RemoteConnection.IsConnected() == true and IsSweepInProgress() == true) then
|
||
(
|
||
sweepTimer.Start()
|
||
lblSweepStatus.Text = "Sweep in Progress"
|
||
waitingForConnection = false
|
||
)
|
||
else if (RemoteConnection.IsConnected() == false and RemoteConnection.Connect() == true) then
|
||
(
|
||
lblConnectionStatus.Text = "Connected"
|
||
connectionTimer.Start()
|
||
)
|
||
else
|
||
(
|
||
if not (progressUpdate (100.0 * timePassed / maxTimeToWait)) then
|
||
(
|
||
btnStart.Enabled = true
|
||
waitingForConnection = false
|
||
)
|
||
else
|
||
(
|
||
sleep checkFrequency
|
||
timePassed += checkFrequency
|
||
)
|
||
)
|
||
)
|
||
)
|
||
|
||
progressEnd()
|
||
)
|
||
)
|
||
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- Events
|
||
-------------------------------------------------------------------------------
|
||
|
||
-- Tick called at regular intervals to check the game connection.
|
||
fn ConnectionTimer_OnTick s e =
|
||
(
|
||
if (RemoteConnection.IsConnected() == false) then
|
||
(
|
||
--If connection has faltered, then notify the UI.
|
||
PrintDebug ("Lost connection to the game!")
|
||
|
||
if (btnStart.Enabled == false) then
|
||
(
|
||
btnStart.Enabled = true
|
||
lblSweepStatus.Text = "Unknown"
|
||
sweepTimer.Stop()
|
||
)
|
||
|
||
lblConnectionStatus.Text = "Lost connection."
|
||
connectionTimer.Stop()
|
||
)
|
||
)
|
||
|
||
-- Tick called at regular intervals to check whether the object position checker has completed.
|
||
fn SweepTimer_OnTick s e =
|
||
(
|
||
if (btnStart.Enabled == false) then
|
||
(
|
||
-- Check whether the sweep's completed.
|
||
if (IsSweepInProgress() == false) then
|
||
(
|
||
btnStart.Enabled = true
|
||
lblSweepStatus.Text = "Sweep Completed"
|
||
sweepTimer.Stop()
|
||
)
|
||
)
|
||
)
|
||
|
||
-- Called when the rollout is opened.
|
||
on GtaObjectPosCheckerInitiatorRoll open do
|
||
(
|
||
-- Get the projects build directory.
|
||
buildDir = ( RsConfigGetProjRootDir() + "build/dev/" )
|
||
|
||
-- Initialise the two timers
|
||
connectionTimer = dotNetObject "System.Windows.Forms.Timer"
|
||
dotnet.addEventHandler connectionTimer "tick" ConnectionTimer_OnTick
|
||
connectionTimer.Interval = connectionTimerInterval
|
||
|
||
sweepTimer = dotNetObject "System.Windows.Forms.Timer"
|
||
dotnet.addEventHandler sweepTimer "tick" SweepTimer_OnTick
|
||
sweepTimer.Interval = sweepTimerInterval
|
||
|
||
-- Attempt to connect to the game.
|
||
if (RemoteConnection.Connect() == true) then
|
||
(
|
||
lblConnectionStatus.Text = "Connected"
|
||
connectionTimer.Start()
|
||
|
||
-- Is a sweep already in progress?
|
||
if (IsSweepInProgress() == true) then
|
||
(
|
||
sweepTimer.Start()
|
||
btnStart.Enabled = false
|
||
lblSweepStatus.Text = "Sweep in Progress"
|
||
)
|
||
)
|
||
)
|
||
|
||
-- Called when the rollout closes.
|
||
on GtaObjectPosCheckerInitiatorRoll close do
|
||
(
|
||
sweepTimer.Stop()
|
||
connectionTimer.Stop()
|
||
)
|
||
|
||
-- Called when the user wishes to browse for the output directory.
|
||
on btnBrowse pressed do
|
||
(
|
||
lblOutputDir.Text = getSavePath caption:"Select output directory:" initialDir:lblOutputDir.Text
|
||
)
|
||
|
||
-- Called when the user wants to kick off the object position checker in game.
|
||
on btnStart pressed do
|
||
(
|
||
-- Attempt to connect to the game.
|
||
if (RemoteConnection.Connect() == true) then
|
||
(
|
||
lblConnectionStatus.Text = "Connected"
|
||
connectionTimer.Start()
|
||
)
|
||
|
||
-- Check whether the game is already running.
|
||
if (IsGameRunningWithSectorTools() == true) then
|
||
(
|
||
-- Is a sweep already in progress?
|
||
if (IsSweepInProgress() == true) then
|
||
(
|
||
-- Tell the user to wait for the current sweep to complete.
|
||
messageBox "Object position check sweep already in progress.\nEither abort it or wait for it to complete before kicking off another one." title:"Sweep in Progress"
|
||
|
||
sweepTimer.Start()
|
||
btnStart.Enabled = false
|
||
lblSweepStatus.Text = "Sweep in Progress"
|
||
)
|
||
else
|
||
(
|
||
PrintDebug "Using existing game"
|
||
StartObjectPositionChecker()
|
||
)
|
||
)
|
||
else
|
||
(
|
||
PrintDebug "Booting game"
|
||
BootAndStartObjectPositionChecker()
|
||
)
|
||
)
|
||
|
||
-- Called when the user hits the abort button.
|
||
on btnAbort pressed do
|
||
(
|
||
if (btnStart.Enabled == false) then
|
||
(
|
||
PrintDebug "Aborting sweep"
|
||
RemoteConnection.SendCommand("widget \"" + RagObjPosCheckerAbort + "\"")
|
||
|
||
btnStart.Enabled = true
|
||
lblSweepStatus.Text = "Aborted"
|
||
sweepTimer.Stop()
|
||
)
|
||
)
|
||
)
|
||
|
||
|
||
|
||
|
||
rollout GtaObjectPosCheckerImportRoll "Object Position Checker Import"
|
||
(
|
||
-------------------------------------------------------------------------------
|
||
-- Structures
|
||
-------------------------------------------------------------------------------
|
||
|
||
struct ObjectCheckerDef
|
||
(
|
||
objName, -- Object name (string)
|
||
initialPos, -- Initial position of object (Point3)
|
||
initialRot, -- Initial rotation of object (Quat)
|
||
finalPos, -- Final position of object (Point3)
|
||
finalRot, -- Final rotation of object (Quat)
|
||
|
||
-- Function to compare two ObjectCheckerDef structs
|
||
fn cmp o1 o2 =
|
||
(
|
||
stricmp o1.objName o2.objName
|
||
)
|
||
)
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- constants
|
||
-------------------------------------------------------------------------------
|
||
|
||
local degThreshold = 1.0
|
||
local useFullMatrixIdx = GetAttrIndex "Gta Object" "Use Full Matrix"
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- Data
|
||
-------------------------------------------------------------------------------
|
||
|
||
local posZero = [ 0.0, 0.0, 0.0 ]
|
||
local checkedObjects = #()
|
||
local checkedObjectIndices = #() -- checkedObjects item indices shown
|
||
local checkedObjectsInMap = #()
|
||
|
||
-- Autorun stuff
|
||
local mode = 0 -- [0 == normal, 1 == autorun]
|
||
local autoRunObjIdx = undefined
|
||
|
||
-- Used to draw the extents of the objchk file
|
||
local rectangleArray = #()
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- UI
|
||
-------------------------------------------------------------------------------
|
||
hyperlink lnkHelp "Help?" address:"https://devstar.rockstargames.com/wiki/index.php/Physical_Object_Position_Checker" align:#right color:(color 0 0 255) hoverColor:(color 0 0 255) visitedColor:(color 0 0 255)
|
||
button btnImport "Import File..." across:6
|
||
button btnImportDir "Import Directory..."
|
||
button btnAutoRun "Auto-Run..."
|
||
button btnCheckAll "Check All"
|
||
button btnInvChecked "Invert Checked"
|
||
button btnFindObject "Find Checked"
|
||
button btnFindSelObject "Find Selected Object"
|
||
dotNetControl lstViewObjects "System.Windows.Forms.ListView" width:180 height:525 across:2
|
||
dotNetControl lstView "System.Windows.Forms.ListView" width:400 height:125 offset:[-100,0]
|
||
groupbox grpAutoRun "Auto-Run" width:555 height:55
|
||
label lblInfo "" align:#left offset:[5,-45]
|
||
button btnInitial "Initial" enabled:false width:100 across:5
|
||
button btnFinal "Final" enabled:false width:100
|
||
button btnBack "Back" enabled:false width:100
|
||
button btnNext "Next" enabled:false width:100
|
||
button btnStop "Stop" enabled:false width:100
|
||
progressbar barProgress across:2 offset:[0,10]
|
||
button btnClose "Close" align:#right width:100 offset:[0,10]
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- Functions
|
||
-------------------------------------------------------------------------------
|
||
|
||
local fEpsilon = 0.01
|
||
|
||
fn PosEqual a b = (
|
||
|
||
if ( a.x <= ( b.x + fEpsilon ) and a.x >= ( b.x - fEpsilon ) ) then
|
||
if ( a.y <= ( b.y + fEpsilon ) and a.y >= ( b.y - fEpsilon ) ) then
|
||
if ( a.z <= ( b.z + fEpsilon ) and a.z >= ( b.z - fEpsilon ) ) then
|
||
return true
|
||
|
||
return false
|
||
)
|
||
|
||
fn RefreshTreeView = (
|
||
|
||
checkedObjectIndices = #()
|
||
|
||
local i = 1
|
||
local n = 0
|
||
|
||
lvops.ClearLvItems lstView
|
||
lvops.ClearLvItems lstViewObjects
|
||
lstViewObjects.BeginUpdate()
|
||
|
||
for o in checkedObjects do
|
||
(
|
||
barProgress.value = 100.0 * i / checkedObjects.count
|
||
i += 1
|
||
n += 1
|
||
|
||
lstViewObjects.Items.Add o.objName
|
||
append checkedObjectIndices n
|
||
)
|
||
|
||
lstViewObjects.EndUpdate()
|
||
|
||
-- DEBUG
|
||
--print checkedObjectIndices
|
||
--print checkedObjects
|
||
)
|
||
|
||
fn RefreshListView o = (
|
||
|
||
lstView.BeginUpdate()
|
||
|
||
local xdiff = ( o.initialPos.x - o.finalPos.x ) as string
|
||
local ydiff = ( o.initialPos.y - o.finalPos.y ) as string
|
||
local zdiff = ( o.initialPos.z - o.finalPos.z ) as string
|
||
|
||
lvops.ClearLvItems lstView
|
||
lvops.AddLvItem lstView pTextItems:#( "1. Position Difference", "[ " + xdiff + ", " + ydiff + ", " + zdiff + " ]" )
|
||
lvops.AddLvItem lstView pTextItems:#( "2. Initial Position", o.initialPos as string )
|
||
lvops.AddLvItem lstView pTextItems:#( "3. Final Position", o.finalPos as string )
|
||
lvops.AddLvItem lstView pTextItems:#( "4. Initial Orientation", o.initialRot as string )
|
||
lvops.AddLvItem lstView pTextItems:#( "5. Final Orientation", o.finalRot as string )
|
||
|
||
lstView.EndUpdate()
|
||
)
|
||
|
||
fn GetCheckedObjects = (
|
||
|
||
local items = ( lvops.GetLvItems lstViewObjects )
|
||
objDefs = #()
|
||
|
||
for o in items do
|
||
(
|
||
|
||
local idx = checkedObjectIndices[o.index+1]
|
||
|
||
-- DEBUG
|
||
--format "Item % checked: %; real item index: %\n" o.index (lvops.GetLvItemCheck lstView o.index ) idx
|
||
|
||
if ( lvops.GetLvItemCheck lstViewObjects o.index ) then
|
||
(
|
||
-- DEBUG
|
||
-- format "Item % is checked, object: %\n" o.index checkedObjects[idx]
|
||
append objDefs checkedObjects[idx]
|
||
)
|
||
)
|
||
|
||
objDefs
|
||
)
|
||
|
||
-- Build object definition cache for loaded objects
|
||
fn BuildCache = (
|
||
|
||
checkedObjectsInMap = #()
|
||
|
||
-- Ensure our list is initialised undefined for all
|
||
for co in checkedObjects do
|
||
(
|
||
append checkedObjectsInMap undefined
|
||
)
|
||
|
||
-- Loop through all objects trying to find the checkedObjects
|
||
local numObjects = $objects.count
|
||
local i = 0
|
||
|
||
for o in $objects do
|
||
(
|
||
|
||
barProgress.value = 100.0 * i / numObjects
|
||
i += 1
|
||
|
||
if ( "Gta Object" != GetAttrClass o ) then
|
||
continue
|
||
|
||
local objName = undefined
|
||
objName = if ( isRefObj o ) then ( toLower o.objectName ) else ( toLower o.name )
|
||
|
||
-- Loop through all checkedObjects
|
||
local nCheckedObj = 1
|
||
for co in checkedObjects do
|
||
(
|
||
if ( objName == (toLower co.objName) ) and ( PosEqual o.pos co.initialPos ) then
|
||
(
|
||
checkedObjectsInMap[nCheckedObj] = o
|
||
break
|
||
)
|
||
|
||
nCheckedObj += 1
|
||
)
|
||
)
|
||
|
||
print checkedObjectsInMap
|
||
)
|
||
|
||
fn Import filename = (
|
||
|
||
-- Open file and process lines, constructing checkedObjects data
|
||
if ( undefined != filename ) then
|
||
(
|
||
fp = openFile filename mode:"rt"
|
||
|
||
local rectMin, rectMax, rectCentre
|
||
|
||
while ( not ( eof fp ) ) do
|
||
(
|
||
local dataLine = readLine fp
|
||
|
||
-- Skip empty lines
|
||
if ( 0 == dataLine.count ) then
|
||
continue
|
||
|
||
if ( matchPattern dataLine pattern:"# Actual Player Pos*" ) then
|
||
(
|
||
centreArray = filterString (filterString dataLine ":")[2] " "
|
||
rectCentre = Point3 (centreArray[1] as float) (centreArray[2] as float) (centreArray[3] as float)
|
||
continue
|
||
)
|
||
if ( matchPattern dataLine pattern:"# Sector Min Coords*" ) then
|
||
(
|
||
minArray = filterString (filterString dataLine ":")[2] " "
|
||
rectMin = Point2 (minArray[1] as float) (minArray[2] as float)
|
||
continue
|
||
)
|
||
if ( matchPattern dataLine pattern:"# Sector Max Coords*" ) then
|
||
(
|
||
maxArray = filterString (filterString dataLine ":")[2] " "
|
||
rectMax = Point2 (maxArray[1] as float) (maxArray[2] as float)
|
||
continue
|
||
)
|
||
|
||
-- Skip comment "#" lines
|
||
if ( matchPattern dataLine pattern:"#*" ) then
|
||
continue
|
||
|
||
-- Filter data line as comma separated data
|
||
local filtLine = filterString dataLine ","
|
||
local posA = [ filtLine[2] as float, filtLine[3] as float, filtLine[4] as float ]
|
||
local posB = [ filtLine[5] as float, filtLine[6] as float, filtLine[7] as float ]
|
||
local rotA = undefined
|
||
local rotB = undefined
|
||
if ( filtLine.count > 7 ) then
|
||
(
|
||
rotA = quat (filtLine[8] as float) (filtLine[9] as float) (filtLine[10] as float) (filtLine[11] as float)
|
||
rotB = quat (filtLine[12] as float) (filtLine[13] as float) (filtLine[14] as float) (filtLine[15] as float)
|
||
)
|
||
|
||
obj = ObjectCheckerDef objName:filtLine[1] initialPos:posA initialRot:rotA finalPos:posB finalRot:rotB
|
||
append checkedObjects obj
|
||
|
||
)
|
||
|
||
if ( rectMin != undefined and rectMax != undefined and rectCentre != undefined ) do
|
||
(
|
||
r = Rectangle length:(abs (rectMax.x - rectMin.x)) width:(abs (rectMax.y - rectMin.y)) pos:rectCentre
|
||
append rectangleArray r
|
||
)
|
||
|
||
close fp
|
||
)
|
||
)
|
||
|
||
-- Find previous checked object
|
||
fn FindBackCheckedObject currIdx = (
|
||
|
||
local items = ( lvops.GetLvItems lstViewObjects )
|
||
local idx = currIdx
|
||
|
||
for o in items do
|
||
(
|
||
|
||
if ( o.index >= currIdx ) then
|
||
continue
|
||
|
||
if ( not o.checked ) then
|
||
continue
|
||
|
||
-- Skip objects not in our map section
|
||
if ( undefined == checkedObjectsInMap[o.index+1] ) then
|
||
continue
|
||
|
||
idx = o.index
|
||
)
|
||
idx
|
||
)
|
||
|
||
-- Find next checked object
|
||
fn FindNextCheckedObject currIdx = (
|
||
|
||
local items = ( lvops.GetLvItems lstViewObjects )
|
||
|
||
for o in items do
|
||
(
|
||
|
||
if ( o.index <= currIdx ) then
|
||
continue
|
||
|
||
if ( not o.checked ) then
|
||
continue
|
||
|
||
-- Skip objects not in our map section
|
||
if ( undefined == checkedObjectsInMap[o.index+1] ) then
|
||
continue
|
||
|
||
return ( o.index )
|
||
)
|
||
undefined
|
||
)
|
||
|
||
fn UpdateInfoLabel = (
|
||
|
||
local ss = stringStream ""
|
||
format "Object % of %." autoRunObjIdx checkedObjects.count to:ss
|
||
lblInfo.text = ( ss as string )
|
||
)
|
||
|
||
-------------------------------------------------------------------------------
|
||
-- Events
|
||
-------------------------------------------------------------------------------
|
||
|
||
-- Dialog open event
|
||
on GtaObjectPosCheckerImportRoll open do
|
||
(
|
||
|
||
-- Initialise TreeView
|
||
lvops.InitListView lstViewObjects pLabelEdit:false pAllowReorder:false pCheckBoxes:true
|
||
lvops.AddLvColumnHeader lstViewObjects pCaption:"Object" pWidth:150
|
||
|
||
-- Initialise ListView
|
||
lvops.InitListView lstView pLabelEdit:false pAllowReorder:false pCheckBoxes:false
|
||
lvops.AddLvColumnHeader lstView pCaption:"Parameter" pWidth:120
|
||
lvops.AddLvColumnHeader lstView pCaption:"Value" pWidth:260
|
||
)
|
||
|
||
on GtaObjectPosCheckerImportRoll close do
|
||
(
|
||
for o in rectangleArray do ( if classof o == Rectangle then delete o )
|
||
rectangleArray = #()
|
||
)
|
||
|
||
-- Import File Button Event Handler
|
||
on btnImport pressed do
|
||
(
|
||
for o in rectangleArray do ( if classof o == Rectangle then delete o )
|
||
rectangleArray = #()
|
||
-- Initialisation
|
||
fileName = getOpenFileName caption:"Select Object Checker output file:" \
|
||
types:"Object Checker Sector Files (*.objchk)|*.objchk|All Files (*.*)|*.*"
|
||
checkedObjectIndices = #()
|
||
checkedObjects = #()
|
||
|
||
Import fileName
|
||
|
||
-- Before refreshing our view, lets sort out objects so our view indicies
|
||
-- match when they are sorted in the damn listview.
|
||
qsort checkedObjects ObjectCheckerDef.cmp
|
||
|
||
BuildCache()
|
||
|
||
RefreshTreeView()
|
||
|
||
local ss = stringStream ""
|
||
format "% objects loaded." checkedObjects.count to:ss
|
||
lblInfo.text = ( ss as string )
|
||
)
|
||
|
||
-- Import Directory Button Event Handler
|
||
on btnImportDir pressed do
|
||
(
|
||
for o in rectangleArray do ( if classof o == Rectangle then delete o )
|
||
rectangleArray = #()
|
||
|
||
-- Initialisation
|
||
fileName = getOpenFileName caption:"Select Object Checker output file:" \
|
||
types:"Object Checker Sector Files (*.objchk)|*.objchk|All Files (*.*)|*.*"
|
||
checkedObjectIndices = #()
|
||
checkedObjects = #()
|
||
|
||
local filepath = RsRemoveFile fileName
|
||
local files = getFiles ( filepath + "\\*.objchk" )
|
||
|
||
for file in files do
|
||
Import file
|
||
|
||
-- Before refreshing our view, lets sort out objects so our view indicies
|
||
-- match when they are sorted in the damn listview.
|
||
qsort checkedObjects ObjectCheckerDef.cmp
|
||
|
||
BuildCache()
|
||
|
||
RefreshTreeView()
|
||
|
||
local ss = stringStream ""
|
||
format "% objects loaded." checkedObjects.count to:ss
|
||
lblInfo.text = ( ss as string )
|
||
)
|
||
|
||
-- On autorun start do
|
||
on btnAutoRun pressed do
|
||
(
|
||
if ( 0 != mode ) then
|
||
return false
|
||
|
||
-- Setup first object
|
||
autoRunObjIdx = FindNextCheckedObject -1
|
||
|
||
if ( undefined == autoRunObjIdx ) then
|
||
(
|
||
MessageBox "No objects in loaded list are in this map section."
|
||
return false
|
||
)
|
||
|
||
-- Initialise UI controls for AutoRun Mode
|
||
mode = 1
|
||
|
||
btnInitial.enabled = true
|
||
btnFinal.enabled = true
|
||
btnNext.enabled = true
|
||
btnBack.enabled = true
|
||
btnStop.enabled = true
|
||
|
||
-- Select object and zoom into it
|
||
clearSelection()
|
||
selectMore checkedObjectsInMap[autoRunObjIdx+1]
|
||
max zoomext sel
|
||
redrawViews()
|
||
|
||
UpdateInfoLabel()
|
||
)
|
||
|
||
-- On node click
|
||
on lstViewObjects ItemSelectionChanged e do
|
||
(
|
||
-- Find objectDef struct this node references
|
||
local idx = e.itemindex + 1
|
||
|
||
RefreshListView checkedObjects[idx]
|
||
)
|
||
|
||
-- Check All Button Event Handler
|
||
on btnCheckAll pressed do
|
||
(
|
||
|
||
for i = 0 to lstViewObjects.Items.Count-1 do
|
||
(
|
||
lvops.SetLvItemCheck lstViewObjects i true
|
||
)
|
||
)
|
||
|
||
-- Invert checked status Button Event Handler
|
||
on btnInvChecked pressed do
|
||
(
|
||
|
||
for i = 0 to lstViewObjects.Items.Count-1 do
|
||
(
|
||
lvops.SetLvItemCheck lstViewObjects i ( not ( lvops.GetLvItemCheck lstViewObjects i ) )
|
||
)
|
||
)
|
||
|
||
-- Find Checked object(s)
|
||
on btnFindObject pressed do
|
||
(
|
||
|
||
local items = ( lvops.GetLvItems lstViewObjects )
|
||
clearSelection()
|
||
|
||
for o in items do
|
||
(
|
||
|
||
if ( lvops.GetLvItemCheck lstViewObjects o.index ) then
|
||
(
|
||
if ( undefined != checkedObjectsInMap[o.index+1] ) then
|
||
selectMore checkedObjectsInMap[o.index+1]
|
||
)
|
||
)
|
||
if ( 0 == $selection.count ) then
|
||
MessageBox "No objects found. They will be in a different map section."
|
||
else
|
||
max zoomext sel
|
||
)
|
||
|
||
on btnFindSelObject pressed do
|
||
(
|
||
if ( selection.count == 1 ) do
|
||
(
|
||
objNameSelected = toLower $.objectName
|
||
objPosSelected = $.pos
|
||
|
||
for idx = 1 to checkedObjects.count do
|
||
(
|
||
-- Might find multiple matches, so then check the positions to make sure right one found
|
||
if (objNameSelected == (toLower checkedObjects[idx].objName)) do
|
||
(
|
||
if (PosEqual objPosSelected checkedObjects[idx].initialPos ) do
|
||
(
|
||
lvops.SetLvItemCheck lstViewObjects (idx - 1) true
|
||
RefreshListView checkedObjects[idx]
|
||
)
|
||
)
|
||
)
|
||
)
|
||
)
|
||
|
||
-- Close Button Event Handler
|
||
on btnClose pressed do
|
||
(
|
||
closeRolloutFloater GtaObjectPosCheckerImportUtil
|
||
)
|
||
|
||
-----------------------------------------------------------------------------
|
||
-- Auto-Run Mode Event Handlers
|
||
-----------------------------------------------------------------------------
|
||
|
||
on btnInitial pressed do
|
||
(
|
||
if ( 1 != mode ) then
|
||
return false
|
||
|
||
in coordsys world
|
||
(
|
||
-- DEBUG
|
||
--format "Moving to %\n\tobject def: %\n" checkedObjects[autoRunObjIdx+1].initialPos checkedObjects[autoRunObjIdx+1]
|
||
|
||
local orientMat = inverse( checkedObjects[autoRunObjIdx+1].initialRot ) as Matrix3
|
||
orientMat.translation = checkedObjects[autoRunObjIdx+1].initialPos
|
||
checkedObjectsInMap[autoRunObjIdx+1].transform = orientMat
|
||
-- DEBUG
|
||
--local angles = quatToEuler ( inverse( checkedObjects[autoRunObjIdx+1].finalRot ) )
|
||
--format "Object %\n\tX Rotation: %\n\tY Rotation:%\n\tZ Rotation: %\n\n" checkedObjects[autoRunObjIdx+1].objName angles.x angles.y angles.z
|
||
)
|
||
)
|
||
|
||
on btnFinal pressed do
|
||
(
|
||
if ( 1 != mode ) then
|
||
return false
|
||
|
||
in coordsys world
|
||
(
|
||
-- DEBUG
|
||
--format "Moving to %\n\tobject def: %\n" checkedObjects[autoRunObjIdx+1].finalPos checkedObjects[autoRunObjIdx+1]
|
||
|
||
local orientMat = inverse( checkedObjects[autoRunObjIdx+1].finalRot ) as Matrix3
|
||
orientMat.translation = checkedObjects[autoRunObjIdx+1].finalPos
|
||
checkedObjectsInMap[autoRunObjIdx+1].transform = orientMat
|
||
|
||
local angles = quatToEuler ( inverse( checkedObjects[autoRunObjIdx+1].finalRot ) )
|
||
-- DEBUG
|
||
--format "Object %\n\tX Rotation: %\n\tY Rotation:%\n\tZ Rotation: %\n\n" checkedObjects[autoRunObjIdx+1].objName angles.x angles.y angles.z
|
||
|
||
-- If our X or Y angle is greater than our threshold then set our Use Full Matrix attribute
|
||
if ( angles.x > degThreshold ) or ( angles.y > degThreshold ) or
|
||
( angles.x < ( -degThreshold ) ) or ( angles.y < ( -degThreshold ) ) then
|
||
SetAttr checkedObjectsInMap[autoRunObjIdx+1] useFullMatrixIdx true
|
||
)
|
||
)
|
||
|
||
on btnBack pressed do
|
||
(
|
||
if ( 1 != mode ) then
|
||
return false
|
||
|
||
autoRunObjIdx = FindBackCheckedObject autoRunObjIdx
|
||
|
||
-- Select object and zoom into it
|
||
clearSelection()
|
||
selectMore checkedObjectsInMap[autoRunObjIdx+1]
|
||
max zoomext sel
|
||
redrawViews()
|
||
|
||
UpdateInfoLabel()
|
||
)
|
||
|
||
on btnNext pressed do
|
||
(
|
||
if ( 1 != mode ) then
|
||
return false
|
||
|
||
autoRunObjIdx = FindNextCheckedObject autoRunObjIdx
|
||
|
||
if ( undefined == autoRunObjIdx ) then
|
||
(
|
||
MessageBox "All objects processed."
|
||
|
||
autoRunObjIdx = undefined
|
||
mode = 0
|
||
|
||
btnInitial.enabled = false
|
||
btnFinal.enabled = false
|
||
btnBack.enabled = false
|
||
btnNext.enabled = false
|
||
btnStop.enabled = false
|
||
|
||
local ss = stringStream ""
|
||
format "% objects loaded." checkedObjects.count to:ss
|
||
lblInfo.text = ( ss as string )
|
||
|
||
return false
|
||
)
|
||
|
||
-- Select object and zoom into it
|
||
clearSelection()
|
||
selectMore checkedObjectsInMap[autoRunObjIdx+1]
|
||
max zoomext sel
|
||
redrawViews()
|
||
|
||
UpdateInfoLabel()
|
||
)
|
||
|
||
on btnStop pressed do
|
||
(
|
||
if ( 1 != mode ) then
|
||
return false
|
||
|
||
autoRunObjIdx = undefined
|
||
mode = 0
|
||
|
||
btnInitial.enabled = false
|
||
btnFinal.enabled = false
|
||
btnBack.enabled = false
|
||
btnNext.enabled = false
|
||
btnStop.enabled = false
|
||
|
||
local ss = stringStream ""
|
||
format "% objects loaded." checkedObjects.count to:ss
|
||
lblInfo.text = ( ss as string )
|
||
)
|
||
)
|
||
|
||
try closeRolloutFloater GtaObjectPosCheckerImportUtil catch()
|
||
GtaObjectPosCheckerImportUtil = newRolloutFloater "Physical Object Position Checker" 600 730 0 50
|
||
addRollout GtaObjectPosCheckerInitiatorRoll GtaObjectPosCheckerImportUtil
|
||
addRollout GtaObjectPosCheckerImportRoll GtaObjectPosCheckerImportUtil
|
||
|
||
-- End of GtaObjectPositionChecker.ms MAXScript
|