module Compiler module_function #============================================================================= # Add new map files to the map tree. #============================================================================= def import_new_maps return false if !$DEBUG mapfiles = {} # Get IDs of all maps in the Data folder Dir.chdir("Data") { mapData = sprintf("Map*.rxdata") for map in Dir.glob(mapData) mapfiles[$1.to_i(10)] = true if map[/map(\d+)\.rxdata/i] end } mapinfos = pbLoadMapInfos maxOrder = 0 # Exclude maps found in mapinfos for id in mapinfos.keys next if !mapinfos[id] mapfiles.delete(id) if mapfiles[id] maxOrder = [maxOrder,mapinfos[id].order].max end # Import maps not found in mapinfos maxOrder += 1 imported = false count = 0 for id in mapfiles.keys next if id==999 # Ignore 999 (random dungeon map) mapinfo = RPG::MapInfo.new mapinfo.order = maxOrder mapinfo.name = sprintf("MAP%03d",id) maxOrder += 1 mapinfos[id] = mapinfo imported = true count += 1 end if imported save_data(mapinfos,"Data/MapInfos.rxdata") $PokemonTemp.mapInfos = nil pbMessage(_INTL("{1} new map(s) copied to the Data folder were successfully imported.",count)) end return imported end #============================================================================= # Generate and modify event commands. #============================================================================= def generate_move_route(commands) route = RPG::MoveRoute.new route.repeat = false route.skippable = true route.list.clear i = 0 while i=map.width || y<0 || y>=map.height passages = getTilesetPassages(map,mapID) priorities = getTilesetPriorities(map,mapID) for i in [2, 1, 0] tile_id = map.data[x, y, i] return false if tile_id==nil passage = passages[tile_id] if !passage raise "The tile used on map #{mapID} at coordinates (#{x}, #{y}) on layer #{i+1} doesn't exist in the tileset. " + "It should be deleted to prevent errors." end return false if passage&0x0f==0x0f return true if priorities[tile_id]==0 end return true end def isCounterTile?(mapID,x,y) map = getMap(mapID) return false if !map passages = getTilesetPassages(map,mapID) for i in [2, 1, 0] tile_id = map.data[x, y, i] return false if tile_id==nil passage = passages[tile_id] if !passage raise "The tile used on map #{mapID} at coordinates (#{x}, #{y}) on layer #{i+1} doesn't exist in the tileset. " + "It should be deleted to prevent errors." end return true if passage&0x80==0x80 end return false end def setCounterTile(mapID,x,y) map = getMap(mapID) return if !map passages = getTilesetPassages(map,mapID) for i in [2, 1, 0] tile_id = map.data[x, y, i] next if tile_id==0 passages[tile_id] |= 0x80 break end end def registerSwitch(switch) return @registeredSwitches[switch] if @registeredSwitches[switch] for id in 1..5000 name = @system.switches[id] next if name && name!="" && name!=switch @system.switches[id] = switch @registeredSwitches[switch] = id return id end return 1 end def saveMap(mapID) save_data(getMap(mapID),mapFilename(mapID)) rescue nil end def saveTilesets save_data(@tilesets, "Data/Tilesets.rxdata") save_data(@system, "Data/System.rxdata") end end #============================================================================= # #============================================================================= class TrainerChecker def initialize @dontaskagain = false end def pbTrainerTypeCheck(trainer_type) return if !$DEBUG || @dontaskagain return if GameData::TrainerType.exists?(trainer_type) if pbConfirmMessage(_INTL("Add new trainer type {1}?", trainer_type.to_s)) pbTrainerTypeEditorNew(trainer_type.to_s) end end def pbTrainerBattleCheck(tr_type, tr_name, tr_version) return if !$DEBUG || @dontaskagain # Check for existence of trainer type pbTrainerTypeCheck(tr_type) return if !GameData::TrainerType.exists?(tr_type) tr_type = GameData::TrainerType.get(tr_type).id # Check for existence of trainer return if GameData::Trainer.exists?(tr_type, tr_name, tr_version) # Add new trainer cmd = pbMissingTrainer(tr_type, tr_name, tr_version) if cmd == 2 @dontaskagain = true Graphics.update end end end #============================================================================= # Convert trainer comments to trainer event. #============================================================================= def convert_to_trainer_event(event,trainerChecker) return nil if !event || event.pages.length==0 list = event.pages[0].list return nil if list.length<2 commands = [] isFirstCommand = false # Find all the trainer comments in the event for i in 0...list.length next if list[i].code!=108 # Comment (first line) command = list[i].parameters[0] for j in (i+1)...list.length break if list[j].code!=408 # Comment (continuation line) command += "\r\n"+list[j].parameters[0] end if command[/^(Battle\:|Type\:|Name\:|BattleID\:|DoubleBattle\:|Backdrop\:|EndSpeech\:|Outcome\:|Continue\:|EndBattle\:|EndIfSwitch\:|VanishIfSwitch\:|RegSpeech\:)/i] commands.push(command) isFirstCommand = true if i==0 end end return nil if commands.length==0 # Found trainer comments; create a new Event object to replace this event ret = RPG::Event.new(event.x,event.y) ret.name = event.name ret.id = event.id firstpage = Marshal::load(Marshal.dump(event.pages[0])) # Copy event's first page firstpage.trigger = 2 # On event touch firstpage.list = [] # Clear page's commands # Rename the event if there's nothing above the trainer comments if isFirstCommand if !event.name[/trainer/i] ret.name = "Trainer(3)" elsif event.name[/^\s*trainer\s*\((\d+)\)\s*$/i] ret.name = "Trainer(#{$1})" end end # Compile the trainer comments rewriteComments = false # You can change this battles = [] trtype = nil trname = nil battleid = 0 doublebattle = false backdrop = nil endspeeches = [] outcome = 0 continue = false endbattles = [] endifswitch = [] vanishifswitch = [] regspeech = nil for command in commands if command[/^Battle\:\s*([\s\S]+)$/i] battles.push($~[1]) push_comment(firstpage.list,command) if rewriteComments elsif command[/^Type\:\s*([\s\S]+)$/i] trtype = $~[1].gsub(/^\s+/,"").gsub(/\s+$/,"") push_comment(firstpage.list,command) if rewriteComments elsif command[/^Name\:\s*([\s\S]+)$/i] trname = $~[1].gsub(/^\s+/,"").gsub(/\s+$/,"") push_comment(firstpage.list,command) if rewriteComments elsif command[/^BattleID\:\s*(\d+)$/i] battleid = $~[1].to_i push_comment(firstpage.list,command) if rewriteComments elsif command[/^DoubleBattle\:\s*([\s\S]+)$/i] value = $~[1].gsub(/^\s+/,"").gsub(/\s+$/,"") doublebattle = true if value.upcase=="TRUE" || value.upcase=="YES" push_comment(firstpage.list,command) if rewriteComments elsif command[/^Backdrop\:\s*([\s\S]+)$/i] backdrop = $~[1].gsub(/^\s+/,"").gsub(/\s+$/,"") push_comment(firstpage.list,command) if rewriteComments elsif command[/^EndSpeech\:\s*([\s\S]+)$/i] endspeeches.push($~[1].gsub(/^\s+/,"").gsub(/\s+$/,"")) push_comment(firstpage.list,command) if rewriteComments elsif command[/^Outcome\:\s*(\d+)$/i] outcome = $~[1].to_i push_comment(firstpage.list,command) if rewriteComments elsif command[/^Continue\:\s*([\s\S]+)$/i] value = $~[1].gsub(/^\s+/,"").gsub(/\s+$/,"") continue = true if value.upcase=="TRUE" || value.upcase=="YES" push_comment(firstpage.list,command) if rewriteComments elsif command[/^EndBattle\:\s*([\s\S]+)$/i] endbattles.push($~[1].gsub(/^\s+/,"").gsub(/\s+$/,"")) push_comment(firstpage.list,command) if rewriteComments elsif command[/^EndIfSwitch\:\s*([\s\S]+)$/i] endifswitch.push(($~[1].gsub(/^\s+/,"").gsub(/\s+$/,"")).to_i) push_comment(firstpage.list,command) if rewriteComments elsif command[/^VanishIfSwitch\:\s*([\s\S]+)$/i] vanishifswitch.push(($~[1].gsub(/^\s+/,"").gsub(/\s+$/,"")).to_i) push_comment(firstpage.list,command) if rewriteComments elsif command[/^RegSpeech\:\s*([\s\S]+)$/i] regspeech = $~[1].gsub(/^\s+/,"").gsub(/\s+$/,"") push_comment(firstpage.list,command) if rewriteComments end end return nil if battles.length<=0 # Run trainer check now, except in editor trainerChecker.pbTrainerBattleCheck(trtype,trname,battleid) if !$INEDITOR # Set the event's charset to one depending on the trainer type if the event # doesn't have a charset if firstpage.graphic.character_name=="" && GameData::TrainerType.exists?(trtype) trainerid = GameData::TrainerType.get(trtype).id filename = GameData::TrainerType.charset_filename_brief(trainerid) if FileTest.image_exist?("Graphics/Characters/"+filename) firstpage.graphic.character_name = sprintf(filename) end end # Create strings that will be used repeatedly safetrcombo = sprintf(":%s,\"%s\"",trtype,safequote(trname)) # :YOUNGSTER,"Joey" introplay = sprintf("pbTrainerIntro(:%s)",trtype) # Write first page push_script(firstpage.list,introplay) # pbTrainerIntro push_script(firstpage.list,"pbNoticePlayer(get_character(0))") push_text(firstpage.list,battles[0]) if battles.length>1 # Has rematches push_script(firstpage.list,sprintf("pbTrainerCheck(%s,%d,%d)",safetrcombo,battles.length,battleid)) end push_script(firstpage.list,"setBattleRule(\"double\")") if doublebattle push_script(firstpage.list,sprintf("setBattleRule(\"backdrop\",\"%s\")",safequote(backdrop))) if backdrop push_script(firstpage.list,sprintf("setBattleRule(\"outcomeVar\",%d)",outcome)) if outcome>1 push_script(firstpage.list,"setBattleRule(\"canLose\")") if continue espeech = (endspeeches[0]) ? sprintf("_I(\"%s\")",safequote2(endspeeches[0])) : "nil" if battleid>0 battleString = sprintf("pbTrainerBattle(%s,%s,nil,%d)",safetrcombo,espeech,battleid) elsif endspeeches[0] battleString = sprintf("pbTrainerBattle(%s,%s)",safetrcombo,espeech) else battleString = sprintf("pbTrainerBattle(%s)",safetrcombo) end push_branch(firstpage.list,battleString) if battles.length>1 # Has rematches push_script(firstpage.list, sprintf("pbPhoneRegisterBattle(_I(\"%s\"),get_character(0),%s,%d)", regspeech,safetrcombo,battles.length),1) end push_self_switch(firstpage.list,"A",true,1) push_branch_end(firstpage.list,1) push_script(firstpage.list,"pbTrainerEnd",0) push_end(firstpage.list) # Copy first page to last page and make changes to its properties lastpage = Marshal::load(Marshal.dump(firstpage)) lastpage.trigger = 0 # On action lastpage.list = [] # Clear page's commands lastpage.condition = firstpage.condition.clone lastpage.condition.self_switch_valid = true lastpage.condition.self_switch_ch = "A" # Copy last page to rematch page rematchpage = Marshal::load(Marshal.dump(lastpage)) rematchpage.list = lastpage.list.clone # Copy the last page's commands rematchpage.condition = lastpage.condition.clone rematchpage.condition.self_switch_valid = true rematchpage.condition.self_switch_ch = "B" # Write rematch and last pages for i in 1...battles.length # Run trainer check now, except in editor trainerChecker.pbTrainerBattleCheck(trtype,trname,battleid+i) if !$INEDITOR if i==battles.length-1 push_branch(rematchpage.list,sprintf("pbPhoneBattleCount(%s)>=%d",safetrcombo,i)) push_branch(lastpage.list,sprintf("pbPhoneBattleCount(%s)>%d",safetrcombo,i)) else push_branch(rematchpage.list,sprintf("pbPhoneBattleCount(%s)==%d",safetrcombo,i)) push_branch(lastpage.list,sprintf("pbPhoneBattleCount(%s)==%d",safetrcombo,i)) end # Rematch page push_script(rematchpage.list,introplay,1) # pbTrainerIntro push_text(rematchpage.list,battles[i],1) push_script(rematchpage.list,"setBattleRule(\"double\")",1) if doublebattle push_script(rematchpage.list,sprintf("setBattleRule(\"backdrop\",%s)",safequote(backdrop)),1) if backdrop push_script(rematchpage.list,sprintf("setBattleRule(\"outcomeVar\",%d)",outcome),1) if outcome>1 push_script(rematchpage.list,"setBattleRule(\"canLose\")",1) if continue espeech = nil if endspeeches.length>0 espeech = (endspeeches[i]) ? endspeeches[i] : endspeeches[endspeeches.length-1] end espeech = (espeech) ? sprintf("_I(\"%s\")",safequote2(espeech)) : "nil" battleString = sprintf("pbTrainerBattle(%s,%s,nil,%d)",safetrcombo,espeech,battleid+i) push_branch(rematchpage.list,battleString,1) push_script(rematchpage.list,sprintf("pbPhoneIncrement(%s,%d)",safetrcombo,battles.length),2) push_self_switch(rematchpage.list,"A",true,2) push_self_switch(rematchpage.list,"B",false,2) push_script(rematchpage.list,"pbTrainerEnd",2) push_branch_end(rematchpage.list,2) push_exit(rematchpage.list,1) # Exit Event Processing push_branch_end(rematchpage.list,1) # Last page if endbattles.length>0 ebattle = (endbattles[i]) ? endbattles[i] : endbattles[endbattles.length-1] push_text(lastpage.list,ebattle,1) end push_script(lastpage.list, sprintf("pbPhoneRegisterBattle(_I(\"%s\"),get_character(0),%s,%d)", regspeech,safetrcombo,battles.length),1) push_exit(lastpage.list,1) # Exit Event Processing push_branch_end(lastpage.list,1) end # Finish writing rematch page push_end(rematchpage.list) # Finish writing last page ebattle = (endbattles[0]) ? endbattles[0] : "..." push_text(lastpage.list,ebattle) if battles.length>1 push_script(lastpage.list, sprintf("pbPhoneRegisterBattle(_I(\"%s\"),get_character(0),%s,%d)", regspeech,safetrcombo,battles.length)) end push_end(lastpage.list) # Add pages to the new event if battles.length==1 # Only one battle ret.pages = [firstpage,lastpage] else # Has rematches ret.pages = [firstpage,rematchpage,lastpage] end # Copy last page to endIfSwitch page for endswitch in endifswitch endIfSwitchPage = Marshal::load(Marshal.dump(lastpage)) endIfSwitchPage.condition = lastpage.condition.clone if endIfSwitchPage.condition.switch1_valid # Add another page condition endIfSwitchPage.condition.switch2_valid = true endIfSwitchPage.condition.switch2_id = endswitch else endIfSwitchPage.condition.switch1_valid = true endIfSwitchPage.condition.switch1_id = endswitch end endIfSwitchPage.condition.self_switch_valid = false endIfSwitchPage.list = [] # Clear page's commands ebattle = (endbattles[0]) ? endbattles[0] : "..." push_text(endIfSwitchPage.list,ebattle) push_end(endIfSwitchPage.list) ret.pages.push(endIfSwitchPage) end # Copy last page to vanishIfSwitch page for vanishswitch in vanishifswitch vanishIfSwitchPage = Marshal::load(Marshal.dump(lastpage)) vanishIfSwitchPage.graphic.character_name = "" # No charset vanishIfSwitchPage.condition = lastpage.condition.clone if vanishIfSwitchPage.condition.switch1_valid # Add another page condition vanishIfSwitchPage.condition.switch2_valid = true vanishIfSwitchPage.condition.switch2_id = vanishswitch else vanishIfSwitchPage.condition.switch1_valid = true vanishIfSwitchPage.condition.switch1_id = vanishswitch end vanishIfSwitchPage.condition.self_switch_valid = false vanishIfSwitchPage.list = [] # Clear page's commands push_end(vanishIfSwitchPage.list) ret.pages.push(vanishIfSwitchPage) end return ret end #============================================================================= # Convert event name to item event. # Checks if the event's name is "Item:POTION" or "HiddenItem:POTION". If so, # rewrites the whole event into one now named "Item"/"HiddenItem" which gives # that item when interacted with. #============================================================================= def convert_to_item_event(event) return nil if !event || event.pages.length==0 name = event.name ret = RPG::Event.new(event.x,event.y) ret.name = event.name ret.id = event.id ret.pages = [] itemName = "" hidden = false if name[/^hiddenitem\:\s*(\w+)\s*$/i] itemName = $1 return nil if !GameData::Item.exists?(itemName) ret.name = "HiddenItem" hidden = true elsif name[/^item\:\s*(\w+)\s*$/i] itemName = $1 return nil if !GameData::Item.exists?(itemName) ret.name = "Item" else return nil end # Event page 1 page = RPG::Event::Page.new page.graphic.character_name = "Object ball" if !hidden page.list = [] push_branch(page.list,sprintf("pbItemBall(:%s)",itemName)) push_self_switch(page.list,"A",true,1) push_else(page.list,1) push_branch_end(page.list,1) push_end(page.list) ret.pages.push(page) # Event page 2 page = RPG::Event::Page.new page.condition.self_switch_valid = true page.condition.self_switch_ch = "A" ret.pages.push(page) return ret end #============================================================================= # Checks whether a given event is likely to be a door. If so, rewrite it to # include animating the event as though it was a door opening and closing as the # player passes through. #============================================================================= def update_door_event(event,mapData) changed = false return false if event.is_a?(RPG::CommonEvent) # Check if event has 2+ pages and the last page meets all of these criteria: # - Has a condition of a Switch being ON # - The event has a charset graphic # - There are more than 5 commands in that page, the first of which is a # Conditional Branch lastPage = event.pages[event.pages.length-1] if event.pages.length>=2 && lastPage.condition.switch1_valid && lastPage.graphic.character_name!="" && lastPage.list.length>5 && lastPage.list[0].code==111 # This bit of code is just in case Switch 22 has been renamed/repurposed, # which is highly unlikely. It changes the Switch used in the condition to # whichever is named 's:tsOff?("A")'. if lastPage.condition.switch1_id==22 && mapData.switchName(lastPage.condition.switch1_id)!='s:tsOff?("A")' lastPage.condition.switch1_id = mapData.registerSwitch('s:tsOff?("A")') changed = true end # If the last page's Switch condition uses a Switch named 's:tsOff?("A")', # check the penultimate page. If it contains exactly 1 "Transfer Player" # command and does NOT contain a "Change Transparent Flag" command, rewrite # both the penultimate page and the last page. if mapData.switchName(lastPage.condition.switch1_id)=='s:tsOff?("A")' list = event.pages[event.pages.length-2].list transferCommand = list.find_all { |cmd| cmd.code==201 } # Transfer Player if transferCommand.length==1 && !list.any? { |cmd| cmd.code==208 } # Change Transparent Flag # Rewrite penultimate page list.clear push_move_route_and_wait(list,0,[ # Move Route for door opening PBMoveRoute::PlaySE,RPG::AudioFile.new("Door enter"),PBMoveRoute::Wait,2, PBMoveRoute::TurnLeft,PBMoveRoute::Wait,2, PBMoveRoute::TurnRight,PBMoveRoute::Wait,2, PBMoveRoute::TurnUp,PBMoveRoute::Wait,2]) push_move_route_and_wait(list,-1,[ # Move Route for player entering door PBMoveRoute::ThroughOn,PBMoveRoute::Up,PBMoveRoute::ThroughOff]) push_event(list,208,[0]) # Change Transparent Flag (invisible) push_move_route_and_wait(list,0,[PBMoveRoute::Wait,2, # Move Route for door closing PBMoveRoute::TurnRight,PBMoveRoute::Wait,2, PBMoveRoute::TurnLeft,PBMoveRoute::Wait,2, PBMoveRoute::TurnDown,PBMoveRoute::Wait,2]) push_event(list,223,[Tone.new(-255,-255,-255),6]) # Change Screen Color Tone push_wait(list,8) # Wait push_event(list,208,[1]) # Change Transparent Flag (visible) push_event(list,transferCommand[0].code,transferCommand[0].parameters) # Transfer Player push_event(list,223,[Tone.new(0,0,0),6]) # Change Screen Color Tone push_end(list) # Rewrite last page list = lastPage.list list.clear push_branch(list,"get_character(0).onEvent?") # Conditional Branch push_event(list,208,[0],1) # Change Transparent Flag (invisible) push_move_route_and_wait(list,0,[ # Move Route for setting door to open PBMoveRoute::TurnLeft,PBMoveRoute::Wait,6],1) push_event(list,208,[1],1) # Change Transparent Flag (visible) push_move_route_and_wait(list,-1,[PBMoveRoute::Down],1) # Move Route for player exiting door push_move_route_and_wait(list,0,[ # Move Route for door closing PBMoveRoute::TurnUp,PBMoveRoute::Wait,2, PBMoveRoute::TurnRight,PBMoveRoute::Wait,2, PBMoveRoute::TurnDown,PBMoveRoute::Wait,2],1) push_branch_end(list,1) push_script(list,"setTempSwitchOn(\"A\")") push_end(list) changed = true end end end return changed end #============================================================================= # Fix up standard code snippets #============================================================================= def event_is_empty?(e) return true if !e return false if e.is_a?(RPG::CommonEvent) return e.pages.length==0 end # Checks if the event has exactly 1 page, said page has no graphic, it has # less than 12 commands and at least one is a Transfer Player, and the tiles # to the left/right/upper left/upper right are not passable but the event's # tile is. Causes a second page to be added to the event which is the "is # player on me?" check that occurs when the map is entered. def likely_passage?(thisEvent,mapID,mapData) return false if !thisEvent || thisEvent.pages.length==0 return false if thisEvent.pages.length!=1 if thisEvent.pages[0].graphic.character_name=="" && thisEvent.pages[0].list.length<=12 && thisEvent.pages[0].list.any? { |cmd| cmd.code==201 } && # Transfer Player # mapData.isPassable?(mapID,thisEvent.x,thisEvent.y+1) && mapData.isPassable?(mapID,thisEvent.x,thisEvent.y) && !mapData.isPassable?(mapID,thisEvent.x-1,thisEvent.y) && !mapData.isPassable?(mapID,thisEvent.x+1,thisEvent.y) && !mapData.isPassable?(mapID,thisEvent.x-1,thisEvent.y-1) && !mapData.isPassable?(mapID,thisEvent.x+1,thisEvent.y-1) return true end return false end def change_script(script,re) tmp = script[0].gsub(re) { yield($~) } if script[0]!=tmp script[0] = tmp return true end return false end def change_scripts(script) changed = false changed |= change_script(script,/\$game_variables\[(\d+)\](?!\s*(?:\=|\!|<|>))/) { |m| "pbGet("+m[1]+")" } changed |= change_script(script,/\$Trainer\.party\[\s*pbGet\((\d+)\)\s*\]/) { |m| "pbGetPokemon("+m[1]+")" } return changed end def fix_event_name(event) return false if !event case event.name.downcase when "tree" event.name = "CutTree" when "rock" event.name = "SmashRock" when "boulder" event.name = "StrengthBoulder" else return false end return true end def fix_event_use(event,_mapID,mapData) return nil if event_is_empty?(event) changed = false trainerMoneyRE = /^\s*\$Trainer\.money\s*(<|<=|>|>=)\s*(\d+)\s*$/ itemBallRE = /^\s*(Kernel\.)?pbItemBall/ # Rewrite event if it looks like a door changed = true if update_door_event(event,mapData) # Check through each page of the event in turn pbEachPage(event) do |page| i = 0 list = page.list while i=2 && e.pages[e.pages.length-1].condition.switch1_valid && e.pages[e.pages.length-1].condition.switch1_id==22 && mapData.switchName(e.pages[e.pages.length-1].condition.switch1_id)!='s:tsOff?("A")' && e.pages[e.pages.length-1].list.length>5 && e.pages[e.pages.length-1].list[0].code==111 # Conditional Branch e.pages[e.pages.length-1].condition.switch1_id = mapData.registerSwitch('s:tsOff?("A")') mapData.saveMap(params[1]) changed = true end # Checks if the found event is a simple Transfer Player one nestled # between tiles that aren't passable - it is likely a door, so give # it a second page with an "is player on me?" check. if likely_passage?(e,params[1],mapData) # Checks the first page add_passage_list(e,mapData) mapData.saveMap(params[1]) changed = true end # If the found event's last page's Switch condition uses a Switch # named 's:tsOff?("A")', it really does look like a door. Make this # command transfer the player on top of it rather than in front of # it. if e && e.pages.length>=2 && e.pages[e.pages.length-1].condition.switch1_valid && mapData.switchName(e.pages[e.pages.length-1].condition.switch1_id)=='s:tsOff?("A")' # If this is really a door, move transfer target to it params[3] -= 1 # Move this command's destination up 1 tile (onto the found event) params[5] = 1 # No fade (the found event should take care of that) changed = true end deletedRoute = nil deleteMoveRouteAt = proc { |list,i| arr = [] if list[i] && list[i].code==209 # Set Move Route arr.push(list[i]); list.delete_at(i) while i=0 list.insert(i,route[j]) j -= 1 end } # If the next event command is a Move Route that moves the player, # check whether all it does is turn the player in a direction (or # its first item is to move the player in a direction). If so, this # Transfer Player command may as well set the player's direction # instead; make it do so and delete that Move Route. if params[4]==0 && # Retain direction i+13 # Retain direction # for j in 0...i # if list[j].code==209 && list[j].parameters[0]==-1 # Set Move Route # route = list[j].parameters[1] # if route && route.list.length<=2 # oldlistlength = list.length # # Delete superfluous move route command if necessary # if route.list[0].code==16 # Player Turn Down # deleteMoveRouteAt.call(list,j); params[4] = 2; changed = true; i -= (oldlistlength-list.length) # elsif route.list[0].code==17 # Player Turn Left # deleteMoveRouteAt.call(list,j); params[4] = 4; changed = true; i -= (oldlistlength-list.length) # elsif route.list[0].code==18 # Player Turn Right # deleteMoveRouteAt.call(list,j); params[4] = 6; changed = true; i -= (oldlistlength-list.length) # elsif route.list[0].code==19 # Player Turn Up # deleteMoveRouteAt.call(list,j); params[4] = 8; changed = true; i -= (oldlistlength-list.length) # end # end # end # end # If the next event command changes the screen color, and the one # after that is a Move Route which only turns the player in a # direction, this Transfer Player command may as well set the # player's direction instead; make it do so and delete that Move # Route. elsif params[4]==0 && # Retain direction i+2=2 && list[i].parameters[0].length>0 && list[i].parameters[0].length<=20 && !list[i].parameters[0][/\\n/] # Very short line list[i].parameters[0] += "\\n"+list[i+1].parameters[0] list.delete_at(i+1) i -= 1 # revisit this text command changed = true # Check whether this Show Text command has 3+ lines and the next command # is also a Show Text elsif lines>=3 && list[i+lines] && list[i+lines].code==101 # Show Text # Check whether a sentence is being broken midway between two Text # commands (i.e. the first Show Text doesn't end in certain punctuation) lastLine = list[i+lines-1].parameters[0].sub(/\s+$/,"") if lastLine.length>0 && !lastLine[/[\\<]/] && lastLine[/[^\.,\!\?\;\-\"]$/] message = list[i].parameters[0] j = i+1 while j=0 list.insert(i,RPG::EventCommand.new((j==0) ? 101 : 401,indent,[nextMessage[j]])) j-=1 end j = newMessage.length-1 while j>=0 list.insert(i,RPG::EventCommand.new((j==0) ? 101 : 401,indent,[newMessage[j]])) j -= 1 end changed = true i += 1 next end end end when 111 # Conditional Branch if list[i].parameters[0]==12 # script x = [list[i].parameters[1]] changed |= change_scripts(x) list[i].parameters[1] = x[0] script = x[0] if script[trainerMoneyRE] # Compares $Trainer.money with a value # Checking money directly operator = $1 amount = $2.to_i if operator=="<" params[0] = 7 # gold params[2] = 1 params[1] = amount-1 changed = true elsif operator=="<=" params[0] = 7 # gold params[2] = 1 params[1] = amount changed = true elsif operator==">" params[0] = 7 # gold params[2] = 0 params[1] = amount+1 changed = true elsif operator==">=" params[0] = 7 # gold params[2] = 0 params[1] = amount changed = true end elsif script[itemBallRE] && i>0 # Contains pbItemBall after another command # Using pbItemBall on non-item events, change it list[i].parameters[1] = script.sub(/pbItemBall/,"pbReceiveItem") changed = true elsif script[/^\s*(Kernel\.)?(pbTrainerBattle|pbDoubleTrainerBattle)/] # Check if trainer battle conditional branch is empty j = i+1 isempty = true elseIndex = -1 # Check if page is empty while j=0 list.insert(elseIndex+1, RPG::EventCommand.new(115,list[i].indent+1,[]) # Exit Event Processing ) else list.insert(i+1, RPG::EventCommand.new(0,list[i].indent+1,[]), # Empty Event RPG::EventCommand.new(411,list[i].indent,[]), # Else RPG::EventCommand.new(115,list[i].indent+1,[]) # Exit Event Processing ) end changed = true end end end end i += 1 end end return (changed) ? event : nil end #============================================================================= # Convert events used as counters into proper counters. #============================================================================= # Checks if the event has just 1 page, which has no conditions and no commands # and whose movement type is "Fixed". def plain_event?(event) return false unless event return false if event.pages.length>1 return false if event.pages[0].move_type!=0 return false if event.pages[0].condition.switch1_valid || event.pages[0].condition.switch2_valid || event.pages[0].condition.variable_valid || event.pages[0].condition.self_switch_valid return true if event.pages[0].list.length<=1 return false end # Checks if the event has just 1 page, which has no conditions and whose # movement type is "Fixed". Then checks if there are no commands, or it looks # like a simple Mart or a Poké Center nurse event. def plain_event_or_mart?(event) return false unless event return false if event.pages.length>1 return false if event.pages[0].move_type!=0 return false if event.pages[0].condition.switch1_valid || event.pages[0].condition.switch2_valid || event.pages[0].condition.variable_valid || event.pages[0].condition.self_switch_valid # No commands in the event return true if event.pages[0].list.length<=1 # pbPokemonMart events return true if event.pages[0].list.length<=12 && event.pages[0].graphic.character_name!="" && # Has charset event.pages[0].list[0].code==355 && # First line is Script event.pages[0].list[0].parameters[0][/^pbPokemonMart/] # pbSetPokemonCenter events return true if event.pages[0].list.length>8 && event.pages[0].graphic.character_name!="" && # Has charset event.pages[0].list[0].code==355 && # First line is Script event.pages[0].list[0].parameters[0][/^pbSetPokemonCenter/] return false end # Given two events that are next to each other, decides whether otherEvent is # likely to be a "counter event", i.e. is placed on a tile with the Counter # flag, or is on a non-passable tile between two passable tiles (e.g. a desk) # where one of those two tiles is occupied by thisEvent. def likely_counter?(thisEvent,otherEvent,mapID,mapData) # Check whether otherEvent is on a counter tile return true if mapData.isCounterTile?(mapID,otherEvent.x,otherEvent.y) # Check whether otherEvent is between an event with a graphic (e.g. an NPC) # and a spot where the player can be yonderX = otherEvent.x + (otherEvent.x - thisEvent.x) yonderY = otherEvent.y + (otherEvent.y - thisEvent.y) return thisEvent.pages[0].graphic.character_name!="" && # Has charset otherEvent.pages[0].graphic.character_name=="" && # Has no charset otherEvent.pages[0].trigger==0 && # Action trigger mapData.isPassable?(mapID,thisEvent.x,thisEvent.y) && !mapData.isPassable?(mapID,otherEvent.x,otherEvent.y) && mapData.isPassable?(mapID,yonderX,yonderY) end # Checks all events in the given map to see if any look like they've been # placed on a desk with an NPC behind it, where the event on the desk is the # actual interaction with the NPC. In other words, it's not making proper use # of the counter flag (which lets the player interact with an event on the # other side of counter tiles). # Any events found to be like this have their contents merged into the NPC # event and the counter event itself is deleted. The tile below the counter # event gets its counter flag set (if it isn't already). def check_counters(map,mapID,mapData) toDelete = [] changed = false for key in map.events.keys event = map.events[key] next if !plain_event_or_mart?(event) # Found an event that is empty or looks like a simple Mart or a Poké # Center nurse. Check adjacent events to see if they are "counter events". neighbors = [] neighbors.push(mapData.getEventFromXY(mapID,event.x,event.y-1)) neighbors.push(mapData.getEventFromXY(mapID,event.x,event.y+1)) neighbors.push(mapData.getEventFromXY(mapID,event.x-1,event.y)) neighbors.push(mapData.getEventFromXY(mapID,event.x+1,event.y)) neighbors.compact! for otherEvent in neighbors next if plain_event?(otherEvent) # Blank/cosmetic-only event next if !likely_counter?(event,otherEvent,mapID,mapData) # Found an adjacent event that looks like it's supposed to be a counter. # Set the counter flag of the tile beneath the counter event, copy the # counter event's pages to the NPC event, and delete the counter event. mapData.setCounterTile(mapID,otherEvent.x,otherEvent.y) savedPage = event.pages[0] event.pages = otherEvent.pages apply_pages(savedPage,event.pages) # Apply NPC's visuals to new event pages toDelete.push(otherEvent.id) changed = true end end toDelete.each { |key| map.events.delete(key) } return changed end #============================================================================= # Main compiler method for events #============================================================================= def compile_trainer_events(_mustcompile) mapData = MapData.new t = Time.now.to_i Graphics.update trainerChecker = TrainerChecker.new for id in mapData.mapinfos.keys.sort changed = false map = mapData.getMap(id) next if !map || !mapData.mapinfos[id] pbSetWindowText(_INTL("Processing map {1} ({2})",id,mapData.mapinfos[id].name)) for key in map.events.keys if Time.now.to_i-t>=5 Graphics.update t = Time.now.to_i end newevent = convert_to_trainer_event(map.events[key],trainerChecker) if newevent map.events[key] = newevent changed = true end newevent = convert_to_item_event(map.events[key]) if newevent map.events[key] = newevent changed = true end changed = true if fix_event_name(map.events[key]) newevent = fix_event_use(map.events[key],id,mapData) if newevent map.events[key] = newevent changed = true end end if Time.now.to_i-t>=5 Graphics.update t = Time.now.to_i end changed = true if check_counters(map,id,mapData) if changed mapData.saveMap(id) mapData.saveTilesets end end changed = false Graphics.update commonEvents = load_data("Data/CommonEvents.rxdata") pbSetWindowText(_INTL("Processing common events")) for key in 0...commonEvents.length newevent = fix_event_use(commonEvents[key],0,mapData) if newevent commonEvents[key] = newevent changed = true end end save_data(commonEvents,"Data/CommonEvents.rxdata") if changed end end