diff --git a/Data/Scripts/001_Settings.rb b/Data/Scripts/001_Settings.rb index 5a3e05608..8983222ab 100644 --- a/Data/Scripts/001_Settings.rb +++ b/Data/Scripts/001_Settings.rb @@ -186,30 +186,58 @@ module Settings 66 => [5, 21, 28, 31, 39, 41, 44, 47, 69], 69 => [5, 21, 28, 31, 39, 41, 44, 47, 66 ] } - # A set of arrays, each containing the details of a roaming Pokémon. The - # information within each array is as follows: - # * Species. - # * Level. - # * Game Switch; the Pokémon roams while this is ON. - # * Encounter type (see def pbRoamingMethodAllowed for their use): - # 0 = grass, walking in cave, surfing - # 1 = grass, walking in cave - # 2 = surfing - # 3 = fishing - # 4 = surfing, fishing - # * Name of BGM to play for that encounter (optional). - # * Roaming areas specifically for this Pokémon (optional; used instead of - # ROAMING_AREAS). + # A set of hashes, each containing the details of a roaming Pokémon. The + # information within each hash is as follows: + # * :species + # * :level + # * :icon - Filename in Graphics/UI/Town Map/ of the roamer's Town Map icon. + # * :game_switch - The Pokémon roams if this is nil or <=0 or if that Game + # Switch is ON. Optional. + # * :encounter_type - One of: + # :all = grass, walking in cave, surfing (default) + # :land = grass, walking in cave + # :water = surfing, fishing + # :surfing = surfing + # :fishing = fishing + # * :bgm - The BGM to play for the encounter. Optional. + # * :areas - A hash of map IDs that determine where this Pokémon roams. Used + # instead of ROAMING_AREAS above. Optional. ROAMING_SPECIES = [ - [:LATIAS, 30, 53, 0, "Battle roaming"], - [:LATIOS, 30, 53, 0, "Battle roaming"], - [:KYOGRE, 40, 54, 2, nil, { - 2 => [ 21, 31 ], - 21 => [2, 31, 69], - 31 => [2, 21, 69], - 69 => [ 21, 31 ] - }], - [:ENTEI, 40, 55, 1] + { + :species => :LATIAS, + :level => 30, + :icon => "pin_latias", + :game_switch => 53, + :encounter_type => :all, + :bgm => "Battle roaming" + }, + { + :species => :LATIOS, + :level => 30, + :icon => "pin_latios", + :game_switch => 53, + :encounter_type => :all, + :bgm => "Battle roaming" + }, + { + :species => :KYOGRE, + :level => 40, + :game_switch => 54, + :encounter_type => :surfing, + :areas => { + 2 => [ 21, 31 ], + 21 => [2, 31, 69], + 31 => [2, 21, 69], + 69 => [ 21, 31 ] + } + }, + { + :species => :ENTEI, + :level => 40, + :icon => "pin_entei", + :game_switch => 55, + :encounter_type => :land + } ] #----------------------------------------------------------------------------- diff --git a/Data/Scripts/003_New_UI_Settings.rb b/Data/Scripts/003_New_UI_Settings.rb index 1183665a4..21931b7d8 100644 --- a/Data/Scripts/003_New_UI_Settings.rb +++ b/Data/Scripts/003_New_UI_Settings.rb @@ -44,6 +44,13 @@ module Settings # by the Pokémon that knows it. SHOW_MODIFIED_MOVE_PROPERTIES = false + # Whether pressing Use in the Town Map will zoom it in to 200% and show a text + # pane on the right showing the selected point's description. The cursor can + # still be moved while zoomed in. + ENABLE_TOWN_MAP_ZOOM_IN_FOR_DETAILS = true + # Whether points in the Town Map can be marked. + ENABLE_TOWN_MAP_MARKING = true + # TODO: Allow renaming a Pokémon from the party screen/summary screen (not # sure which). Gen 9 feature. # TODO: Allow forgetting/remembering moves from the summary screen. Gen 9 diff --git a/Data/Scripts/010_Data/002_PBS data/002_TownMap.rb b/Data/Scripts/010_Data/002_PBS data/002_TownMap.rb index 07d3a167f..6a8c4d056 100644 --- a/Data/Scripts/010_Data/002_PBS data/002_TownMap.rb +++ b/Data/Scripts/010_Data/002_PBS data/002_TownMap.rb @@ -6,7 +6,10 @@ module GameData attr_reader :id attr_reader :real_name attr_reader :filename - attr_reader :point + attr_reader :margins + attr_reader :point_size + attr_reader :size + attr_reader :points attr_reader :flags attr_reader :pbs_file_suffix @@ -14,23 +17,43 @@ module GameData DATA_FILENAME = "town_map.dat" PBS_BASE_FILENAME = "town_map" SCHEMA = { - "SectionName" => [:id, "u"], - "Name" => [:real_name, "s"], - "Filename" => [:filename, "s"], - "Point" => [:point, "^uusSUUUU"], - "Flags" => [:flags, "*s"] + "SectionName" => [:id, "u"], + "Name" => [:real_name, "s"], + "Filename" => [:filename, "s"], + "Margins" => [:margins, "uu"], # Left/right and top/bottom padding in pixels + "PointSize" => [:point_size, "vv"], # Size of a point in pixels + "Size" => [:size, "vv"], # Width and height in points + "Point" => [:points, "^uusSUUUU"], + "Flags" => [:flags, "*s"] + } + # This schema is for definable properties of individual points (apart from + # position and name which are above). + SUB_SCHEMA = { + "Image" => [:image, "s"], + "Description" => [:real_description, "q"], + "FlySpot" => [:fly_spot, "vuu"], # Map ID, x coord, y coord + "HideFlyIcon" => [:hide_fly_icon, "b"], + "FlyIconOffset" => [:fly_icon_offset, "ii"], # x and y offsets in pixels + "Switch" => [:switch, "v"] # Game Switch ID } extend ClassMethodsIDNumbers include InstanceMethods + def self.sub_schema + return SUB_SCHEMA + end + #--------------------------------------------------------------------------- def initialize(hash) @id = hash[:id] @real_name = hash[:real_name] || "???" @filename = hash[:filename] - @point = hash[:point] || [] + @margins = hash[:margins] || [0, 0] + @point_size = hash[:point_size] || [16, 16] + @size = hash[:size] || [30, 20] + @points = hash[:points] || [] @flags = hash[:flags] || [] @pbs_file_suffix = hash[:pbs_file_suffix] || "" end @@ -43,5 +66,18 @@ module GameData def has_flag?(flag) return @flags.any? { |f| f.downcase == flag.downcase } end + + def get_point_property_for_PBS(key, index = 0) + return [*@points[index][:position], @points[index][:real_name]] if key == "Point" + ret = @points[index][SUB_SCHEMA[key][0]] + ret = nil if ret == false || (ret.is_a?(Array) && ret.length == 0) || ret == "" + case key + when "Margins" + ret = nil if ret == [0, 0] + when "FlySpot" + ret = nil if ret && ret.compact.empty? + end + return ret + end end end diff --git a/Data/Scripts/012_Overworld/002_Battle triggering/005_Overworld_RoamingPokemon.rb b/Data/Scripts/012_Overworld/002_Battle triggering/005_Overworld_RoamingPokemon.rb index f7738fc5f..5ef6c81a3 100644 --- a/Data/Scripts/012_Overworld/002_Battle triggering/005_Overworld_RoamingPokemon.rb +++ b/Data/Scripts/012_Overworld/002_Battle triggering/005_Overworld_RoamingPokemon.rb @@ -17,6 +17,15 @@ end #=============================================================================== # Making roaming Pokémon roam around. #=============================================================================== +def each_active_roamer(ignore_caught = true) + Settings::ROAMING_SPECIES.each_with_index do |roamer, i| + next if !GameData::Species.exists?(roamer[:species]) + next if roamer[:game_switch] && roamer[:game_switch] > 0 && !$game_switches[roamer[:game_switch]] + next if ignore_caught && $PokemonGlobal.roamPokemon[i] == true # Has been caught + yield roamer, i + end +end + # Resets all roaming Pokemon that were defeated without having been caught. def pbResetAllRoamers return if !$PokemonGlobal.roamPokemon @@ -30,7 +39,7 @@ end def pbRoamingAreas(idxRoamer) # [species ID, level, Game Switch, encounter type, battle BGM, area maps hash] roamData = Settings::ROAMING_SPECIES[idxRoamer] - return roamData[5] if roamData && roamData[5] + return roamData[:areas] if roamData && roamData[:areas] return Settings::ROAMING_AREAS end @@ -47,8 +56,8 @@ def pbRoamPokemon # Start all roamers off in random maps if !$PokemonGlobal.roamPosition $PokemonGlobal.roamPosition = {} - Settings::ROAMING_SPECIES.length.times do |i| - next if !GameData::Species.exists?(Settings::ROAMING_SPECIES[i][0]) + Settings::ROAMING_SPECIES.each_with_index do |roamer, i| + next if !GameData::Species.exists?(roamer[:species]) keys = pbRoamingAreas(i).keys $PokemonGlobal.roamPosition[i] = keys[rand(keys.length)] end @@ -62,10 +71,9 @@ end # Makes a single roaming Pokémon roam to another map. Doesn't roam if it isn't # currently possible to encounter it (i.e. its Game Switch is off). def pbRoamPokemonOne(idxRoamer) - # [species ID, level, Game Switch, encounter type, battle BGM, area maps hash] - roamData = Settings::ROAMING_SPECIES[idxRoamer] - return if roamData[2] > 0 && !$game_switches[roamData[2]] # Game Switch is off - return if !GameData::Species.exists?(roamData[0]) + roamer = Settings::ROAMING_SPECIES[idxRoamer] + return if !GameData::Species.exists?(roamer[:species]) + return if roamer[:game_switch] && roamer[:game_switch] > 0 && !$game_switches[roamer[:game_switch]] # Get hash of area patrolled by the roaming Pokémon mapIDs = pbRoamingAreas(idxRoamer).keys return if !mapIDs || mapIDs.length == 0 # No roaming area defined somehow @@ -81,10 +89,10 @@ def pbRoamPokemonOne(idxRoamer) return if !nextMaps nextMaps.each { |map| newMapChoices.push(map) } # Rarely, add a random possible map into the mix - newMapChoices.push(mapIDs[rand(mapIDs.length)]) if rand(32) == 0 + newMapChoices.push(mapIDs.sample) if rand(32) == 0 # Choose a random new map to roam to if newMapChoices.length > 0 - $PokemonGlobal.roamPosition[idxRoamer] = newMapChoices[rand(newMapChoices.length)] + $PokemonGlobal.roamPosition[idxRoamer] = newMapChoices.sample end end @@ -118,16 +126,16 @@ def pbRoamingMethodAllowed(roamer_method) enc_type = $PokemonEncounters.encounter_type type = GameData::EncounterType.get(enc_type).type case roamer_method - when 0 # Any step-triggered method (except Bug Contest) + when :all # Any step-triggered method (except Bug Contest) return [:land, :cave, :water].include?(type) - when 1 # Walking (except Bug Contest) + when :land # Walking (except Bug Contest) return [:land, :cave].include?(type) - when 2 # Surfing - return type == :water - when 3 # Fishing - return type == :fishing - when 4 # Water-based + when :water # Surfing or rishing return [:water, :fishing].include?(type) + when :surfing # Surfing + return type == :water + when :fishing # Fishing + return type == :fishing end return false end @@ -146,11 +154,7 @@ EventHandlers.add(:on_wild_species_chosen, :roaming_pokemon, currentRegion = pbGetCurrentRegion currentMapName = $game_map.name possible_roamers = [] - Settings::ROAMING_SPECIES.each_with_index do |data, i| - # data = [species, level, Game Switch, roamer method, battle BGM, area maps hash] - next if !GameData::Species.exists?(data[0]) - next if data[2] > 0 && !$game_switches[data[2]] # Isn't roaming - next if $PokemonGlobal.roamPokemon[i] == true # Roaming Pokémon has been caught + each_active_roamer do |roamer, i| # Get the roamer's current map roamerMap = $PokemonGlobal.roamPosition[i] if !roamerMap @@ -168,9 +172,9 @@ EventHandlers.add(:on_wild_species_chosen, :roaming_pokemon, next if pbGetMapNameFromId(roamerMap) != currentMapName end # Check whether the roamer's roamer method is currently possible - next if !pbRoamingMethodAllowed(data[3]) + next if !pbRoamingMethodAllowed(roamer[:encounter_type]) # Add this roaming Pokémon to the list of possible roaming Pokémon to encounter - possible_roamers.push([i, data[0], data[1], data[4]]) # [i, species, level, BGM] + possible_roamers.push([i, roamer[:species], roamer[:level], roamer[:bgm]]) end # No encounterable roaming Pokémon were found, just have the regular encounter next if possible_roamers.length == 0 diff --git a/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb b/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb index 7cf9401f7..db8edf6da 100644 --- a/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb +++ b/Data/Scripts/012_Overworld/002_Overworld_Metadata.rb @@ -23,6 +23,8 @@ class PokemonGlobalMetadata attr_accessor :pokedexDex # Dex currently looking at (-1 is National Dex) attr_accessor :pokedexIndex # Last species viewed per Dex attr_accessor :pokedexMode # Search mode + # Town Map + attr_accessor :townMapMarkings # Day Care attr_accessor :day_care # Special battle modes @@ -78,6 +80,8 @@ class PokemonGlobalMetadata (numRegions + 1).times do |i| # National Dex isn't a region, but is included @pokedexIndex[i] = 0 end + # Town Map + @townMapMarkings = [] # Day Care @day_care = DayCare.new # Special battle modes diff --git a/Data/Scripts/013_Items/002_Item_Effects.rb b/Data/Scripts/013_Items/002_Item_Effects.rb index f14065617..b70eca1b6 100644 --- a/Data/Scripts/013_Items/002_Item_Effects.rb +++ b/Data/Scripts/013_Items/002_Item_Effects.rb @@ -313,11 +313,10 @@ ItemHandlers::UseInField.copy(:ITEMFINDER, :DOWSINGMCHN, :DOWSINGMACHINE) ItemHandlers::UseFromBag.add(:TOWNMAP, proc { |item, bag_screen| pbFadeOutInWithUpdate(bag_screen&.sprites) do - scene = PokemonRegionMap_Scene.new(-1, false) - screen = PokemonRegionMapScreen.new(scene) - ret = screen.pbStartScreen - if ret - $game_temp.fly_destination = ret + town_map_screen = UI::TownMap.new + town_map_screen.main + if town_map_screen.result + $game_temp.fly_destination = town_map_screen.result bag_screen&.silent_end_screen end end diff --git a/Data/Scripts/016_UI/004_UI_Pokedex_Entry.rb b/Data/Scripts/016_UI/004_UI_Pokedex_Entry.rb index c2b43b0ed..59bd0caa4 100644 --- a/Data/Scripts/016_UI/004_UI_Pokedex_Entry.rb +++ b/Data/Scripts/016_UI/004_UI_Pokedex_Entry.rb @@ -2,6 +2,13 @@ # #=============================================================================== class PokemonPokedexInfo_Scene + LEFT = 0 + TOP = 0 + RIGHT = 29 + BOTTOM = 19 + SQUARE_WIDTH = 16 + SQUARE_HEIGHT = 16 + def pbStartScene(dexlist, index, region) @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) @viewport.z = 99999 @@ -31,8 +38,8 @@ class PokemonPokedexInfo_Scene pbDrawImagePositions( @sprites["areamap"].bitmap, [["Graphics/UI/Town Map/#{hidden[4]}", - hidden[2] * PokemonRegionMap_Scene::SQUARE_WIDTH, - hidden[3] * PokemonRegionMap_Scene::SQUARE_HEIGHT]] + hidden[2] * SQUARE_WIDTH, + hidden[3] * SQUARE_HEIGHT]] ) end @sprites["areahighlight"] = BitmapSprite.new(Graphics.width, Graphics.height, @viewport) @@ -315,12 +322,12 @@ class PokemonPokedexInfo_Scene # defined point in town_map.txt, and which either have no Self Switch # controlling their visibility or whose Self Switch is ON) visible_points = [] - @mapdata.point.each do |loc| - next if loc[7] && !$game_switches[loc[7]] # Point is not visible - visible_points.push([loc[0], loc[1]]) + @mapdata.points.each do |loc| + next if loc[:switch] && !$game_switches[loc[:switch]] # Point is not visible + visible_points.push(loc[:position]) end # Find all points with a visible area for @species - town_map_width = 1 + PokemonRegionMap_Scene::RIGHT - PokemonRegionMap_Scene::LEFT + town_map_width = 1 + RIGHT - LEFT ret = [] GameData::Encounter.each_of_version($PokemonGlobal.encounter_version) do |enc_data| next if !pbFindEncounter(enc_data.types, @species) # Species isn't in encounter table @@ -362,9 +369,9 @@ class PokemonPokedexInfo_Scene # Draw coloured squares on each point of the Town Map with a nest pointcolor = Color.new(0, 248, 248) pointcolorhl = Color.new(192, 248, 248) - town_map_width = 1 + PokemonRegionMap_Scene::RIGHT - PokemonRegionMap_Scene::LEFT - sqwidth = PokemonRegionMap_Scene::SQUARE_WIDTH - sqheight = PokemonRegionMap_Scene::SQUARE_HEIGHT + town_map_width = 1 + RIGHT - LEFT + sqwidth = SQUARE_WIDTH + sqheight = SQUARE_HEIGHT points.length.times do |j| next if !points[j] x = (j % town_map_width) * sqwidth diff --git a/Data/Scripts/016_UI/008_UI_Pokegear.rb b/Data/Scripts/016_UI/008_UI_Pokegear.rb index 664fb93e9..37ef8b9e4 100644 --- a/Data/Scripts/016_UI/008_UI_Pokegear.rb +++ b/Data/Scripts/016_UI/008_UI_Pokegear.rb @@ -164,11 +164,10 @@ MenuHandlers.add(:pokegear_menu, :map, { "order" => 10, "effect" => proc { |menu| pbFadeOutIn do - scene = PokemonRegionMap_Scene.new(-1, false) - screen = PokemonRegionMapScreen.new(scene) - ret = screen.pbStartScreen - if ret - $game_temp.fly_destination = ret + town_map_screen = UI::TownMap.new + town_map_screen.main + if town_map_screen.result + $game_temp.fly_destination = town_map_screen.result menu.dispose next 99999 end diff --git a/Data/Scripts/016_UI/016_UI_ReadyMenu.rb b/Data/Scripts/016_UI/016_UI_ReadyMenu.rb index 62da5d22e..8e3a15959 100644 --- a/Data/Scripts/016_UI/016_UI_ReadyMenu.rb +++ b/Data/Scripts/016_UI/016_UI_ReadyMenu.rb @@ -263,9 +263,9 @@ class PokemonReadyMenu ret = nil pbFadeOutInWithUpdate(@scene.sprites) do pbHideMenu - scene = PokemonRegionMap_Scene.new(-1, false) - screen = PokemonRegionMapScreen.new(scene) - ret = screen.pbStartFlyScreen + town_map_screen = UI::TownMap.new(mode: :fly) + town_map_screen.main + ret = town_map_screen.result pbShowMenu if !ret end if ret diff --git a/Data/Scripts/016b_UI redesign/000_UI_base.rb b/Data/Scripts/016b_UI redesign/000_UI_base.rb index 79d420588..66431d89d 100644 --- a/Data/Scripts/016b_UI redesign/000_UI_base.rb +++ b/Data/Scripts/016b_UI redesign/000_UI_base.rb @@ -307,6 +307,7 @@ module UI #============================================================================= class BaseVisuals attr_reader :sprites + attr_reader :mode BACKGROUND_FILENAME = "bg" @@ -709,6 +710,7 @@ module UI #============================================================================= class BaseScreen attr_reader :visuals + attr_reader :mode attr_accessor :result def initialize diff --git a/Data/Scripts/016b_UI redesign/001_UI_PauseMenu.rb b/Data/Scripts/016b_UI redesign/001_UI_PauseMenu.rb index 087cfe7ee..fd6a68ae5 100644 --- a/Data/Scripts/016b_UI redesign/001_UI_PauseMenu.rb +++ b/Data/Scripts/016b_UI redesign/001_UI_PauseMenu.rb @@ -256,9 +256,9 @@ MenuHandlers.add(:pause_menu, :town_map, { "effect" => proc { |menu| pbPlayDecisionSE pbFadeOutIn do - scene = PokemonRegionMap_Scene.new(-1, false) - screen = PokemonRegionMapScreen.new(scene) - ret = screen.pbStartScreen + town_map_screen = UI::TownMap.new + town_map_screen.main + ret = town_map_screen.result $game_temp.fly_destination = ret if ret ($game_temp.fly_destination) ? menu.silent_end_screen : menu.refresh end diff --git a/Data/Scripts/016b_UI redesign/005_UI_Party.rb b/Data/Scripts/016b_UI redesign/005_UI_Party.rb index 406216ccc..1969b6000 100644 --- a/Data/Scripts/016b_UI redesign/005_UI_Party.rb +++ b/Data/Scripts/016b_UI redesign/005_UI_Party.rb @@ -738,7 +738,7 @@ end # #=============================================================================== class UI::Party < UI::BaseScreen - attr_reader :party, :mode + attr_reader :party SCREEN_ID = :party_screen @@ -930,13 +930,12 @@ class UI::Party < UI::BaseScreen if pbCanUseHiddenMove?(pkmn, move_id) && pbConfirmUseHiddenMove(pkmn, move_id) if move_id == :FLY pbFadeOutInWithUpdate(sprites) do - town_map_scene = PokemonRegionMap_Scene.new(-1, false) - town_map_screen = PokemonRegionMapScreen.new(town_map_scene) - ret = town_map_screen.pbStartFlyScreen - if ret + town_map_screen = UI::TownMap.new(mode: :fly) + town_map_screen.main + if town_map_screen.result $game_temp.field_move_to_use = move_id $game_temp.field_move_user = pkmn - $game_temp.fly_destination = ret + $game_temp.fly_destination = town_map_screen.result silent_end_screen end end diff --git a/Data/Scripts/016b_UI redesign/006_UI_Summary.rb b/Data/Scripts/016b_UI redesign/006_UI_Summary.rb index cb9b94413..891660dfd 100644 --- a/Data/Scripts/016b_UI redesign/006_UI_Summary.rb +++ b/Data/Scripts/016b_UI redesign/006_UI_Summary.rb @@ -1372,7 +1372,7 @@ end # #=============================================================================== class UI::PokemonSummary < UI::BaseScreen - attr_reader :party, :mode + attr_reader :party attr_accessor :party_index, :pokemon SCREEN_ID = :summary_screen diff --git a/Data/Scripts/016b_UI redesign/009_UI_TownMap.rb b/Data/Scripts/016b_UI redesign/009_UI_TownMap.rb new file mode 100644 index 000000000..cb6ab18b1 --- /dev/null +++ b/Data/Scripts/016b_UI redesign/009_UI_TownMap.rb @@ -0,0 +1,1078 @@ +#=============================================================================== +# +#=============================================================================== +class UI::TownMapVisuals < UI::BaseVisuals + attr_reader :region, :visited_regions + + GRAPHICS_FOLDER = "Town Map/" # Subfolder in Graphics/UI + MAP_TOP_LEFT = [16, 16] # Top left of map's display area in pixels + MAP_SIZE = [480, 320] # Size of display area for map in pixels, NOT the map graphic's size + MAP_SCROLL_PADDING = [64, 64] # In pixels. Don't make these more than half of MAP_SIZE! + CURSOR_MOVE_TIME = 0.08 # In seconds + ZOOM_TIME = 0.2 # In seconds + ZOOM_CURSOR_POSITION = [MAP_SIZE[0] / 4, MAP_SIZE[1] / 2] + TEXT_COLOR_THEMES = { # These color themes are added to @sprites[:overlay] + :default => [Color.new(248, 248, 248), Color.new(0, 0, 0)], # Base and shadow colour + :black => [Color.new(64, 64, 64), Color.new(176, 176, 176)] + } + MARKINGS_COUNT = 4 # Number of markings a point can have + MARKING_SPACING = 8 # In the markings panel (where markings are changed) + + def initialize(region = 0, mode = :normal) + @region = region + @mode = mode + @sub_mode = :none # Could be toggled to :fly + @cursor_offset = {:x => 0, :y => 0} + load_region_data + find_visited_regions + super() + set_player_position + @sprites[:cursor].x, @sprites[:cursor].y = point_to_screen(@cursor_pos[:x], @cursor_pos[:y]) + center_map_on_cursor + end + + def initialize_viewport + @map_viewport = Viewport.new(*MAP_TOP_LEFT, *MAP_SIZE) + @map_viewport.z = 99999 + @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height) + @viewport.z = @map_viewport.z + 1 + end + + def initialize_bitmaps + @bitmaps[:input_icons] = AnimatedBitmap.new(UI_FOLDER + "input_icons") + @bitmaps[:map_markings] = AnimatedBitmap.new(graphics_folder + "map_markings") + @bitmaps[:details_marking_bg] = AnimatedBitmap.new(graphics_folder + themed_filename("details_marking_bg")) + end + + def initialize_background + addBackgroundPlane(@sprites, :background, self.class::GRAPHICS_FOLDER + themed_filename(self.class::BACKGROUND_FILENAME), @viewport) + @sprites[:background].z = -1000 + end + + def initialize_overlay + add_overlay(:map_name_overlay, 234, 32) + @sprites[:map_name_overlay].x = 262 + @sprites[:map_name_overlay].y = Graphics.height - 40 + if @mode == :wall_map + @sprites[:map_name_overlay].x = (Graphics.width - @sprites[:map_name_overlay].width) / 2 + @sprites[:map_name_overlay].y += 4 + end + add_overlay(:input_helpers_overlay, 256, 42) + @sprites[:input_helpers_overlay].y = Graphics.height - 42 + end + + def initialize_sprites + initialize_map_sprite + initialize_map_overlay + initialize_pins + initialize_cursor + initialize_details_panel + initialize_marking_panel + generate_fly_icons if @mode == :fly + end + + def initialize_map_sprite + add_icon_sprite(:map, 0, 0, graphics_folder + @map_data.filename) + @sprites[:map].viewport = @map_viewport + end + + # An overlay sprite for the map, which zooms in as it does. Used for drawing + # unlockable things onto the map. + def initialize_map_overlay + add_overlay(:map_overlay, @sprites[:map].width, @sprites[:map].height) + @sprites[:map_overlay].viewport = @map_viewport + @sprites[:map_overlay].z = 1 + end + + # These are anything drawn onto the map but which will not enlarge if the map + # is zoomed in. + def initialize_pins + @pins_pos ||= {} + # Markings + $PokemonGlobal.townMapMarkings ||= [] + $PokemonGlobal.townMapMarkings[@region] ||= [] + @sprites.each_pair { |key, sprite| sprite.dispose if key.to_s.include?("mark_") } + @sprites.delete_if { |key, sprite| key.to_s.include?("mark_") } + @pins_pos.delete_if { |key, sprite| key.to_s.include?("mark_") } + $PokemonGlobal.townMapMarkings[@region].each do |marking| + next if !marking || marking[2].all?(0) + key = "mark_#{marking[0]}_#{marking[1]}".to_sym + create_pin(key, marking[0], marking[1], graphics_folder + themed_filename("icon_marking"), 50) + end + # Roamer icons + @sprites.each_pair { |key, sprite| sprite.dispose if key.to_s.include?("roamer_") } + @sprites.delete_if { |key, sprite| key.to_s.include?("roamer_") } + @pins_pos.delete_if { |key, sprite| key.to_s.include?("roamer_") } + if @mode != :wall_map + each_active_roamer do |roamer, i| + next if !roamer[:icon] + roamer_map = GameData::MapMetadata.try_get($PokemonGlobal.roamPosition[i]) + return if !roamer_map || !roamer_map.town_map_position + return if roamer_map.town_map_position[0] != @region + key = "roamer_#{i}".to_sym + @pins_pos[key] = [roamer_map.town_map_position[1], roamer_map.town_map_position[2]] + if roamer_map.town_map_size + area_width = roamer_map.town_map_size[0] + area_height = (roamer_map.town_map_size[1].length.to_f / roamer_map.town_map_size[0]).ceil + @pins_pos[key][0] += (area_width.to_f - 1) / 2 + @pins_pos[key][1] += (area_height.to_f - 1) / 2 + end + create_pin(key, @pins_pos[key][0], @pins_pos[key][1], graphics_folder + roamer[:icon], 80) + end + end + # Player's head showing their current location + if !@pins_pos[:player] + create_pin(:player, 0, 0, GameData::TrainerType.player_map_icon_filename($player.trainer_type), 100) + end + end + + def create_pin(key, this_x, this_y, filename, this_z) + @pins_pos[key] = [this_x, this_y] + add_icon_sprite(key, 0, 0, filename) + @sprites[key].x, @sprites[key].y = point_to_screen(this_x, this_y) + @sprites[key].z = this_z + @sprites[key].ox = @sprites[key].width / 2 + @sprites[key].oy = @sprites[key].height / 2 + @sprites[key].viewport = @map_viewport + end + + def initialize_cursor + @cursor_pos = {:x => 0, :y => 0} # In points, not pixels + @sprites[:cursor] = AnimatedSprite.create( + graphics_folder + themed_filename("cursor"), 2, 5, @map_viewport # 2 frames, 5/20 seconds per frame + ) + @sprites[:cursor].z = 1000 + @sprites[:cursor].ox = @sprites[:cursor].height / 2 + @sprites[:cursor].oy = @sprites[:cursor].height / 2 + @sprites[:cursor].play + end + + def initialize_details_panel + return if !Settings::ENABLE_TOWN_MAP_ZOOM_IN_FOR_DETAILS + add_icon_sprite(:details, 256, 16, graphics_folder + themed_filename("details_panel")) + @sprites[:details].z = 900 + @sprites[:details].visible = false + add_overlay(:details_overlay, @sprites[:details].width, @sprites[:details].height) + @sprites[:details_overlay].x = @sprites[:details].x + @sprites[:details_overlay].y = @sprites[:details].y + @sprites[:details_overlay].z = @sprites[:details].z + 1 + @sprites[:details_overlay].visible = false + end + + def initialize_marking_panel + # Background + add_icon_sprite(:marking_bg, 0, 0, graphics_folder + themed_filename("marking_bg")) + @sprites[:marking_bg].z = 1000 + @sprites[:marking_bg].visible = false + # Overlay + add_overlay(:marking_overlay, @sprites[:marking_bg].width, @sprites[:marking_bg].height) + @sprites[:marking_overlay].x = @sprites[:marking_bg].x + @sprites[:marking_overlay].y = @sprites[:marking_bg].y + @sprites[:marking_overlay].z = @sprites[:marking_bg].z + 1 + @sprites[:marking_overlay].visible = false + # Cursor + @sprites[:marking_cursor] = AnimatedSprite.create( + graphics_folder + themed_filename("marking_cursor"), 2, 5, @viewport # 2 frames, 5/20 seconds per frame + ) + @sprites[:marking_cursor].z = @sprites[:marking_overlay].z + 1 + @sprites[:marking_cursor].ox = @sprites[:marking_cursor].height / 2 + @sprites[:marking_cursor].oy = @sprites[:marking_cursor].height / 2 + @sprites[:marking_cursor].visible = false + @sprites[:marking_cursor].play + end + + def dispose + super + @map_viewport.dispose + @viewport.dispose + end + + #----------------------------------------------------------------------------- + + def themed_filename(base_filename) + return filename_with_appendix(base_filename, "_wall") if @mode == :wall_map + # NOTE: The Pokégear theme would be manually chosen by the player, and would + # not depend on the player's gender. However, because there isn't a + # variable that contains the Pokégear's theme, so for the sake of + # example, the player's gender matters here. + return filename_with_appendix(base_filename, "_f") if $player&.female? + return base_filename + end + + def load_region_data + if !GameData::TownMap.exists?(@region) + raise _INTL("No Town Map data is defined for region {1}.", @region) + end + @map_data = GameData::TownMap.get(@region) + end + + def find_visited_regions + @visited_regions = [] + GameData::MapMetadata.each do |map_data| + next if !map_data.town_map_position || @visited_regions.include?(map_data.town_map_position[0]) + next if !$PokemonGlobal.visitedMaps[map_data.id] + @visited_regions.push(map_data.town_map_position[0]) + end + end + + def set_region(new_region) + return if @region == new_region + @region = new_region + load_region_data + @sprites[:map].setBitmap(graphics_folder + @map_data.filename) + initialize_pins + set_player_position + @sprites[:cursor].x, @sprites[:cursor].y = point_to_screen(@cursor_pos[:x], @cursor_pos[:y]) + center_map_on_cursor + refresh + end + + def each_fly_point + @map_data.points.each do |point| + yield point if point[:fly_spot] + end + end + + def get_point_data(this_x = -1, this_y = -1) + this_x = @cursor_pos[:x] if this_x < 0 + this_y = @cursor_pos[:y] if this_y < 0 + @map_data.points.each do |point| + next if point[:position][0] != this_x || point[:position][1] != this_y + return nil if point[:switch] && (@mode == :wall_map || point[:switch] <= 0 || !$game_switches[point[:switch]]) + return point + end + return nil + end + + #----------------------------------------------------------------------------- + + # Positions the player's head on the town map. It is invisible if the player + # isn't in a map belonging to that town map. Also sets the cursor's position + # to the same point. + def set_player_position + map_pos = $game_map.metadata&.town_map_position + if map_pos.nil? || map_pos[0] != @region + set_cursor_pos((@map_data.size[0] - 1) / 2, (@map_data.size[1] - 1) / 2) # Middle of the map + @sprites[:player].visible = false + return + end + @pins_pos[:player] = [map_pos[1], map_pos[2]] + map_size = $game_map.metadata&.town_map_size + if map_size + area_width = map_size[0] + area_height = (map_size[1].length.to_f / map_size[0]).ceil + @pins_pos[:player][0] += ($game_player.x * area_width / $game_map.width).floor if area_width > 1 + @pins_pos[:player][1] += ($game_player.y * area_height / $game_map.height).floor if area_height > 1 + end + @sprites[:player].x, @sprites[:player].y = point_to_screen(*@pins_pos[:player]) + @sprites[:player].visible = true + set_cursor_pos(*@pins_pos[:player]) # In points + end + + def can_move_to_point?(new_x, new_y) + return false if new_x < 0 || new_x >= @map_data.size[0] + return false if new_y < 0 || new_y >= @map_data.size[1] + return true + end + + def cursor_moving? + return @cursor_offset[:x] != 0 || @cursor_offset[:y] != 0 + end + + def set_cursor_pos(new_x, new_y) + changed = (@cursor_pos[:x] != new_x) || (@cursor_pos[:y] != new_y) + @cursor_pos[:x] = new_x + @cursor_pos[:y] = new_y + refresh_on_cursor_move if changed + end + + def point_to_screen(point_x, point_y) + return point_x_to_screen_x(point_x), point_y_to_screen_y(point_y) + end + + # Returns the x coordinate of the middle of the point. + def point_x_to_screen_x(value) + return ((value * @map_data.point_size[0]) + (@map_data.point_size[0] / 2) + @map_data.margins[0]) * @sprites[:map].zoom_x + end + + # Returns the y coordinate of the middle of the point. + def point_y_to_screen_y(value) + return ((value * @map_data.point_size[1]) + (@map_data.point_size[1] / 2) + @map_data.margins[1]) * @sprites[:map].zoom_y + end + + # Called during initialization only. + def center_map_on_cursor + @map_viewport.ox = @sprites[:cursor].x - (MAP_SIZE[0] / 2) + @map_viewport.oy = @sprites[:cursor].y - (MAP_SIZE[1] / 2) + clamp_map_position + end + + def clamp_map_position + max_ox = ((@map_data.size[0] * @map_data.point_size[0]) + (@map_data.margins[0] * 2)) * @sprites[:map].zoom_x + max_oy = ((@map_data.size[1] * @map_data.point_size[1]) + (@map_data.margins[1] * 2)) * @sprites[:map].zoom_y + if zoomed? + max_ox -= ZOOM_CURSOR_POSITION[0] * 2 + max_oy -= ZOOM_CURSOR_POSITION[1] * 2 + else + max_ox -= MAP_SIZE[0] + max_oy -= MAP_SIZE[1] + end + @map_viewport.ox = max_ox if @map_viewport.ox > max_ox + @map_viewport.oy = max_oy if @map_viewport.oy > max_oy + @map_viewport.ox = 0 if @map_viewport.ox < 0 + @map_viewport.oy = 0 if @map_viewport.oy < 0 + end + + #----------------------------------------------------------------------------- + + def screen_menu_options + ret = [] + MenuHandlers.each_available(:town_map_menu, self) do |option, _hash, _name| + ret.push(option) + end + return ret + end + + def can_access_screen_menu? + return false if @mode != :normal || @sub_mode == :fly || zoomed? + return screen_menu_options.length > 1 # At least 1 command (plus "Cancel") + end + + #----------------------------------------------------------------------------- + + def zoomed? + return @sprites[:map].zoom_x > 1 + end + + def can_zoom? + return Settings::ENABLE_TOWN_MAP_ZOOM_IN_FOR_DETAILS && @mode == :normal && !zoomed? + end + + def zoom_in + @sprites[:details].visible = true + @sprites[:details_overlay].visible = true + @sprites[:input_helpers_overlay].visible = false + refresh_details_panel(false) + start_ox = @map_viewport.ox + start_oy = @map_viewport.oy + max_ox = (((@map_data.size[0] * @map_data.point_size[0]) + (@map_data.margins[0] * 2)) * 2) - (MAP_SIZE[0] / 2) + max_oy = (((@map_data.size[1] * @map_data.point_size[1]) + (@map_data.margins[1] * 2)) * 2) - MAP_SIZE[1] + end_ox = (@sprites[:cursor].x * 2) - ZOOM_CURSOR_POSITION[0] + end_ox = 0 if end_ox < 0 + end_ox = max_ox if end_ox > max_ox + end_oy = (@sprites[:cursor].y * 2) - ZOOM_CURSOR_POSITION[1] + end_oy = 0 if end_oy < 0 + end_oy = max_oy if end_oy > max_oy + # Animate the zoom in + animate_zoom(1, 2, start_ox, end_ox, start_oy, end_oy) + @sprites[:input_helpers_overlay].visible = true + refresh_input_helpers + end + + def zoom_out + @sprites[:input_helpers_overlay].visible = false + start_ox = @map_viewport.ox + start_oy = @map_viewport.oy + max_ox = (@map_data.size[0] * @map_data.point_size[0]) + (@map_data.margins[0] * 2) - MAP_SIZE[0] + max_oy = (@map_data.size[1] * @map_data.point_size[1]) + (@map_data.margins[1] * 2) - MAP_SIZE[1] + end_ox = @sprites[:cursor].x - MAP_SIZE[0] / 2 + end_ox = 0 if end_ox < 0 + end_ox = max_ox if end_ox > max_ox + end_oy = @sprites[:cursor].y - MAP_SIZE[1] / 2 + end_oy = 0 if end_oy < 0 + end_oy = max_oy if end_oy > max_oy + # Animate the zoom in + animate_zoom(2, 1, start_ox, end_ox, start_oy, end_oy) + @sprites[:details].visible = false + @sprites[:details_overlay].visible = false + @sprites[:input_helpers_overlay].visible = true + refresh_input_helpers + end + + def animate_zoom(start_zoom, end_zoom, start_ox, end_ox, start_oy, end_oy) + timer_start = System.uptime + loop do + Graphics.update + update_visuals + now = System.uptime + @map_viewport.ox = lerp(start_ox, end_ox, ZOOM_TIME, timer_start, now) + @map_viewport.oy = lerp(start_oy, end_oy, ZOOM_TIME, timer_start, now) + @sprites[:map].zoom_x = lerp(start_zoom, end_zoom, ZOOM_TIME, timer_start, now) + @sprites[:map].zoom_y = @sprites[:map].zoom_x + @sprites[:map_overlay].zoom_x = @sprites[:map].zoom_x + @sprites[:map_overlay].zoom_y = @sprites[:map].zoom_y + update_pin_positions_while_zooming + break if timer_start + ZOOM_TIME <= now + end + end + + def update_pin_positions_while_zooming + @sprites[:cursor].x, @sprites[:cursor].y = point_to_screen(@cursor_pos[:x], @cursor_pos[:y]) + # Player, roamers, markings + @pins_pos.each_pair do |key, pos| + @sprites[key].x, @sprites[key].y = point_to_screen(*pos) + end + end + + #----------------------------------------------------------------------------- + + def can_mark? + return Settings::ENABLE_TOWN_MAP_MARKING && @mode == :normal && !can_zoom? + end + + # Returns the [x, y, [markings]] for the given point. Creates and returns an + # empty set of markings for that point if there isn't an existing one. + def markings_of_point(this_x, this_y) + ret = [@cursor_pos[:x], @cursor_pos[:y], [0] * MARKINGS_COUNT] + $PokemonGlobal.townMapMarkings[@region].each do |marking| + next if !marking || marking[0] != @cursor_pos[:x] || marking[1] != @cursor_pos[:y] + ret = marking + end + return ret + end + + def apply_new_markings(new_markings) + if new_markings[2].all?(0) + $PokemonGlobal.townMapMarkings[@region].delete_if { |marking| marking[0] == new_markings[0] && marking[1] == new_markings[1] } + former_key = "mark_#{new_markings[0]}_#{new_markings[1]}".to_sym + @sprites.each_pair { |key, sprite| sprite.dispose if key == former_key } + @sprites.delete(former_key) + @pins_pos.delete(former_key) + else + found_existing = false + $PokemonGlobal.townMapMarkings[@region].each_with_index do |marking, i| + next if marking[0] != new_markings[0] || marking[1] != new_markings[1] + found_existing = true + $PokemonGlobal.townMapMarkings[@region][i] = new_markings + break + end + if !found_existing + $PokemonGlobal.townMapMarkings[@region].push(new_markings) + key = "mark_#{new_markings[0]}_#{new_markings[1]}".to_sym + create_pin(key, new_markings[0], new_markings[1], graphics_folder + themed_filename("icon_marking"), 50) + end + end + end + + #----------------------------------------------------------------------------- + + def has_fly_points? + ret = false + each_fly_point do |point| + if !$DEBUG || !Input.press?(Input::CTRL) + next if point[:switch] && (point[:switch] <= 0 || !$game_switches[point[:switch]]) + next if !$PokemonGlobal.visitedMaps[point[:fly_spot][0]] + end + ret = true + break + end + return ret + end + + def start_fly_mode + return if @mode == :fly || @sub_mode == :fly + @sub_mode = :fly + generate_fly_icons + refresh_input_helpers + end + + def end_fly_mode + @sub_mode = :none + clear_fly_icons + refresh_input_helpers + end + + def generate_fly_icons + @fly_coords = [] + counter = 0 + each_fly_point do |point| + if !$DEBUG || !Input.press?(Input::CTRL) + next if point[:switch] && (point[:switch] <= 0 || !$game_switches[point[:switch]]) + next if !$PokemonGlobal.visitedMaps[point[:fly_spot][0]] + end + @fly_coords.push(point[:position]) + next if point[:hide_fly_icon] + counter += 1 + sprite_key = "fly_icon_#{counter}".to_sym + @sprites[sprite_key] = AnimatedSprite.create( + graphics_folder + "icon_fly", 2, 10, @map_viewport # 2 frames, 10/20 seconds per frame + ) + @sprites[sprite_key].x, @sprites[sprite_key].y = point_to_screen(*point[:position]) + if point[:fly_icon_offset] + @sprites[sprite_key].x += point[:fly_icon_offset][0] + @sprites[sprite_key].y += point[:fly_icon_offset][1] + end + @sprites[sprite_key].z = 900 + @sprites[sprite_key].ox = @sprites[sprite_key].bitmap.height / 2 + @sprites[sprite_key].oy = @sprites[sprite_key].bitmap.height / 2 + @sprites[sprite_key].play + end + end + + def clear_fly_icons + @sprites.each_pair do |key, sprite| + next if !key.to_s.include?("fly_icon_") + sprite.dispose + @sprites[key] = nil + end + end + + #----------------------------------------------------------------------------- + + def refresh + super + refresh_map_overlay + refresh_on_cursor_move + end + + def refresh_overlay + super + refresh_input_helpers + refresh_map_name + end + + def refresh_map_overlay + @sprites[:map_overlay].bitmap.clear + Settings::REGION_MAP_EXTRAS.each do |graphic| + next if graphic[0] != @region + next if !graphic[5] && @mode == :wall_map + return if graphic[1] <= 0 || !$game_switches[graphic[1]] + draw_image(graphics_folder + graphic[4], + graphic[2] * @map_data.point_size[0], graphic[3] * @map_data.point_size[1], overlay: :map_overlay) + end + end + + def refresh_on_cursor_move + refresh_map_name + refresh_details_panel + end + + def refresh_map_name + @sprites[:map_name_overlay].bitmap.clear + point_data = get_point_data + if point_data && point_data[:real_name] + name = pbGetMessageFromHash(MessageTypes::REGION_LOCATION_NAMES, point_data[:real_name]) + name = name.gsub(/\\PN/, $player.name) + name = name.gsub(/\\v\[(\d+)\]/) { |num| $game_variables[$~[1].to_i].to_s } + theme = (@mode == :wall_map) ? :black : :default + draw_text(name, @sprites[:map_name_overlay].width / 2, 6, align: :center, theme: theme, overlay: :map_name_overlay) + end + end + + def refresh_input_helpers + @sprites[:input_helpers_overlay].bitmap.clear + input_spacing = 24 + icon_text_spacing = 6 + input_x = 4 + action_icon_y = 4 + action_text_y = 12 + draw_input = lambda do |number, action_text| + draw_image(@bitmaps[:input_icons], input_x, action_icon_y, + number * @bitmaps[:input_icons].height, 0, + @bitmaps[:input_icons].height, @bitmaps[:input_icons].height, + overlay: :input_helpers_overlay) + draw_text(action_text, input_x + @bitmaps[:input_icons].height + icon_text_spacing, action_text_y, overlay: :input_helpers_overlay) + input_x += @bitmaps[:input_icons].height + icon_text_spacing + input_x += @sprites[:input_helpers_overlay].bitmap.text_size(action_text).width + input_x += input_spacing + end + if @mode == :fly || @sub_mode == :fly + draw_input.call(0, _INTL("Fly to here")) + return + end + if can_zoom? + draw_input.call(0, _INTL("Zoom")) + elsif can_mark? + draw_input.call(0, _INTL("Mark")) + end + if can_access_screen_menu? + options = screen_menu_options + if options.length == 2 && options.include?(:fly_mode) # Also contains :cancel + draw_input.call(2, _INTL("Fly")) + else + draw_input.call(2, _INTL("Menu")) + end + end + end + + def refresh_details_panel(skip_if_not_visible = true) + return if skip_if_not_visible && !@sprites[:details]&.visible + @sprites[:details_overlay].bitmap.clear + draw_markings_on_details_panel + draw_point_details_on_details_panel + end + + def draw_markings_on_details_panel + return if !Settings::ENABLE_TOWN_MAP_MARKING + MARKINGS_COUNT.times do |i| + draw_image(@bitmaps[:details_marking_bg], + 38 + (168 * (i % 2)) - (@bitmaps[:details_marking_bg].width / 2), + 44 + (52 * (i / 2)) - (@bitmaps[:details_marking_bg].height / 2), + overlay: :details_overlay) + end + $PokemonGlobal.townMapMarkings[@region].each do |marking| + next if !marking || marking[0] != @cursor_pos[:x] || marking[1] != @cursor_pos[:y] + next if !marking[2] + marking[2].each_with_index do |mark, i| + next if mark == 0 + draw_image(@bitmaps[:map_markings], + 38 + (168 * (i % 2)) - (@bitmaps[:map_markings].height / 2), + 44 + (52 * (i / 2)) - (@bitmaps[:map_markings].height / 2), + @bitmaps[:map_markings].height * (mark - 1), 0, + @bitmaps[:map_markings].height, @bitmaps[:map_markings].height, + overlay: :details_overlay) + end + break + end + end + + def draw_point_details_on_details_panel + point_data = get_point_data + return if !point_data + if point_data[:image] + draw_image(graphics_folder + point_data[:image], 74, 22, overlay: :details_overlay) + end + if point_data[:real_description] + description = pbGetMessageFromHash(MessageTypes::REGION_LOCATION_DESCRIPTIONS, point_data[:real_description]) + description = description.gsub(/\\PN/, $player.name) + description = description.gsub(/\\v\[(\d+)\]/) { |num| $game_variables[$~[1].to_i].to_s } + draw_formatted_text(description, 18, 144, 210, overlay: :details_overlay) + end + end + + def refresh_markings_panel + @sprites[:marking_overlay].bitmap.clear + draw_point_name_on_markings_panel + draw_marking_slots_on_markings_panel + draw_markings_lineup_on_markings_panel + end + + def draw_point_name_on_markings_panel + point_data = get_point_data + if point_data && point_data[:real_name] + name = pbGetMessageFromHash(MessageTypes::REGION_LOCATION_NAMES, point_data[:real_name]) + name = name.gsub(/\\PN/, $player.name) + name = name.gsub(/\\v\[(\d+)\]/) { |num| $game_variables[$~[1].to_i].to_s } + draw_text(name, @sprites[:marking_overlay].width / 2, 114, align: :center, theme: :black, overlay: :marking_overlay) + end + end + + def draw_marking_slots_on_markings_panel + # Draw current markings + middle_y = 168 + MARKINGS_COUNT.times do |i| + middle_x = (Graphics.width / 2) + ((@bitmaps[:details_marking_bg].width + MARKING_SPACING) * (i - ((MARKINGS_COUNT.to_f - 1) / 2))) + draw_image(@bitmaps[:details_marking_bg], + middle_x - (@bitmaps[:details_marking_bg].width / 2), + middle_y - (@bitmaps[:details_marking_bg].height / 2), + overlay: :marking_overlay) + if @markings[2][i] > 0 + draw_image(@bitmaps[:map_markings], + middle_x - (@bitmaps[:map_markings].height / 2), + middle_y - (@bitmaps[:map_markings].height / 2), + @bitmaps[:map_markings].height * (@markings[2][i] - 1), 0, + @bitmaps[:map_markings].height, @bitmaps[:map_markings].height, + overlay: :marking_overlay) + end + end + end + + def draw_markings_lineup_on_markings_panel + # Draw all markings to choose from + middle_y = 292 + icons_count = 1 + (@bitmaps[:map_markings].width / @bitmaps[:map_markings].height) + icons_count.times do |i| + middle_x = (Graphics.width / 2) + ((@bitmaps[:details_marking_bg].width + MARKING_SPACING) * (i - ((icons_count.to_f - 1) / 2))) + draw_image(@bitmaps[:details_marking_bg], + middle_x - (@bitmaps[:details_marking_bg].width / 2), + middle_y - (@bitmaps[:details_marking_bg].height / 2), + overlay: :marking_overlay) + next if i == 0 + draw_image(@bitmaps[:map_markings], + middle_x - (@bitmaps[:map_markings].height / 2), + middle_y - (@bitmaps[:map_markings].height / 2), + @bitmaps[:map_markings].height * (i - 1), 0, + @bitmaps[:map_markings].height, @bitmaps[:map_markings].height, + overlay: :marking_overlay) + end + end + + def refresh_markings_cursor + if @marking_new_index >= 0 + spaces_count = 1 + (@bitmaps[:map_markings].width / @bitmaps[:map_markings].height) + space_index = @marking_new_index + else + spaces_count = MARKINGS_COUNT + space_index = @marking_index + end + middle_x = (Graphics.width / 2) + ((@bitmaps[:details_marking_bg].width + MARKING_SPACING) * (space_index - ((spaces_count.to_f - 1) / 2))) + @sprites[:marking_cursor].x = middle_x + @sprites[:marking_cursor].y = (@marking_new_index >= 0) ? 292 : 168 + end + + #----------------------------------------------------------------------------- + + def update_input + return if cursor_moving? && update_move_cursor + # Check for cursor movement + update_direction_input + if cursor_moving? + @cursor_timer_start ||= System.uptime + return + end + # Check for interaction + if Input.trigger?(Input::USE) + return update_interaction(Input::USE) + elsif Input.trigger?(Input::BACK) + return update_interaction(Input::BACK) + elsif Input.trigger?(Input::ACTION) + return update_interaction(Input::ACTION) + end + return nil + end + + # Moves the cursor after a direction input has said it should move. Returns + # whether the cursor is still moving. + def update_move_cursor + now = System.uptime + if @cursor_offset[:x] && @cursor_offset[:x] != 0 + point_x = lerp(@cursor_pos[:x] - @cursor_offset[:x], @cursor_pos[:x], + CURSOR_MOVE_TIME * @cursor_offset[:x].abs * @sprites[:map].zoom_x, + @cursor_timer_start, now) + @sprites[:cursor].x = point_x_to_screen_x(point_x) + @cursor_offset[:x] = 0 if @cursor_timer_start + (CURSOR_MOVE_TIME * @cursor_offset[:x].abs * @sprites[:map].zoom_x) <= now + end + if @cursor_offset[:y] && @cursor_offset[:y] != 0 + point_y = lerp(@cursor_pos[:y] - @cursor_offset[:y], @cursor_pos[:y], + CURSOR_MOVE_TIME * @cursor_offset[:y].abs * @sprites[:map].zoom_y, + @cursor_timer_start, now) + @sprites[:cursor].y = point_y_to_screen_y(point_y) + @cursor_offset[:y] = 0 if @cursor_timer_start + (CURSOR_MOVE_TIME * @cursor_offset[:y].abs * @sprites[:map].zoom_y) <= now + end + update_map_offset + return true if cursor_moving? + @cursor_timer_start = nil + return false + end + + def update_direction_input + x_offset = 0 + y_offset = 0 + x_offset = -1 if Input.press?(Input::LEFT) + x_offset = 1 if Input.press?(Input::RIGHT) + y_offset = -1 if Input.press?(Input::UP) + y_offset = 1 if Input.press?(Input::DOWN) + return if x_offset == 0 && y_offset == 0 + x_offset = 0 if x_offset != 0 && !can_move_to_point?(@cursor_pos[:x] + x_offset, @cursor_pos[:y]) + y_offset = 0 if y_offset != 0 && !can_move_to_point?(@cursor_pos[:x], @cursor_pos[:y] + y_offset) + return if x_offset == 0 && y_offset == 0 + @cursor_offset[:x] = x_offset + @cursor_offset[:y] = y_offset + set_cursor_pos(@cursor_pos[:x] + @cursor_offset[:x], @cursor_pos[:y] + @cursor_offset[:y]) + end + + def update_interaction(input) + case input + when Input::USE + if @mode == :fly || @sub_mode == :fly + if @fly_coords.include?([@cursor_pos[:x], @cursor_pos[:y]]) + pbPlayDecisionSE + return :use_fly + end + elsif can_zoom? + pbPlayDecisionSE + zoom_in + elsif can_mark? + pbPlayDecisionSE + return :marking + end + when Input::ACTION + if can_access_screen_menu? + pbPlayDecisionSE + options = screen_menu_options + return :fly_mode if options.length == 2 && options.include?(:fly_mode) # Also contains :cancel + return :screen_menu + end + when Input::BACK + if @sub_mode == :fly + pbPlayCancelSE + end_fly_mode + elsif zoomed? + pbPlayCancelSE + zoom_out + else + pbPlayCloseMenuSE + return :quit + end + end + return nil + end + + # Ensures the cursor remains in the display area by shifting the map sprite's + # viewport's ox/oy. + def update_map_offset + changed = false + if zoomed? + if @map_viewport.ox != @sprites[:cursor].x - ZOOM_CURSOR_POSITION[0] + @map_viewport.ox = @sprites[:cursor].x - ZOOM_CURSOR_POSITION[0] + changed = true + end + if @map_viewport.oy != @sprites[:cursor].y - ZOOM_CURSOR_POSITION[1] + @map_viewport.oy = @sprites[:cursor].y - ZOOM_CURSOR_POSITION[1] + changed = true + end + else + if @sprites[:cursor].x - (@map_data.point_size[0] / 2) < @map_viewport.ox + MAP_SCROLL_PADDING[0] + @map_viewport.ox = @sprites[:cursor].x - (@map_data.point_size[0] / 2) - MAP_SCROLL_PADDING[0] + changed = true + elsif @sprites[:cursor].x + (@map_data.point_size[0] / 2) > @map_viewport.ox + MAP_SIZE[0] - MAP_SCROLL_PADDING[0] - @map_data.margins[0] + @map_viewport.ox = @sprites[:cursor].x + (@map_data.point_size[0] / 2) - MAP_SIZE[0] + MAP_SCROLL_PADDING[0] + @map_data.margins[0] + changed = true + end + if @sprites[:cursor].y - (@map_data.point_size[1] / 2) < @map_viewport.oy + MAP_SCROLL_PADDING[1] + @map_viewport.oy = @sprites[:cursor].y - (@map_data.point_size[1] / 2) - MAP_SCROLL_PADDING[1] + changed = true + elsif @sprites[:cursor].y + (@map_data.point_size[1] / 2) > @map_viewport.oy + MAP_SIZE[1] - MAP_SCROLL_PADDING[1] - @map_data.margins[1] + @map_viewport.oy = @sprites[:cursor].y + (@map_data.point_size[1] / 2) - MAP_SIZE[1] + MAP_SCROLL_PADDING[1] + @map_data.margins[1] + changed = true + end + end + clamp_map_position if changed + end + + #----------------------------------------------------------------------------- + + def update_input_marking + # Check for movement to a new marking + if Input.repeat?(Input::LEFT) + if @marking_new_index >= 0 + @marking_new_index -= 1 + icons_count = 1 + (@bitmaps[:map_markings].width / @bitmaps[:map_markings].height) + @marking_new_index += icons_count if @marking_new_index < 0 + else + @marking_index -= 1 + @marking_index += MARKINGS_COUNT if @marking_index < 0 + end + elsif Input.repeat?(Input::RIGHT) + if @marking_new_index >= 0 + @marking_new_index += 1 + icons_count = 1 + (@bitmaps[:map_markings].width / @bitmaps[:map_markings].height) + @marking_new_index -= icons_count if @marking_new_index >= icons_count + else + @marking_index += 1 + @marking_index -= MARKINGS_COUNT if @marking_index >= MARKINGS_COUNT + end + end + # Check for up/down movement between marking rows (doesn't apply changes) + if Input.trigger?(Input::UP) && @marking_new_index >= 0 + pbPlayCursorSE + @marking_new_index = -1 + refresh_markings_cursor + elsif Input.trigger?(Input::DOWN) && @marking_new_index < 0 + pbPlayCursorSE + @marking_new_index = @markings[2][@marking_index] + refresh_markings_cursor + end + # Check for interaction + if Input.trigger?(Input::USE) + pbPlayDecisionSE + if @marking_new_index >= 0 # Chosen a new marking + if @markings[2][@marking_index] != @marking_new_index + @markings[2][@marking_index] = @marking_new_index + refresh_markings_panel + end + @marking_new_index = -1 + refresh_markings_cursor + else # Start to choose a new marking + @marking_new_index = @markings[2][@marking_index] + refresh_markings_cursor + end + elsif Input.trigger?(Input::BACK) + if @marking_new_index >= 0 # Cancel choosing a new marking + pbPlayCancelSE + @marking_new_index = -1 + refresh_markings_cursor + else # Close the markings panel + pbPlayCloseMenuSE + return true + end + end + return false + end + + def navigate_markings + # Setup + @sprites[:marking_bg].visible = true + @sprites[:marking_overlay].visible = true + @sprites[:marking_cursor].visible = true + @markings = markings_of_point(@cursor_pos[:x], @cursor_pos[:y]) + @marking_index = 0 + @marking_new_index = -1 + refresh_markings_panel + refresh_markings_cursor + # Navigate loop + loop do + Graphics.update + Input.update + update_visuals + old_marking_index = @marking_index + old_marking_new_index = @marking_new_index + break if update_input_marking + if @marking_index != old_marking_index || + (@marking_new_index != old_marking_new_index && (@marking_new_index == -1) == (old_marking_new_index == -1)) + pbPlayCursorSE + refresh_markings_cursor + end + end + # Clean up + @sprites[:marking_bg].visible = false + @sprites[:marking_overlay].visible = false + @sprites[:marking_cursor].visible = false + apply_new_markings(@markings) + @marking_index = nil + @marking_new_index = nil + end +end + +#=============================================================================== +# +#=============================================================================== +class UI::TownMap < UI::BaseScreen + SCREEN_ID = :town_map_screen + + # mode is one of: + # :normal + # :wall_map - Doesn't show unlockable content, can't change region, can't fly + # :fly - Can't zoom or change region, choose a point to fly to + def initialize(this_region = -1, mode: :normal) + @region = this_region + if @region < 0 + this_map_data = $game_map.metadata&.town_map_position + @region = (this_map_data) ? this_map_data[0] : 0 + end + @mode = mode + super() + end + + def initialize_visuals + @visuals = UI::TownMapVisuals.new(@region, @mode) + end + + #----------------------------------------------------------------------------- + + def visited_regions + return @visuals.visited_regions + end + + def region + return @visuals.region + end + + def set_region(new_region) + @visuals.set_region(new_region) + end + + def has_fly_points? + return @visuals.has_fly_points? + end + + def start_fly_mode + @visuals.start_fly_mode + end + + def set_fly_destination + point_data = @visuals.get_point_data + if !point_data || !point_data[:fly_spot] + raise _INTL("No data for this point defined in town_map.txt somehow.") + end + if @mode != :fly + map_name = pbGetMapNameFromId(point_data[:fly_spot][0]) + return false if !show_confirm_message(_INTL("Would you like to use Fly to go to {1}?", map_name)) + end + @result = point_data[:fly_spot] + return true + end +end + +#=============================================================================== +# +#=============================================================================== +# Shows a choice menu using the MenuHandlers options below. +UIActionHandlers.add(UI::TownMap::SCREEN_ID, :screen_menu, { + :menu => :town_map_menu, + :menu_message => proc { |screen| _INTL("Choose an option.") } +}) + +UIActionHandlers.add(UI::TownMap::SCREEN_ID, :fly_mode, { + :effect => proc { |screen| + screen.start_fly_mode + } +}) + +UIActionHandlers.add(UI::TownMap::SCREEN_ID, :use_fly, { + :returns_value => true, + :effect => proc { |screen| + next (screen.set_fly_destination) ? :quit : :none + } +}) + +UIActionHandlers.add(UI::TownMap::SCREEN_ID, :change_region, { + :effect => proc { |screen| + commands = {} + index = 0 + screen.visited_regions.each do |region| + region_data = GameData::TownMap.get(region) + index = commands.length if region_data.id == screen.region + commands[region] = region_data.name + end + commands[:cancel] = _INTL("Cancel") + region = screen.show_choice_message(_INTL("Which region's map do you want to view?"), commands, screen.region) + screen.set_region(region) if region && region != :cancel + } +}) + +UIActionHandlers.add(UI::TownMap::SCREEN_ID, :marking, { + :effect => proc { |screen| + screen.visuals.navigate_markings + screen.refresh + } +}) + +#=============================================================================== +# Menu options for choice menus that exist in the party screen. +#=============================================================================== +MenuHandlers.add(:town_map_menu, :fly_mode, { + "name" => _INTL("Fly"), + "order" => 10, + "condition" => proc { |screen| + this_map_data = $game_map.metadata&.town_map_position + current_region = (this_map_data) ? this_map_data[0] : 0 + next Settings::CAN_FLY_FROM_TOWN_MAP && pbCanFly? && screen.mode == :normal && + current_region == screen.region && screen.has_fly_points? + } +}) + +MenuHandlers.add(:town_map_menu, :change_region, { + "name" => _INTL("Change region"), + "order" => 20, + "condition" => proc { |screen| + next screen.mode == :normal && screen.visited_regions.length >= 2 + } +}) + +MenuHandlers.add(:town_map_menu, :cancel, { + "name" => _INTL("Cancel"), + "order" => 9999 +}) + +#=============================================================================== +# +#=============================================================================== +def pbShowMap(region = -1, wall_map = true) + mode = (wall_map) ? :wall_map : :normal + pbFadeOutIn do + town_map_screen = UI::TownMap.new(region, mode: mode) + town_map_screen.main + $game_temp.fly_destination = town_map_screen.result if town_map_screen.result + end +end diff --git a/Data/Scripts/016b_UI redesign/017_UI_PokemonStorage.rb b/Data/Scripts/016b_UI redesign/017_UI_PokemonStorage.rb index 8ec53d4d6..545bd1580 100644 --- a/Data/Scripts/016b_UI redesign/017_UI_PokemonStorage.rb +++ b/Data/Scripts/016b_UI redesign/017_UI_PokemonStorage.rb @@ -1604,7 +1604,7 @@ end # #=============================================================================== class UI::PokemonStorage < UI::BaseScreen - attr_reader :storage, :mode + attr_reader :storage SCREEN_ID = :pokemon_storage_screen diff --git a/Data/Scripts/016b_UI redesign/022_UI_MoveReminder.rb b/Data/Scripts/016b_UI redesign/022_UI_MoveReminder.rb index d29ed8e73..af97b20e6 100644 --- a/Data/Scripts/016b_UI redesign/022_UI_MoveReminder.rb +++ b/Data/Scripts/016b_UI redesign/022_UI_MoveReminder.rb @@ -263,7 +263,7 @@ end # #=============================================================================== class UI::MoveReminder < UI::BaseScreen - attr_reader :pokemon, :mode + attr_reader :pokemon SCREEN_ID = :move_reminder_screen diff --git a/Data/Scripts/016_UI/009_UI_RegionMap.rb b/Data/Scripts/016c_UI_old/009_UI_old_TownMap.rb similarity index 99% rename from Data/Scripts/016_UI/009_UI_RegionMap.rb rename to Data/Scripts/016c_UI_old/009_UI_old_TownMap.rb index 45c984c15..c042aaabd 100644 --- a/Data/Scripts/016_UI/009_UI_RegionMap.rb +++ b/Data/Scripts/016c_UI_old/009_UI_old_TownMap.rb @@ -1,3 +1,4 @@ +=begin #=============================================================================== # #=============================================================================== @@ -356,3 +357,4 @@ def pbShowMap(region = -1, wallmap = true) $game_temp.fly_destination = ret if ret && !wallmap end end +=end diff --git a/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb b/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb index caa52063c..5f9a949bb 100644 --- a/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb +++ b/Data/Scripts/020_Debug/003_Debug menus/003_Debug_MenuExtraCode.rb @@ -448,10 +448,10 @@ class SpriteWindow_DebugRoamers < Window_DrawableCommand self.shadowtext(_INTL("[Clear all current roamer locations]"), rect.x, rect.y, nameWidth, rect.height) else pkmn = Settings::ROAMING_SPECIES[index] - name = GameData::Species.get(pkmn[0]).name + " (Lv. #{pkmn[1]})" + name = GameData::Species.get(pkmn[:species]).name + " (Lv. #{pkmn[:level]})" status = "" statuscolor = 0 - if pkmn[2] <= 0 || $game_switches[pkmn[2]] + if !pkmn[:game_switch] || pkmn[:game_switch] <= 0 || $game_switches[pkmn[:game_switch]] status = $PokemonGlobal.roamPokemon[index] if status == true if $PokemonGlobal.roamPokemonCaught[index] @@ -472,7 +472,7 @@ class SpriteWindow_DebugRoamers < Window_DrawableCommand statuscolor = 2 end else - status = "[NOT ROAMING][Switch #{pkmn[2]} is off]" + status = "[NOT ROAMING][Switch #{pkmn[:game_switch]} is off]" end self.shadowtext(name, rect.x, rect.y, nameWidth, rect.height) self.shadowtext(status, rect.x + nameWidth, rect.y, statusWidth, rect.height, 1, statuscolor) @@ -500,7 +500,7 @@ def pbDebugRoamers pkmn = nil end if Input.trigger?(Input::ACTION) && cmdwindow.index < cmdwindow.roamerCount && - (pkmn[2] <= 0 || $game_switches[pkmn[2]]) && + (!pkmn[:game_switch] || pkmn[:game_switch] <= 0 || $game_switches[pkmn[:game_switch]]) && $PokemonGlobal.roamPokemon[cmdwindow.index] != true # Roam selected Pokémon pbPlayDecisionSE @@ -527,9 +527,9 @@ def pbDebugRoamers if cmdwindow.index < cmdwindow.roamerCount pbPlayDecisionSE # Toggle through roaming, not roaming, defeated - if pkmn[2] > 0 && !$game_switches[pkmn[2]] + if pkmn[:game_switch] && pkmn[:game_switch] > 0 && !$game_switches[pkmn[:game_switch]] # not roaming -> roaming - $game_switches[pkmn[2]] = true + $game_switches[pkmn[:game_switch]] = true elsif $PokemonGlobal.roamPokemon[cmdwindow.index] != true # roaming -> defeated $PokemonGlobal.roamPokemon[cmdwindow.index] = true @@ -538,9 +538,9 @@ def pbDebugRoamers !$PokemonGlobal.roamPokemonCaught[cmdwindow.index] # defeated -> caught $PokemonGlobal.roamPokemonCaught[cmdwindow.index] = true - elsif pkmn[2] > 0 + elsif pkmn[:game_switch] && pkmn[:game_switch] > 0 # caught -> not roaming (or roaming if Switch ID is 0) - $game_switches[pkmn[2]] = false if pkmn[2] > 0 + $game_switches[pkmn[:game_switch]] = false if pkmn[:game_switch] && pkmn[:game_switch] > 0 $PokemonGlobal.roamPokemon[cmdwindow.index] = nil $PokemonGlobal.roamPokemonCaught[cmdwindow.index] = false end diff --git a/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb b/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb index bb4be9a90..7b4f13261 100644 --- a/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb +++ b/Data/Scripts/021_Compiler/002_Compiler_CompilePBS.rb @@ -207,9 +207,79 @@ module Compiler # Compile Town Map data. #----------------------------------------------------------------------------- def compile_town_map(*paths) - compile_PBS_file_generic(GameData::TownMap, *paths) do |final_validate, hash| - (final_validate) ? validate_all_compiled_town_maps : validate_compiled_town_map(hash) + GameData::TownMap::DATA.clear + schema = GameData::TownMap.schema + sub_schema = GameData::TownMap.sub_schema + idx = 0 + # Read from PBS file(s) + paths.each do |path| + compile_pbs_file_message_start(path) + file_suffix = File.basename(path, ".txt")[GameData::TownMap::PBS_BASE_FILENAME.length + 1, path.length] || "" + data_hash = nil + current_point = nil + section_name = nil + section_line = nil + # Read each line of town_map.txt at a time and compile it as a town map property + pbCompilerEachPreppedLine(path) do |line, line_no| + echo "." if idx % 100 == 0 + idx += 1 + Graphics.update if idx % 500 == 0 + FileLineData.setSection(section_name, nil, section_line) + if line[/^\s*\[\s*(.+)\s*\]\s*$/] + # New section [region_number] + section_name = $~[1] + section_line = line + if data_hash + validate_compiled_town_map(data_hash) + GameData::TownMap.register(data_hash) + end + FileLineData.setSection(section_name, nil, section_line) + # Construct data hash + data_hash = { + :pbs_file_suffix => file_suffix + } + data_hash[schema["SectionName"][0]] = get_csv_record(section_name.clone, schema["SectionName"]) + data_hash[schema["Point"][0]] = [] + current_point = nil + elsif line[/^\s*(\w+)\s*=\s*(.*)$/] + # XXX=YYY lines + if !data_hash + raise _INTL("Expected a section at the beginning of the file.") + "\n" + FileLineData.linereport + end + key = $~[1] + if schema[key] # Property of the town map + property_value = get_csv_record($~[2], schema[key]) + if key == "Point" + current_point = { + :position => [property_value[0], property_value[1]], + :real_name => property_value[2] + } + current_point[:real_description] = property_value[3] if property_value[3] + current_point[:fly_spot] = [property_value[4], property_value[5], property_value[6]] if property_value[4] + current_point[:switch] = property_value[7] if property_value[7] + data_hash[schema[key][0]].push(current_point) + else + data_hash[schema[key][0]] = property_value + end + elsif sub_schema[key] # Property of a point + if !current_point + raise _INTL("Property \"{1}\" is point-specific, but a point hasn't been defined yet.", key) + "\n" + FileLineData.linereport + end + current_point[sub_schema[key][0]] = get_csv_record($~[2], sub_schema[key]) + end + end + end + # Add last town map's data to records + if data_hash + FileLineData.setSection(section_name, nil, section_line) + validate_compiled_town_map(data_hash) + GameData::TownMap.register(data_hash) + end + process_pbs_file_message_end end + validate_all_compiled_town_maps + # Save all data + GameData::TownMap.save end def validate_compiled_town_map(hash) @@ -219,19 +289,19 @@ module Compiler # Get town map names and descriptions for translating region_names = [] point_names = [] - interest_names = [] + point_descriptions = [] GameData::TownMap.each do |town_map| region_names[town_map.id] = town_map.real_name - town_map.point.each do |point| - point_names.push(point[2]) - interest_names.push(point[3]) + town_map.points.each do |point| + point_names.push(point[:real_name]) + point_descriptions.push(point[:real_description]) end end point_names.uniq! - interest_names.uniq! + point_descriptions.uniq! MessageTypes.setMessagesAsHash(MessageTypes::REGION_NAMES, region_names) MessageTypes.setMessagesAsHash(MessageTypes::REGION_LOCATION_NAMES, point_names) - MessageTypes.setMessagesAsHash(MessageTypes::REGION_LOCATION_DESCRIPTIONS, interest_names) + MessageTypes.setMessagesAsHash(MessageTypes::REGION_LOCATION_DESCRIPTIONS, point_descriptions) end #----------------------------------------------------------------------------- diff --git a/Data/Scripts/021_Compiler/003_Compiler_WritePBS.rb b/Data/Scripts/021_Compiler/003_Compiler_WritePBS.rb index de4310637..cb0afb3c6 100644 --- a/Data/Scripts/021_Compiler/003_Compiler_WritePBS.rb +++ b/Data/Scripts/021_Compiler/003_Compiler_WritePBS.rb @@ -107,7 +107,57 @@ module Compiler # Save Town Map data to PBS file. #----------------------------------------------------------------------------- def write_town_map - write_PBS_file_generic(GameData::TownMap) + paths = get_all_PBS_file_paths(GameData::TownMap) + schema = GameData::TownMap.schema + sub_schema = GameData::TownMap.sub_schema + idx = 0 + paths.each do |path| + write_pbs_file_message_start(path[0]) + File.open(path[0], "wb") do |f| + add_PBS_header_to_file(f) + # Write each element in turn + GameData::TownMap.each do |element| + next if element.pbs_file_suffix != path[1] + echo "." if idx % 100 == 0 + Graphics.update if idx % 500 == 0 + idx += 1 + f.write("\#-------------------------------\r\n") + if schema["SectionName"] + f.write("[") + pbWriteCsvRecord(element.get_property_for_PBS("SectionName"), f, schema["SectionName"]) + f.write("]\r\n") + else + f.write("[#{element.id}]\r\n") + end + # Write each town map property + schema.each_key do |key| + next if ["SectionName", "Point"].include?(key) + val = element.get_property_for_PBS(key) + next if val.nil? + f.write(sprintf("%s = ", key)) + pbWriteCsvRecord(val, f, schema[key]) + f.write("\r\n") + end + # Write each point in turn + element.points.each_with_index do |point, i| + # Write position/name + val = element.get_point_property_for_PBS("Point", i) + f.write("Point = ") + pbWriteCsvRecord(val, f, schema["Point"]) + f.write("\r\n") + # Write other point properties + sub_schema.each_key do |key| + val = element.get_point_property_for_PBS(key, i) + next if val.nil? + f.write(sprintf(" %s = ", key)) + pbWriteCsvRecord(val, f, sub_schema[key]) + f.write("\r\n") + end + end + end + end + process_pbs_file_message_end + end end #----------------------------------------------------------------------------- diff --git a/PBS/town_map.txt b/PBS/town_map.txt index 349e1c279..d75bc9907 100644 --- a/PBS/town_map.txt +++ b/PBS/town_map.txt @@ -3,34 +3,77 @@ [0] Name = Essen Filename = mapRegion0.png -Point = 13,12,Lappet Town,Oak's Lab,2,8,8 +PointSize = 16,16 +Size = 30,20 +Point = 13,12,Lappet Town + Image = image_lappet_town + Description = \PN's house\nProfessor Oak's lab + FlySpot = 2,8,8 Point = 13,11,Route 1 -Point = 13,10,Cedolan City,Cedolan Dept. Store,7,47,11 -Point = 14,10,Cedolan City,,7,47,11 + Description = Kurt's house +Point = 13,10,Cedolan City + Image = image_cedolan_city + Description = Cedolan Dept. Store\nCedolan Gym\nMove Maniac House + FlySpot = 7,47,11 + FlyIconOffset = 8,0 +Point = 14,10,Cedolan City + Image = image_cedolan_city + Description = Game Corner\nPokémon Institute + FlySpot = 7,47,11 + HideFlyIcon = true Point = 14,9,Route 2 + Description = Bridges Point = 14,8,Route 2 -Point = 15,8,Lerucean Town,,23,11,15 + Description = Bridges +Point = 15,8,Lerucean Town + Image = image_lerucean_town + Description = Day Care Center\nPokémon Fan Club\nLerucean Market + FlySpot = 23,11,15 Point = 16,8,Natural Park + Description = A Bug Catching Contest is held here every Tuesday, Thursday and Saturday. Point = 15,7,Route 3 -Point = 15,6,Route 3,Ice Cave +Point = 15,6,Route 3 + Description = Ice Cave Point = 14,6,Route 3 -Point = 13,6,Ingido Plateau,,35,17,7 + Description = Examples of trainer battles. +Point = 13,6,Ingido Plateau + Image = image_ingido_plateau + Description = Pokémon League + FlySpot = 35,17,7 Point = 12,6,Route 4 Point = 11,6,Route 4 -Point = 11,7,Route 5,Cycle Road -Point = 11,8,Route 5,Cycle Road -Point = 11,9,Route 5,Cycle Road + Description = Cycle Road +Point = 11,7,Route 5 + Description = Cycle Road +Point = 11,8,Route 5 + Description = Cycle Road +Point = 11,9,Route 5 + Description = Cycle Road Point = 11,10,Route 6 + Description = Cycle Road Point = 12,10,Route 6 Point = 15,10,Route 7 -Point = 16,10,Route 7,Rock Cave -Point = 17,10,Battle Frontier,,52,17,14 +Point = 16,10,Route 7 + Description = Rock Cave +Point = 17,10,Battle Frontier + Image = image_battle_frontier + Description = Battle Palace\nBattle Tower\nBattle Arena\nBattle Factory + FlySpot = 52,17,14 Point = 12,12,Safari Zone -Point = 13,13,Route 8,Diving area -Point = 18,17,Berth Island,,,,,51 -Point = 22,16,Faraday Island,,,,,52 + Description = Challenge yourself by catching Pokémon without using your own. +Point = 13,13,Route 8 + Image = image_route_8 + Description = Harbor\nDiving area +Point = 18,17,Berth Island + Description = Four mysterious meteorites can be found here. + Switch = 51 +Point = 22,16,Faraday Island + Description = A blue Mew can be found here every so often. + Switch = 52 #------------------------------- [1] Name = Tiall Filename = mapRegion1.png +PointSize = 16,16 +Size = 30,20 Point = 13,16,Here