#============================================================================== # ** Game_Map #------------------------------------------------------------------------------ # This class handles the map. It includes scrolling and passable determining # functions. Refer to "$game_map" for the instance of this class. #============================================================================== class Game_Map attr_accessor :map_id attr_accessor :tileset_name # tileset file name attr_accessor :autotile_names # autotile file name attr_reader :passages # passage table attr_reader :priorities # priority table attr_reader :terrain_tags # terrain tag table attr_reader :events # events attr_accessor :panorama_name # panorama file name attr_accessor :panorama_hue # panorama hue attr_accessor :fog_name # fog file name attr_accessor :fog_hue # fog hue attr_accessor :fog_opacity # fog opacity level attr_accessor :fog_blend_type # fog blending method attr_accessor :fog_zoom # fog zoom rate attr_accessor :fog_sx # fog sx attr_accessor :fog_sy # fog sy attr_accessor :fog_ox # fog x-coordinate starting point attr_accessor :fog_oy # fog y-coordinate starting point attr_accessor :fog2_ox # fog x-coordinate starting point attr_accessor :fog2_oy # fog y-coordinate starting point attr_accessor :fog2_sx # fog sx attr_accessor :fog2_sy # fog sy attr_accessor :fog2_opacity # fog sy attr_reader :fog_tone # fog color tone attr_accessor :battleback_name # battleback file name attr_reader :display_x # display x-coordinate * 128 attr_reader :display_y # display y-coordinate * 128 attr_accessor :need_refresh # refresh request flag attr_accessor :scroll_direction TILE_WIDTH = 32 TILE_HEIGHT = 32 X_SUBPIXELS = 4 Y_SUBPIXELS = 4 REAL_RES_X = TILE_WIDTH * X_SUBPIXELS REAL_RES_Y = TILE_HEIGHT * Y_SUBPIXELS def initialize @map_id = 0 @display_x = 0 @display_y = 0 end def setup(map_id) @map_id = map_id @map = load_data(sprintf("Data/Map%03d.rxdata", map_id)) tileset = $data_tilesets[@map.tileset_id] updateTileset @fog_ox = 0 @fog_oy = 0 @fog2_ox = 0 @fog2_oy = 0 @fog2_sx = 0 @fog2_sy = 0 @fog2_opacity = 0 @fog_tone = Tone.new(0, 0, 0, 0) @fog_tone_target = Tone.new(0, 0, 0, 0) @fog_tone_duration = 0 @fog_opacity_duration = 0 @fog_opacity_target = 0 self.display_x = 0 self.display_y = 0 @need_refresh = false Events.onMapCreate.trigger(self, map_id, @map, tileset) @events = {} for i in @map.events.keys @events[i] = Game_Event.new(@map_id, @map.events[i], self) end @common_events = {} for i in 1...$data_common_events.size @common_events[i] = Game_CommonEvent.new(i) end @scroll_direction = 2 @scroll_rest = 0 @scroll_speed = 4 end def updateTileset tileset = $data_tilesets[@map.tileset_id] @tileset_name = tileset.tileset_name @autotile_names = tileset.autotile_names @panorama_name = tileset.panorama_name @panorama_hue = tileset.panorama_hue @fog_name = tileset.fog_name @fog_hue = tileset.fog_hue @fog_opacity = tileset.fog_opacity @fog_blend_type = tileset.fog_blend_type @fog_zoom = tileset.fog_zoom @fog_sx = tileset.fog_sx @fog_sy = tileset.fog_sy @battleback_name = tileset.battleback_name @passages = tileset.passages @priorities = tileset.priorities @terrain_tags = tileset.terrain_tags end def width return @map.width; end def height return @map.height; end def encounter_list return @map.encounter_list; end def encounter_step return @map.encounter_step; end def data return @map.data; end def tileset_id; return @map.tileset_id; end def name ret = pbGetMessage(MessageTypes::MapNames, @map_id) ret.gsub!(/\\PN/, $Trainer.name) if $Trainer return ret end #----------------------------------------------------------------------------- # * Autoplays background music # Plays music called "[normal BGM]_n" if it's night time and it exists #----------------------------------------------------------------------------- def autoplayAsCue if @map.autoplay_bgm if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/" + @map.bgm.name + "_n") pbCueBGM(@map.bgm.name + "_n", 1.0, @map.bgm.volume, @map.bgm.pitch) else pbCueBGM(@map.bgm, 1.0) end end if @map.autoplay_bgs pbBGSPlay(@map.bgs) end end def setFog2(filename,sx=0,sy=0,opacity=32) @fog2_sx=sx @fog2_sy=-sy @fog2_opacity = opacity $scene.spriteset.setFog2(filename) end def eraseFog2() @fog2_sx=0 @fog2_sy=-0 @fog2_opacity = 0 $scene.spriteset.disposeFog2() end #----------------------------------------------------------------------------- # * Plays background music # Plays music called "[normal BGM]_n" if it's night time and it exists #----------------------------------------------------------------------------- def autoplay if @map.autoplay_bgm if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/" + @map.bgm.name + "_n") pbBGMPlay(@map.bgm.name + "_n", @map.bgm.volume, @map.bgm.pitch) else pbBGMPlay(@map.bgm) end end if @map.autoplay_bgs pbBGSPlay(@map.bgs) end end def valid?(x, y) return x >= 0 && x < width && y >= 0 && y < height end def validLax?(x, y) return x >= -10 && x <= width + 10 && y >= -10 && y <= height + 10 end def passable?(x, y, d, self_event = nil) return false if !valid?(x, y) bit = (1 << (d / 2 - 1)) & 0x0f for event in events.values next if event.tile_id <= 0 next if event == self_event next if !event.at_coordinate?(x, y) next if event.through next if GameData::TerrainTag.try_get(@terrain_tags[event.tile_id]).ignore_passability passage = @passages[event.tile_id] return false if passage & bit != 0 return false if passage & 0x0f == 0x0f return true if @priorities[event.tile_id] == 0 end return playerPassable?(x, y, d, self_event) if self_event == $game_player # All other events newx = x newy = y case d when 1 newx -= 1 newy += 1 when 2 newy += 1 when 3 newx += 1 newy += 1 when 4 newx -= 1 when 6 newx += 1 when 7 newx -= 1 newy -= 1 when 8 newy -= 1 when 9 newx += 1 newy -= 1 end return false if !valid?(newx, newy) for i in [2, 1, 0] tile_id = data[x, y, i] terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) # If already on water, only allow movement to another water tile if self_event != nil && terrain.can_surf_freely for j in [2, 1, 0] facing_tile_id = data[newx, newy, j] return false if facing_tile_id == nil facing_terrain = GameData::TerrainTag.try_get(@terrain_tags[facing_tile_id]) if facing_terrain.id != :None && !facing_terrain.ignore_passability return facing_terrain.can_surf_freely end end return false # Can't walk onto ice # removed for mahogany gym. idk if this will cause problems, hopefully not # elsif terrain.ice # return false elsif self_event != nil && self_event.x == x && self_event.y == y # Can't walk onto ledges for j in [2, 1, 0] facing_tile_id = data[newx, newy, j] return false if facing_tile_id == nil facing_terrain = GameData::TerrainTag.try_get(@terrain_tags[facing_tile_id]) return false if facing_terrain.ledge break if facing_terrain.id != :None && !facing_terrain.ignore_passability end end # Regular passability checks if !terrain || !terrain.ignore_passability passage = @passages[tile_id] return false if passage & bit != 0 || passage & 0x0f == 0x0f return true if @priorities[tile_id] == 0 end end return true end def playerPassable?(x, y, d, self_event = nil) bit = (1 << (d / 2 - 1)) & 0x0f for i in [2, 1, 0] tile_id = data[x, y, i] terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) passage = @passages[tile_id] if terrain # Ignore bridge tiles if not on a bridge next if terrain.bridge && $PokemonGlobal.bridge == 0 # Make water tiles passable if player is surfing return true if $PokemonGlobal.surfing && terrain.can_surf && !terrain.waterfall # Prevent cycling in really tall grass/on ice return false if $PokemonGlobal.bicycle && terrain.must_walk # Depend on passability of bridge tile if on bridge if terrain.bridge && $PokemonGlobal.bridge > 0 return (passage & bit == 0 && passage & 0x0f != 0x0f) end end # Regular passability checks if !terrain || !terrain.ignore_passability return false if passage & bit != 0 || passage & 0x0f == 0x0f return true if @priorities[tile_id] == 0 end end return true end # Returns whether the position x,y is fully passable (there is no blocking # event there, and the tile is fully passable in all directions) def passableStrict?(x, y, d, self_event = nil) return false if !valid?(x, y) for event in events.values next if event == self_event || event.tile_id < 0 || event.through next if !event.at_coordinate?(x, y) next if GameData::TerrainTag.try_get(@terrain_tags[event.tile_id]).ignore_passability return false if @passages[event.tile_id] & 0x0f != 0 return true if @priorities[event.tile_id] == 0 end for i in [2, 1, 0] tile_id = data[x, y, i] next if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).ignore_passability return false if @passages[tile_id] & 0x0f != 0 return true if @priorities[tile_id] == 0 end return true end def bush?(x, y) for i in [2, 1, 0] tile_id = data[x, y, i] return false if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).bridge && $PokemonGlobal.bridge > 0 return true if @passages[tile_id] & 0x40 == 0x40 end return false end def deepBush?(x, y) for i in [2, 1, 0] tile_id = data[x, y, i] terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) return false if terrain.bridge && $PokemonGlobal.bridge > 0 return true if terrain.deep_bush && @passages[tile_id] & 0x40 == 0x40 end return false end def counter?(x, y) for i in [2, 1, 0] tile_id = data[x, y, i] passage = @passages[tile_id] return true if passage & 0x80 == 0x80 end return false end def terrain_tag(x, y, countBridge = false) if valid?(x, y) for i in [2, 1, 0] tile_id = data[x, y, i] terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id]) next if terrain.id == :None || terrain.ignore_passability next if !countBridge && terrain.bridge && $PokemonGlobal.bridge == 0 return terrain end end return GameData::TerrainTag.get(:None) end # Unused. def check_event(x, y) for event in self.events.values return event.id if event.at_coordinate?(x, y) end end def event_at_position(x, y) for event in self.events.values return true if event.at_coordinate?(x, y) end return false end def get_event_at_position(x, y, excluding_IDs = []) for event in self.events.values next if excluding_IDs.include?(event.id) return event if event.at_coordinate?(x, y) end return nil end def display_x=(value) return if @display_x == value @display_x = value if GameData::MapMetadata.exists?(self.map_id) && GameData::MapMetadata.get(self.map_id).snap_edges max_x = (self.width - Graphics.width * 1.0 / TILE_WIDTH) * REAL_RES_X @display_x = [0, [@display_x, max_x].min].max end $MapFactory.setMapsInRange if $MapFactory end def display_y=(value) return if @display_y == value @display_y = value if GameData::MapMetadata.exists?(self.map_id) && GameData::MapMetadata.get(self.map_id).snap_edges max_y = (self.height - Graphics.height * 1.0 / TILE_HEIGHT) * REAL_RES_Y @display_y = [0, [@display_y, max_y].min].max end $MapFactory.setMapsInRange if $MapFactory end def scroll_up(distance) self.display_y -= distance end def scroll_down(distance) self.display_y += distance end def scroll_left(distance) self.display_x -= distance end def scroll_right(distance) self.display_x += distance end def start_scroll(direction, distance, speed) @scroll_direction = direction if direction == 2 || direction == 8 # down or up @scroll_rest = distance * REAL_RES_Y else @scroll_rest = distance * REAL_RES_X end @scroll_speed = speed end def scrolling? return @scroll_rest > 0 end def start_fog_tone_change(tone, duration) @fog_tone_target = tone.clone @fog_tone_duration = duration if @fog_tone_duration == 0 @fog_tone = @fog_tone_target.clone end end def start_fog_opacity_change(opacity, duration) @fog_opacity_target = opacity * 1.0 @fog_opacity_duration = duration if @fog_opacity_duration == 0 @fog_opacity = @fog_opacity_target end end def refresh for event in @events.values event.refresh end for common_event in @common_events.values common_event.refresh end @need_refresh = false end def update # refresh maps if necessary if $MapFactory for i in $MapFactory.maps i.refresh if i.need_refresh end $MapFactory.setCurrentMap end # If scrolling if @scroll_rest > 0 distance = (1 << @scroll_speed) * 40.0 / Graphics.frame_rate distance = @scroll_rest if distance > @scroll_rest case @scroll_direction when 2 then scroll_down(distance) when 4 then scroll_left(distance) when 6 then scroll_right(distance) when 8 then scroll_up(distance) end @scroll_rest -= distance end # Only update events that are on-screen for event in @events.values event.update end # Update common events for common_event in @common_events.values common_event.update end # Update fog @fog_ox -= @fog_sx / 8.0 @fog_oy -= @fog_sy / 8.0 @fog2_ox -= @fog2_sx / 8.0 if @fog2_ox @fog2_oy -= @fog2_sy / 8.0 if @fog2_oy if @fog_tone_duration >= 1 d = @fog_tone_duration target = @fog_tone_target @fog_tone.red = (@fog_tone.red * (d - 1) + target.red) / d @fog_tone.green = (@fog_tone.green * (d - 1) + target.green) / d @fog_tone.blue = (@fog_tone.blue * (d - 1) + target.blue) / d @fog_tone.gray = (@fog_tone.gray * (d - 1) + target.gray) / d @fog_tone_duration -= 1 end if @fog_opacity_duration >= 1 d = @fog_opacity_duration @fog_opacity = (@fog_opacity * (d - 1) + @fog_opacity_target) / d @fog_opacity_duration -= 1 end end end #=============================================================================== # #=============================================================================== def pbScrollMap(direction, distance, speed) if speed == 0 case direction when 2 then $game_map.scroll_down(distance * Game_Map::REAL_RES_Y) when 4 then $game_map.scroll_left(distance * Game_Map::REAL_RES_X) when 6 then $game_map.scroll_right(distance * Game_Map::REAL_RES_X) when 8 then $game_map.scroll_up(distance * Game_Map::REAL_RES_Y) end else $game_map.start_scroll(direction, distance, speed) oldx = $game_map.display_x oldy = $game_map.display_y loop do Graphics.update Input.update break if !$game_map.scrolling? pbUpdateSceneMap break if $game_map.display_x == oldx && $game_map.display_y == oldy oldx = $game_map.display_x oldy = $game_map.display_y end end end