# # # Author:: Mark Harrison-Ball # 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