495 lines
14 KiB
Plaintext
Executable File
495 lines
14 KiB
Plaintext
Executable File
-- Rockstar mloPortal Object
|
|
-- Rockstar North
|
|
-- 14/10/2005
|
|
-- by Greg Smith
|
|
--
|
|
--
|
|
-- 12/03/2007 -- DHM --
|
|
-- Add multiple portal objects parameter block and associated UI
|
|
-- Add version update event handler to cope with this change
|
|
-- Remove "Door/Window Object" rollout
|
|
--
|
|
|
|
-- 04/03/2010 -- Luke Openshaw
|
|
-- Check for rollout initialisation before accessing pick button rollout controls
|
|
-- or Max 2010 will crash like the piece of shit that it is
|
|
--
|
|
-- 28/02/2012 -- Matt Harrad
|
|
-- Added set size from scale function and button.
|
|
--
|
|
-- helper object for milo portal setup
|
|
--
|
|
|
|
global RsAudioOcclusionFile = RsMakeSafeSlashes (RsProjectGetAudioDir() + "/assets/Objects/Core/Audio/GAMEOBJECTS/ENVIRONMENT/PORTAL_SETTINGS.XML")
|
|
global gRsExtraAudioOccluderFile = RsMakeSafeSlashes (RsConfigGetExtraAudioOcclusionDir())
|
|
global RsSyncAudioOcclusion = true
|
|
|
|
plugin helper GtaMloPortal
|
|
name:"Gta MloPortal"
|
|
classID:#(0xa697b45, 0x197567c7)
|
|
category:"Gta"
|
|
extends:RSGrid
|
|
version:2
|
|
(
|
|
parameters extraParams rollout:extraRollout
|
|
(
|
|
AudioOcclusionHash type:#string
|
|
)
|
|
|
|
rollout extraRollout "Audio Occlusion"
|
|
(
|
|
hyperlink hlp "Help" color:blue address:"https://devstar.rockstargames.com/wiki/index.php/Milo_and_Portals#Audio_Occluders" align:#right
|
|
dotnetControl edtAudioOccl "Combobox" height:25 width:154 offset:[-8,0]
|
|
button btnUpdateAudioFile "Update Occluder List"
|
|
group "Add Occluder Info"(
|
|
edittext txtFolder "Folder"
|
|
spinner txtValue "Occ Value" range:[0,1,0] scale:0.01 indeterminate:true
|
|
button btnAddAudOccl "Add new Occluder to XML"
|
|
)
|
|
fn UpdateAudioFile =
|
|
(
|
|
-- Update global path to extraOccluder xml
|
|
gRsExtraAudioOccluderFile = RsMakeSafeSlashes (RsConfigGetExtraAudioOcclusionDir())
|
|
|
|
local occlFiles = #(RsAudioOcclusionFile)
|
|
edtAudioOccl.Items.Clear()
|
|
|
|
if RsIsDLCProj() and (RsFileExists gRsExtraAudioOccluderFile) then
|
|
(
|
|
append occlFiles gRsExtraAudioOccluderFile
|
|
)
|
|
|
|
local occluderItems = #("")
|
|
|
|
for i = 1 to occlFiles.count do(
|
|
|
|
if RsSyncAudioOcclusion and i == 1 do
|
|
(
|
|
gRsPerforce.sync occlFiles silent:true
|
|
RsSyncAudioOcclusion = false
|
|
)
|
|
|
|
|
|
if RsFileExists occlFiles[i] then
|
|
(
|
|
local docNav = dotnetobject "System.Xml.XPath.XPathDocument" occlFiles[i]
|
|
local nav = docNav.CreateNavigator()
|
|
local strExpression = dotnetobject "System.String" "//PortalSettings/@name"
|
|
local NodeIter = nav.Select strExpression
|
|
while (NodeIter.MoveNext()) do
|
|
(
|
|
append occluderItems NodeIter.Current.Value
|
|
)
|
|
|
|
)
|
|
else
|
|
(
|
|
gRsUlog.LogWarning ("No audio occlusion file found at \""+occlFiles[i]+"\"") quiet:false
|
|
if undefined!=AudioOcclusionHash and ""!=AudioOcclusionHash then
|
|
(
|
|
local occluderItems = #("", AudioOcclusionHash)
|
|
edtAudioOccl.items.addrange occluderItems
|
|
edtAudioOccl.SelectedIndex = 1
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
)
|
|
edtAudioOccl.items.addrange occluderItems
|
|
local itemIndex = findItem occluderItems AudioOcclusionHash
|
|
if 0!=itemIndex then( edtAudioOccl.SelectedIndex = (itemIndex-1) )
|
|
|
|
AudioOcclusionHash = edtAudioOccl.Text
|
|
|
|
txtFolder.text = ""
|
|
txtValue.value = 0
|
|
|
|
)
|
|
|
|
on btnAddAudOccl pressed do (
|
|
|
|
local newOcclName = edtAudioOccl.Text
|
|
format "edtAudioOccl.Text: %\n" (edtAudioOccl.Text as string)
|
|
|
|
for i = 0 to edtAudioOccl.Items.Count - 1 do (
|
|
if (edtAudioOccl.Items.Item i) == newOcclName then return false
|
|
)
|
|
|
|
local occlFile = RsAudioOcclusionFile
|
|
format "occlFile: %\n" (occlFile as string)
|
|
|
|
-- Create a new file if dlc specific one doesn't exist.
|
|
if(RsIsDLCProj()) then (
|
|
|
|
occlFile = gRsExtraAudioOccluderFile
|
|
|
|
if not (RsFileExists gRsExtraAudioOccluderFile) then (
|
|
local xdoc = XmlDocument()
|
|
xdoc.Init()
|
|
elem = xdoc.createelement "Objects" appendTo:xdoc.document
|
|
xdoc.save occlFile
|
|
xdoc.Release()
|
|
|
|
)
|
|
)
|
|
|
|
|
|
if((newOcclName != "") and (RsFileExists occlFile)) then(
|
|
|
|
if not (gRsPerforce.readOnlyP4Check #(occlFile)) then return false
|
|
local clnum = gRsPerforce.createChangelist ("Adding '" + newOcclName + "' to audio occluders")
|
|
gRsPerforce.add_or_edit #(occlFile) queueAdd:true
|
|
gRsPerforce.addToChangelist clnum #(occlFile)
|
|
try(
|
|
local doc = XmlDocument()
|
|
doc.load occlFile
|
|
obj = RsGetXMLElement doc.document "Objects"
|
|
|
|
local occls = #()
|
|
for i = 0 to ((obj.childnodes.count) - 1) do(
|
|
append occls (((obj.childnodes.Item i).Attributes.item 0).Value)
|
|
)
|
|
|
|
local found = false
|
|
for o in occls while not found do(
|
|
found = (o == newOcclName)
|
|
)
|
|
|
|
if(found) then (
|
|
return false
|
|
)else(
|
|
|
|
folderName = txtFolder.text
|
|
maxOcclValue = txtValue.value
|
|
|
|
local att = #(#("name",newOcclName))
|
|
if(folderName != "" and folderName != " ") then (append att #("folder",folderName))
|
|
|
|
ps = doc.createelement "PortalSettings" attrs:att appendTo:obj
|
|
maxOccl = doc.createelement "MaxOcclusion" value:maxOcclValue appendTo:ps
|
|
|
|
doc.save occlFile
|
|
doc.Release()
|
|
AudioOcclusionHash = newOcclName
|
|
gRsPerforce.postExportAdd changelistNum:clnum
|
|
UpdateAudioFile()
|
|
)
|
|
)catch(
|
|
format "Exception: %\n" (getCurrentException())
|
|
stack()
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
)
|
|
on btnUpdateAudioFile pressed do
|
|
(
|
|
UpdateAudioFile()
|
|
)
|
|
|
|
on extraRollout open do
|
|
(
|
|
UpdateAudioFile()
|
|
edtAudioOccl.DropDownWidth = 300
|
|
)
|
|
|
|
on edtAudioOccl SelectedIndexChanged e do
|
|
(
|
|
AudioOcclusionHash = edtAudioOccl.SelectedItem
|
|
)
|
|
)
|
|
|
|
parameters objParams rollout:objRollout
|
|
(
|
|
LinkFirst type:#node ui:btnFirst
|
|
LinkSecond type:#node ui:btnSecond
|
|
)
|
|
rollout objRollout "Rooms"
|
|
(
|
|
fn isRoom obj =
|
|
(
|
|
isKindOf obj GtaMloRoom
|
|
)
|
|
|
|
label lblFirst "1st:" width:18 offset:[-6,0] across:2 align:#left
|
|
pickbutton btnFirst "none" width:124 offset:[4,-3] filter:isRoom autoDisplay:true align:#right
|
|
label lblSecond "2nd:" width:18 offset:[-6,0] across:2 align:#left
|
|
pickbutton btnSecond "none" width:124 offset:[4,-3] filter:isRoom autoDisplay:true align:#right
|
|
label lblRgtClick "(rightclick buttons to clear)" offset:[0,-2]
|
|
label lblAudioOccl "Audio Occlusion:" align:#left
|
|
|
|
button btnFlip "Flip Portal" width:120 offset:[0,8] tooltip:"Flips this portal"
|
|
button btnRetard "Am I Retarded?" width:120 offset:[0,-4] tooltip:"Does this portal probably need to be flipped?"
|
|
button btnSizeFromScale "Set Size From Scale" width:120 tooltip:"Set the size of the portal from its scale."
|
|
|
|
fn CalcPortalVec portalObj =
|
|
(
|
|
portalPt = [ 0.0, 0.0, 5.0 ]
|
|
portalPt = portalPt * portalObj.transform
|
|
portalVec = portalPt - portalObj.position
|
|
portalVec = normalize portalVec
|
|
|
|
return portalVec
|
|
)
|
|
|
|
fn RetardationCheck =
|
|
(
|
|
portalObj = selection[1]
|
|
room1 = portalObj.LinkFirst
|
|
room2 = portalObj.LinkSecond
|
|
|
|
bbMin = [10000, 10000, 10000]
|
|
bbMax = [-10000, -10000, -10000]
|
|
|
|
|
|
if room1 != undefined do
|
|
(
|
|
for roomObj in room1.children do
|
|
(
|
|
if roomObj.position.x < bbMin.x then bbMin.x = roomObj.position.x
|
|
if roomObj.position.y < bbMin.y then bbMin.y = roomObj.position.y
|
|
if roomObj.position.z < bbMin.z then bbMin.z = roomObj.position.z
|
|
|
|
if roomObj.position.x > bbMax.x then bbMax.x = roomObj.position.x
|
|
if roomObj.position.y > bbMax.y then bbMax.y = roomObj.position.y
|
|
if roomObj.position.z > bbMax.z then bbMax.z = roomObj.position.z
|
|
)
|
|
|
|
centroid = (bbMin + bbMax) / 2
|
|
|
|
roomVec = portalObj.position - centroid
|
|
roomVec = normalize roomVec
|
|
|
|
|
|
portalVec = CalcPortalVec portalObj
|
|
|
|
|
|
if (dot portalVec roomVec) < 0 then return true
|
|
|
|
|
|
return false
|
|
)
|
|
|
|
if room2 != undefined do
|
|
(
|
|
for roomObj in room2.children do
|
|
(
|
|
if roomObj.position.x < bbMin.x then bbMin.x = roomObj.position.x
|
|
if roomObj.position.y < bbMin.y then bbMin.y = roomObj.position.y
|
|
if roomObj.position.z < bbMin.z then bbMin.z = roomObj.position.z
|
|
|
|
if roomObj.position.x > bbMax.x then bbMax.x = roomObj.position.x
|
|
if roomObj.position.y > bbMax.y then bbMax.y = roomObj.position.y
|
|
if roomObj.position.z > bbMax.z then bbMax.z = roomObj.position.z
|
|
)
|
|
centroid = (bbMin + bbMax) / 2
|
|
roomVec = portalObj.position - centroid
|
|
roomVec = normalize roomVec
|
|
|
|
portalPt = [ 0.0, 0.0, 5.0 ]
|
|
portalPt = portalPt * portalObj.transform
|
|
portalVec = portalPt - portalObj.position
|
|
portalVec = normalize portalVec
|
|
|
|
|
|
if (dot portalVec roomVec) > 0 then return true
|
|
|
|
|
|
return false
|
|
)
|
|
--else (
|
|
-- messagebox "Portal has no rooms."
|
|
-- )
|
|
|
|
|
|
return true
|
|
)
|
|
|
|
fn sizeFromScale =
|
|
(
|
|
portal = selection[1]
|
|
portal.grid.length = portal.grid.length*portal.scale.y
|
|
portal.grid.width = portal.grid.width*portal.scale.x
|
|
portal.scale = [1,1,1]
|
|
)
|
|
|
|
on btnFirst rightClick do
|
|
(
|
|
this.linkFirst = undefined
|
|
)
|
|
|
|
on btnSecond rightClick do
|
|
(
|
|
this.linkSecond = undefined
|
|
)
|
|
|
|
on btnFlip pressed do
|
|
(
|
|
portalVec = CalcPortalVec selection[1]
|
|
if (dot portalVec [0.0,0.0,1.0]) > 0.95 or (dot portalVec [0.0,0.0,1.0]) < -0.95 then
|
|
(
|
|
rotatePortal = eulerangles 180 0 0
|
|
rotate selection[1] rotatePortal
|
|
)
|
|
else
|
|
(
|
|
rotatePortal = eulerangles 0 0 180
|
|
rotate selection[1] rotatePortal
|
|
)
|
|
)
|
|
|
|
on btnRetard pressed do
|
|
(
|
|
if RetardationCheck() == true then messagebox "This portal is probably reversed. If it does not behave correctly in game, flip it and re-export."
|
|
else messagebox "Portal seems OK."
|
|
)
|
|
|
|
on btnSizeFromScale pressed do sizeFromScale()
|
|
|
|
)
|
|
|
|
-- Unused but maintained for backwards compatibility
|
|
parameters objParams2 --DHM -- rollout:objRollout2
|
|
(
|
|
PortalObject type:#node -- DHM -- ui:btnPortalObject
|
|
|
|
on PortalObject set val do (
|
|
|
|
if objRollout2 == undefined then return false
|
|
|
|
if val != undefined then (
|
|
|
|
objRollout2.btnPortalObject.caption = val.name
|
|
) else(
|
|
|
|
objRollout2.btnPortalObject.caption = "none"
|
|
)
|
|
)
|
|
|
|
)
|
|
|
|
parameters objParams3 rollout:objRollout3
|
|
(
|
|
PortalObjects type:#nodeTab tabSizeVariable:true
|
|
)
|
|
|
|
-- Interface rollout to allow multiple objects to be attached to Mlo Portal objects
|
|
rollout objRollout3 "Door/Window Objects"
|
|
(
|
|
listbox lbObjects "Objects:" height:4 align:#left toolTip:"List of objects attached to the Portal" selection:0
|
|
button btnAdd "Add (by Pick)" width:100 toolTip:"Add a mesh/poly/ref object by clicking here and then picking the object to add from a viewport"
|
|
button btnRemove "Delete" width:100 toolTip:"Select an object in the list and click here to remove the object from the Portal"
|
|
|
|
fn updateListBox =
|
|
(
|
|
-- Populate our listbox
|
|
if (portalObjects != undefined) do
|
|
(
|
|
lbObjects.items = for obj in portalObjects where (obj != undefined) collect obj.name
|
|
)
|
|
)
|
|
|
|
on lbObjects doubleClicked objNum do
|
|
(
|
|
if (objNum != 0) do
|
|
(
|
|
undo "select door/window object" on
|
|
(
|
|
select portalObjects[objNum]
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Rollout open event handler
|
|
on objRollout3 open do
|
|
(
|
|
-- Take this opportunity to get rid of invalid list-items:
|
|
local newArray = for obj in portalObjects where isValidNode obj collect obj
|
|
portalObjects = newArray
|
|
|
|
updateListBox()
|
|
)
|
|
|
|
fn objFilterFn obj =
|
|
(
|
|
(isKindOf obj Editable_Poly) or (isKindOf obj Editable_mesh) or (isRefObj obj)
|
|
)
|
|
|
|
-- Add button event handler
|
|
on btnAdd pressed do
|
|
(
|
|
-- This check is done at run-time (and a variable array is used) so that we
|
|
-- can support more attached objects in the future (if required)
|
|
if (portalObjects.count == 4) do
|
|
(
|
|
MessageBox( "Portals can only have upto 4 attached objects. No more objects may be attached to this Portal." )
|
|
return 0
|
|
)
|
|
|
|
obj = pickObject "Select object to attach to Portal" filter:objFilterFn
|
|
|
|
-- Append to our PortalObjects parameter array only if the object
|
|
-- isn't already attached
|
|
if (obj != undefined) and ((findItem portalObjects obj) == 0) do
|
|
(
|
|
append portalObjects obj
|
|
)
|
|
|
|
-- Update the rollout list:
|
|
updateListBox()
|
|
)
|
|
|
|
-- Remove button event handler
|
|
on btnRemove pressed do
|
|
(
|
|
if (lbObjects.selection == 0) then
|
|
(
|
|
MessageBox( "You must select an object to remove from the Portal first." )
|
|
)
|
|
else
|
|
(
|
|
-- Remove from our PortalObjects parameter array
|
|
deleteItem PortalObjects lbObjects.selection
|
|
updateListBox()
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Our update event handler
|
|
-- Here we maintain backwards compatibility by moving the single reference in
|
|
-- objParams2 to the objParams3 array.
|
|
on update do
|
|
(
|
|
-- Update version 1 instances to version 2
|
|
-- by moving the reference in objParams2 to objParams3 and then setting the
|
|
-- objParams2.PortalObject to undefined
|
|
if ( version == 1 ) then
|
|
(
|
|
if ( this.PortalObject != undefined ) then
|
|
(
|
|
this.PortalObjects = append this.PortalObjects this.PortalObject
|
|
this.PortalObject = undefined
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
MiloPortalColourChangeCallback = "
|
|
for obj in selection where (getAttrClass obj == \"Gta MloPortal\") do
|
|
(
|
|
if ((getAttr obj (getAttrIndex \"Gta MloPortal\" \"Mirror Type\")) == 0) then
|
|
(
|
|
obj.Grid.colour = ([0, 255, 255] as color)
|
|
) else
|
|
(
|
|
obj.grid.colour = orange
|
|
)
|
|
)
|
|
"
|
|
|
|
gRsExtraAttrCommands.setCmd #updateMLOPortals class:"Gta MloPortal" attrs:#() cmd:MiloPortalColourChangeCallback priority:5
|