382 lines
10 KiB
Ruby
Executable File
382 lines
10 KiB
Ruby
Executable File
#
|
|
#
|
|
# Author:: Mark Harrison-Ball <Mark.Harrison-Ball@rockstargames.com>
|
|
# Date:: 21 November 2014 (AP3)
|
|
# Purpose:
|
|
# This is to parse the map terrain files and determine which cutscenes play in which tile
|
|
#
|
|
# $NAME = GLobal Value
|
|
# @NAME = Ruby Instance Variables:
|
|
# @@NAME = Ruby Class Variables, must be initialized
|
|
# NAME = Ruby Constants, Constants defined within a class or module can be accessed from within that class or module, and those defined outside a class or module can be accessed globally
|
|
|
|
# read interior xml, then read the level containers
|
|
# pass in cutscene position and determine if its in the bound, so we need to do a bound check
|
|
# For now we will just the Milo position and work on a bias on the number compared to the scene posiiton
|
|
|
|
#!/usr/bin/ruby
|
|
path = File.expand_path $0
|
|
path = File.dirname(path)
|
|
|
|
require 'System.Xml'
|
|
require 'System.Xml.Linq'
|
|
require 'RSG.Base.dll'
|
|
require 'RSG.Base.Configuration.dll'
|
|
|
|
|
|
include System::Xml
|
|
include System::Xml::Linq
|
|
include RSG::Base::Configuration
|
|
include RSG::Base::Logging
|
|
include RSG::Base::Logging::Universal
|
|
|
|
|
|
|
|
#using_clr_extensions System::Linq
|
|
using_clr_extensions System::Xml::Linq
|
|
using_clr_extensions System::Xml::XPath
|
|
|
|
require "#{path}/../../Global/terrain/tileObject.rb"
|
|
require "#{path}/../../Global/project/project"
|
|
require "#{path}/../../Global/perforce/p4utils"
|
|
# logging
|
|
require "#{path}/../../Global/logging/logging.rb"
|
|
#Database
|
|
require "#{path}/../../Global/database/DatabaseConnection.rb"
|
|
|
|
require "#{path}/../../Utils/TerrainTiles/TerrainNotify.rb"
|
|
|
|
#Terrain_XML_PATH = "#{@g_Config.project.DefaultBranch.Assets}/export/levels/#{@g_Config.project.name}/Terrain/"
|
|
|
|
GTA_MILO = 'gta_milo'
|
|
GTA_MILOTRI = 'gta_milotri'
|
|
GTA_MLOROOM = 'gtamloroom'
|
|
GTA_REFOBJECT = 'rsrefobject'
|
|
GTA_CONTAINER = 'container'
|
|
POS_BIAS = 7
|
|
TILE_SIZE = 512
|
|
|
|
SERVER = "nycw-mhb-sql"
|
|
|
|
# set to True for email notfiy to only author
|
|
DEBUG = false
|
|
|
|
|
|
class AnimMissionInfo
|
|
|
|
attr_reader :mission
|
|
attr_reader :animator
|
|
attr_reader :animatorEmail
|
|
attr_reader :cutsceneList
|
|
|
|
attr_writer :mission
|
|
attr_writer :animator
|
|
attr_writer :animatorEmail
|
|
attr_writer :cutsceneList
|
|
|
|
def initialize( _mission, _animator, _animatorEmail )
|
|
@mission = _mission
|
|
@animator = _animator
|
|
@animatorEmail = _animatorEmail
|
|
@cutsceneList = []
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class TileInfo
|
|
attr_reader :tileID
|
|
attr_reader :tileName
|
|
attr_reader :cutsceneList
|
|
attr_reader :tileBndMin
|
|
attr_reader :tileBndMax
|
|
|
|
|
|
attr_writer :tileID
|
|
attr_writer :tileName
|
|
attr_writer :cutsceneList
|
|
|
|
def initialize(tileID)
|
|
@tileID = tileID
|
|
@tileName = ''
|
|
@cutsceneList = Hash.new
|
|
|
|
end
|
|
|
|
end
|
|
|
|
class Terrain
|
|
attr_reader :terrainList
|
|
|
|
def initialize(g_Config, g_log)
|
|
@@g_Config = g_Config
|
|
@@glog = g_log
|
|
|
|
@terrainList = []
|
|
@terrainIDList = []
|
|
@@sourceCutsceneDb = "CutsceneStats_#{@@g_Config.project.name.upcase}"
|
|
@@sourceAnimDb = "AnimationStats_#{@@g_Config.project.name.upcase}"
|
|
@@sourceLevelDB = "LevelStats_#{@@g_Config.project.name.upcase}"
|
|
|
|
end
|
|
|
|
|
|
|
|
# parses teh containers and searches for milo's
|
|
def parseXML( xmlfile )
|
|
|
|
# validate its a terrain file (should end with an _
|
|
|
|
if ( xmlfile =~ /\/[a-z]_[0-9][0-9]_.xml/im )
|
|
@@glog.message("Loading: #{xmlfile}")
|
|
|
|
begin
|
|
|
|
@xmldoc = XmlDocument.new
|
|
@xmldoc = XDocument::Load( xmlfile )
|
|
|
|
rescue => e
|
|
@@glog.errorEmail(e, "Notify")
|
|
@@glog.error("skipping")
|
|
|
|
return
|
|
end
|
|
|
|
|
|
_containerName = ''
|
|
_guid = ''
|
|
containObj = @xmldoc.Root.XPathSelectElement( 'objects/object' )
|
|
if containObj.Attribute( XName::Get( "class" ) ).Value == GTA_CONTAINER then
|
|
_containerName = containObj.Attribute( XName::Get( "name" ) ).Value.downcase
|
|
_guid = containObj.Attribute( XName::Get( "guid" ) ).Value.downcase
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
@xmldoc.Root.XPathSelectElements( 'objects/object/transform/object/position' ).each {| elm |
|
|
_tileObj = TileObject.new()
|
|
_tileObj.name = _containerName
|
|
_tileObj.guid = _guid
|
|
|
|
_x = elm.Attribute( XName::Get( "x" ) ).Value.to_i
|
|
_y = elm.Attribute( XName::Get( "y" ) ).Value.to_i
|
|
_z = elm.Attribute( XName::Get( "z" ) ).Value.to_i
|
|
|
|
_tileObj.position = [_x, _y, _z]
|
|
|
|
# sort out X and Y bound
|
|
|
|
_xMin = _x - (TILE_SIZE / 2)
|
|
_yMin = _y - (TILE_SIZE / 2)
|
|
_tileObj.boundingBoxMin = [_xMin, _yMin ]
|
|
|
|
_xMax = _x + (TILE_SIZE / 2)
|
|
_yMax = _y + (TILE_SIZE / 2)
|
|
_tileObj.boundingBoxMax = [_xMax, _yMax]
|
|
|
|
|
|
@@glog.message(_tileObj.name)
|
|
# @@glog.message(_tileObj.guid.to_s)
|
|
@@glog.message(_tileObj.position.to_s)
|
|
terrainList << _tileObj
|
|
|
|
}
|
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
def connectDB( srcDB )
|
|
@dBconnection.connect(SERVER, srcDB)
|
|
end
|
|
|
|
def closeDB()
|
|
@dBconnection.close()
|
|
end
|
|
|
|
# Read in our list of cutscene positions
|
|
def parseCutscenes()
|
|
@dBconnection = DatabaseConnection.new( @@glog )
|
|
#update our list of tiles to the DB
|
|
connectDB(@@sourceLevelDB)
|
|
|
|
|
|
terrainList.each do | tile |
|
|
@@glog.message("UPDATING #{tile.name}")
|
|
|
|
paramaters = { '@name' => tile.name,
|
|
'@PositionX' => tile.position[0],
|
|
'@PositionY' => tile.position[1],
|
|
'@PositionZ' => tile.position[2],
|
|
'@BndMin' => tile.boundingBoxMin.to_s,
|
|
'@BndMax' => tile.boundingBoxMax.to_s
|
|
}
|
|
|
|
|
|
table = @dBconnection.read_data('','UpdateTerrainTile', paramaters )
|
|
# add our terrain ID to the list so we no what has been updated
|
|
@terrainIDList << table[0]['TileID']
|
|
|
|
end
|
|
|
|
# Update Cutscene Tile ID's
|
|
connectDB(@@sourceCutsceneDb)
|
|
paramaters = {}
|
|
cutsceneList = @dBconnection.read_data('','GetCutsceneIDPositions', paramaters )
|
|
|
|
cutsceneList.each do | cutscene |
|
|
|
|
# check if
|
|
terrainList.each do | tile |
|
|
if cutscene['vOffset_X'] > tile.boundingBoxMin[0] and cutscene['vOffset_X'] < tile.boundingBoxMax[0] and
|
|
cutscene['vOffset_Y'] > tile.boundingBoxMin[1] and cutscene['vOffset_Y'] < tile.boundingBoxMax[1] then
|
|
puts "#{cutscene['CutsceneID']} is in tile #{tile.name}"
|
|
paramaters = { '@name' => tile.name,
|
|
'@cutsceneid' => cutscene['CutsceneID'].to_i
|
|
}
|
|
@dBconnection.read_data('','UpdateCutsceneTile', paramaters )
|
|
end
|
|
end
|
|
end
|
|
|
|
# Updated Anim Scene Tile ID's
|
|
connectDB(@@sourceAnimDb)
|
|
paramaters = {}
|
|
cutsceneList = @dBconnection.read_data('','GetAnimIDPositions', paramaters )
|
|
|
|
cutsceneList.each do | cutscene |
|
|
|
|
# check if
|
|
terrainList.each do | tile |
|
|
if cutscene['sceneOrigin_X'] > tile.boundingBoxMin[0] and cutscene['sceneOrigin_X'] < tile.boundingBoxMax[0] and
|
|
cutscene['sceneOrigin_Y'] > tile.boundingBoxMin[1] and cutscene['sceneOrigin_Y'] < tile.boundingBoxMax[1] then
|
|
puts "#{cutscene['CutsceneID']} is in tile #{tile.name}"
|
|
paramaters = { '@name' => tile.name,
|
|
'@animid' => cutscene['ID'].to_i
|
|
}
|
|
@dBconnection.read_data('','UpdateAnimSceneTile', paramaters )
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
closeDB()
|
|
|
|
|
|
|
|
end
|
|
|
|
def getAffectedCutscenes()
|
|
|
|
affectedTiles = []
|
|
|
|
|
|
if @terrainIDList.count > 0 then
|
|
connectDB(@@sourceCutsceneDb)
|
|
@terrainIDList.each do | tileID |
|
|
|
|
# get a list of affect cutscenes from tile ID if any
|
|
paramaters = { '@tileid' => tileID}
|
|
|
|
table = @dBconnection.read_data('','Automation.GetCutscenesByTileID', paramaters )
|
|
|
|
if table.count > 0 then
|
|
|
|
# Create a new Tile HOlder with list of cutscenes
|
|
tileInfo = TileInfo.new(tileID)
|
|
|
|
|
|
|
|
table.each do | cutscene |
|
|
tileInfo.tileName = cutscene['Tile']
|
|
|
|
if not tileInfo.cutsceneList.include?(cutscene['Mission'])
|
|
tileInfo.cutsceneList[cutscene['Mission']] = AnimMissionInfo.new( cutscene['Mission'], cutscene['Animator'], cutscene['Email'] )
|
|
end
|
|
tileInfo.cutsceneList[cutscene['Mission']].cutsceneList << cutscene['CutsceneName'].to_s
|
|
end
|
|
affectedTiles << tileInfo
|
|
end
|
|
|
|
end
|
|
closeDB()
|
|
end
|
|
affectedTiles
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
# get our change info from a single passed in CL
|
|
def parseChangefromCL( cl )
|
|
#puts @p4.methods
|
|
#f = @p4.get_changelistDescribe( cl )
|
|
change = @p4.get_Change_from_CL( cl )
|
|
if change != nil
|
|
validfiles = @p4.findFilesinChange( change , '*.xml' )
|
|
|
|
validfiles.each do | xmlfile |
|
|
@mapTerrain.parseXML( xmlfile.replace('//', 'x:/' ) )
|
|
end
|
|
|
|
@mapTerrain.parseCutscenes()
|
|
affectedTiles = @mapTerrain.getAffectedCutscenes()
|
|
if affectedTiles.count > 0 then
|
|
userEmail = @p4.get_user_email(change.username)
|
|
emailToUsers( affectedTiles, change, userEmail )
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
end
|
|
|
|
|
|
if ( __FILE__ == $0 ) then
|
|
|
|
LogFactory.Initialize()
|
|
LogFactory.CreateApplicationConsoleLogTarget( )
|
|
g_Log = LogFactory.ApplicationLog
|
|
# Config initialisation.
|
|
$g_Config = RSG::Base::Configuration::ConfigFactory::CreateConfig( )
|
|
@p4 = P4Utils.new(g_Log)
|
|
|
|
@glog = Logger.new( $g_Config , g_Log)
|
|
|
|
begin
|
|
|
|
@mapTerrain = Terrain.new( $g_Config, @glog)
|
|
|
|
if ARGV.length == 0
|
|
@glog.message("Running in Directory mode...")
|
|
levelXmlPath = "#{$g_Config.project.DefaultBranch.Export}/levels/rdr3/terrain"
|
|
|
|
Dir.glob("#{levelXmlPath}/**/*.xml").each do | xmlfile |
|
|
@mapTerrain.parseXML( xmlfile )
|
|
end
|
|
|
|
|
|
@mapTerrain.parseCutscenes()
|
|
|
|
|
|
|
|
elsif ARGV.length > 0
|
|
@glog.message("Running in Automation mode...")
|
|
cl = ARGV[0].to_i
|
|
parseChangefromCL( cl )
|
|
|
|
end
|
|
rescue => e
|
|
@glog.errorEmail(e)
|
|
raise
|
|
end
|
|
|
|
end
|
|
|