#encoding:utf-8
#==============================================================================
# ■ Game_Map
#------------------------------------------------------------------------------
# 管理地图的类。拥有卷动地图以及判断通行度的功能。
# 本类的实例请参考 $game_map 。
#==============================================================================
class Game_Map
#--------------------------------------------------------------------------
# ● 定义实例变量
#--------------------------------------------------------------------------
attr_reader :screen # 地图画面的状态
attr_reader :interpreter # 地图事件用事件解释器
attr_reader :events # 事件
attr_reader :display_x # 显示 X 坐标
attr_reader :display_y # 显示 Y 坐标
attr_reader :parallax_name # 远景图文件名
attr_reader :vehicles # 载具
attr_reader :battleback1_name # 战斗背景(地面)文件名
attr_reader :battleback2_name # 战斗背景(墙壁)文件名
attr_accessor :name_display # 地图名显示的标志
attr_accessor :need_refresh # 刷新要求的标志
#--------------------------------------------------------------------------
# ● 初始化对象
#--------------------------------------------------------------------------
def initialize
@screen = Game_Screen.new
@interpreter = Game_Interpreter.new
@map_id = 0
@events = {}
@display_x = 0
@display_y = 0
create_vehicles
@name_display = true
end
#--------------------------------------------------------------------------
# ● 设置
#--------------------------------------------------------------------------
def setup(map_id)
@map_id = map_id
@map = load_data(sprintf("Data/Map%03d.rvdata2", @map_id))
@tileset_id = @map.tileset_id
@display_x = 0
@display_y = 0
referesh_vehicles
setup_events
setup_scroll
setup_parallax
setup_battleback
@need_refresh = false
end
#--------------------------------------------------------------------------
# ● 生成载具
#--------------------------------------------------------------------------
def create_vehicles
@vehicles = []
@vehicles[0] = Game_Vehicle.new(:boat)
@vehicles[1] = Game_Vehicle.new(:ship)
@vehicles[2] = Game_Vehicle.new(:airship)
end
#--------------------------------------------------------------------------
# ● 更新载具
#--------------------------------------------------------------------------
def referesh_vehicles
@vehicles.each {|vehicle| vehicle.refresh }
end
#--------------------------------------------------------------------------
# ● 获取载具
#--------------------------------------------------------------------------
def vehicle(type)
return @vehicles[0] if type == :boat
return @vehicles[1] if type == :ship
return @vehicles[2] if type == :airship
return nil
end
#--------------------------------------------------------------------------
# ● 获取小舟
#--------------------------------------------------------------------------
def boat
@vehicles[0]
end
#--------------------------------------------------------------------------
# ● 获取大船
#--------------------------------------------------------------------------
def ship
@vehicles[1]
end
#--------------------------------------------------------------------------
# ● 获取飞艇
#--------------------------------------------------------------------------
def airship
@vehicles[2]
end
#--------------------------------------------------------------------------
# ● 设置事件
#--------------------------------------------------------------------------
def setup_events
@events = {}
@map.events.each do |i, event|
@events[i] = Game_Event.new(@map_id, event)
end
@common_events = parallel_common_events.collect do |common_event|
Game_CommonEvent.new(common_event.id)
end
refresh_tile_events
end
#--------------------------------------------------------------------------
# ● 获取并行处理的公共事件的数组
#--------------------------------------------------------------------------
def parallel_common_events
$data_common_events.select {|event| event && event.parallel? }
end
#--------------------------------------------------------------------------
# ● 设置卷动
#--------------------------------------------------------------------------
def setup_scroll
@scroll_direction = 2
@scroll_rest = 0
@scroll_speed = 4
end
#--------------------------------------------------------------------------
# ● 设置远景图
#--------------------------------------------------------------------------
def setup_parallax
@parallax_name = @map.parallax_name
@parallax_loop_x = @map.parallax_loop_x
@parallax_loop_y = @map.parallax_loop_y
@parallax_sx = @map.parallax_sx
@parallax_sy = @map.parallax_sy
@parallax_x = 0
@parallax_y = 0
end
#--------------------------------------------------------------------------
# ● 设置战斗背景
#--------------------------------------------------------------------------
def setup_battleback
if @map.specify_battleback
@battleback1_name = @map.battleback1_name
@battleback2_name = @map.battleback2_name
else
@battleback1_name = nil
@battleback2_name = nil
end
end
#--------------------------------------------------------------------------
# ● 设置显示位置
#--------------------------------------------------------------------------
def set_display_pos(x, y)
x = [0, [x, width - screen_tile_x].min].max unless loop_horizontal?
y = [0, [y, height - screen_tile_y].min].max unless loop_vertical?
@display_x = (x + width) % width
@display_y = (y + height) % height
@parallax_x = x
@parallax_y = y
end
#--------------------------------------------------------------------------
# ● 计算远景图显示的原点 X 坐标
#--------------------------------------------------------------------------
def parallax_ox(bitmap)
if @parallax_loop_x
@parallax_x * 16
else
w1 = [bitmap.width - Graphics.width, 0].max
w2 = [width * 32 - Graphics.width, 1].max
@parallax_x * 16 * w1 / w2
end
end
#--------------------------------------------------------------------------
# ● 计算远景图显示的原点 Y 坐标
#--------------------------------------------------------------------------
def parallax_oy(bitmap)
if @parallax_loop_y
@parallax_y * 16
else
h1 = [bitmap.height - Graphics.height, 0].max
h2 = [height * 32 - Graphics.height, 1].max
@parallax_y * 16 * h1 / h2
end
end
#--------------------------------------------------------------------------
# ● 获取地图 ID
#--------------------------------------------------------------------------
def map_id
@map_id
end
#--------------------------------------------------------------------------
# ● 获取图块组
#--------------------------------------------------------------------------
def tileset
$data_tilesets[@tileset_id]
end
#--------------------------------------------------------------------------
# ● 获取显示名称
#--------------------------------------------------------------------------
def display_name
@map.display_name
end
#--------------------------------------------------------------------------
# ● 获取宽度
#--------------------------------------------------------------------------
def width
@map.width
end
#--------------------------------------------------------------------------
# ● 获取高度
#--------------------------------------------------------------------------
def height
@map.height
end
#--------------------------------------------------------------------------
# ● 获取是否横向循环
#--------------------------------------------------------------------------
def loop_horizontal?
@map.scroll_type == 2 || @map.scroll_type == 3
end
#--------------------------------------------------------------------------
# ● 获取是否纵向循环
#--------------------------------------------------------------------------
def loop_vertical?
@map.scroll_type == 1 || @map.scroll_type == 3
end
#--------------------------------------------------------------------------
# ● 获取是否禁止跑步
#--------------------------------------------------------------------------
def disable_dash?
@map.disable_dashing
end
#--------------------------------------------------------------------------
# ● 获取遇敌列表
#--------------------------------------------------------------------------
def encounter_list
@map.encounter_list
end
#--------------------------------------------------------------------------
# ● 获取遇敌步数
#--------------------------------------------------------------------------
def encounter_step
@map.encounter_step
end
#--------------------------------------------------------------------------
# ● 获取地图数据
#--------------------------------------------------------------------------
def data
@map.data
end
#--------------------------------------------------------------------------
# ● 是否环球类型
#--------------------------------------------------------------------------
def overworld?
tileset.mode == 0
end
#--------------------------------------------------------------------------
# ● 画面的横向图块数
#--------------------------------------------------------------------------
def screen_tile_x
Graphics.width / 32
end
#--------------------------------------------------------------------------
# ● 画面的纵向图块数
#--------------------------------------------------------------------------
def screen_tile_y
Graphics.height / 32
end
#--------------------------------------------------------------------------
# ● 计算显示坐标的剩余 X 坐标
#--------------------------------------------------------------------------
def adjust_x(x)
if loop_horizontal? && x < @display_x - (width - screen_tile_x) / 2
x - @display_x + @map.width
else
x - @display_x
end
end
#--------------------------------------------------------------------------
# ● 计算显示坐标的剩余 Y 坐标
#--------------------------------------------------------------------------
def adjust_y(y)
if loop_vertical? && y < @display_y - (height - screen_tile_y) / 2
y - @display_y + @map.height
else
y - @display_y
end
end
#--------------------------------------------------------------------------
# ● 计算循环修正后的 X 坐标
#--------------------------------------------------------------------------
def round_x(x)
loop_horizontal? ? (x + width) % width : x
end
#--------------------------------------------------------------------------
# ● 计算循环修正后的 Y 坐标
#--------------------------------------------------------------------------
def round_y(y)
loop_vertical? ? (y + height) % height : y
end
#--------------------------------------------------------------------------
# ● 计算特定方向推移一个图块的 X 坐标(没有循环修正)
#--------------------------------------------------------------------------
def x_with_direction(x, d)
x + (d == 6 ? 1 : d == 4 ? -1 : 0)
end
#--------------------------------------------------------------------------
# ● 计算特定方向推移一个图块的 Y 坐标(没有循环修正)
#--------------------------------------------------------------------------
def y_with_direction(y, d)
y + (d == 2 ? 1 : d == 8 ? -1 : 0)
end
#--------------------------------------------------------------------------
# ● 计算特定方向推移一个图块的 X 坐标(没有循环修正)
#--------------------------------------------------------------------------
def round_x_with_direction(x, d)
round_x(x + (d == 6 ? 1 : d == 4 ? -1 : 0))
end
#--------------------------------------------------------------------------
# ● 计算特定方向推移一个图块的 Y 坐标(没有循环修正)
#--------------------------------------------------------------------------
def round_y_with_direction(y, d)
round_y(y + (d == 2 ? 1 : d == 8 ? -1 : 0))
end
#--------------------------------------------------------------------------
# ● 自动切换 BGM / BGS
#--------------------------------------------------------------------------
def autoplay
@map.bgm.play if @map.autoplay_bgm
@map.bgs.play if @map.autoplay_bgs
end
#--------------------------------------------------------------------------
# ● 刷新
#--------------------------------------------------------------------------
def refresh
@events.each_value {|event| event.refresh }
@common_events.each {|event| event.refresh }
refresh_tile_events
@need_refresh = false
end
#--------------------------------------------------------------------------
# ● 更新图块事件的数组
#--------------------------------------------------------------------------
def refresh_tile_events
@tile_events = @events.values.select {|event| event.tile? }
end
#--------------------------------------------------------------------------
# ● 获取指定坐标处存在的事件的数组
#--------------------------------------------------------------------------
def events_xy(x, y)
@events.values.select {|event| event.pos?(x, y) }
end
#--------------------------------------------------------------------------
# ● 获取指定坐标处存在的事件(穿透以外)的数组
#--------------------------------------------------------------------------
def events_xy_nt(x, y)
@events.values.select {|event| event.pos_nt?(x, y) }
end
#--------------------------------------------------------------------------
# ● 获取指定坐标处存在的图块事件(穿透以外)的数组
#--------------------------------------------------------------------------
def tile_events_xy(x, y)
@tile_events.select {|event| event.pos_nt?(x, y) }
end
#--------------------------------------------------------------------------
# ● 获取指定坐标处存在的事件的 ID (仅一个)
#--------------------------------------------------------------------------
def event_id_xy(x, y)
list = events_xy(x, y)
list.empty? ? 0 : list[0].id
end
#--------------------------------------------------------------------------
# ● 向下卷动
#--------------------------------------------------------------------------
def scroll_down(distance)
if loop_vertical?
@display_y += distance
@display_y %= @map.height
@parallax_y += distance if @parallax_loop_y
else
last_y = @display_y
@display_y = [@display_y + distance, height - screen_tile_y].min
@parallax_y += @display_y - last_y
end
end
#--------------------------------------------------------------------------
# ● 向左卷动
#--------------------------------------------------------------------------
def scroll_left(distance)
if loop_horizontal?
@display_x += @map.width - distance
@display_x %= @map.width
@parallax_x -= distance if @parallax_loop_x
else
last_x = @display_x
@display_x = [@display_x - distance, 0].max
@parallax_x += @display_x - last_x
end
end
#--------------------------------------------------------------------------
# ● 向右卷动
#--------------------------------------------------------------------------
def scroll_right(distance)
if loop_horizontal?
@display_x += distance
@display_x %= @map.width
@parallax_x += distance if @parallax_loop_x
else
last_x = @display_x
@display_x = [@display_x + distance, (width - screen_tile_x)].min
@parallax_x += @display_x - last_x
end
end
#--------------------------------------------------------------------------
# ● 向上卷动
#--------------------------------------------------------------------------
def scroll_up(distance)
if loop_vertical?
@display_y += @map.height - distance
@display_y %= @map.height
@parallax_y -= distance if @parallax_loop_y
else
last_y = @display_y
@display_y = [@display_y - distance, 0].max
@parallax_y += @display_y - last_y
end
end
#--------------------------------------------------------------------------
# ● 有效坐标判定
#--------------------------------------------------------------------------
def valid?(x, y)
x >= 0 && x < width && y >= 0 && y < height
end
#--------------------------------------------------------------------------
# ● 通行检查
# bit : 判断通行禁止与否的字节(请参照二进制运算)
#--------------------------------------------------------------------------
def check_passage(x, y, bit)
all_tiles(x, y).each do |tile_id|
flag = tileset.flags[tile_id]
next if flag & 0x10 != 0 # [☆] : 不影响通行
return true if flag & bit == 0 # [○] : 可以通行
return false if flag & bit == bit # [×] : 不能通行
end
return false # 不能通行
end
#--------------------------------------------------------------------------
# ● 获取指定坐标处的图块 ID
#--------------------------------------------------------------------------
def tile_id(x, y, z)
@map.data[x, y, z] || 0
end
#--------------------------------------------------------------------------
# ● (由上至下)获取指定坐标处所有层次的图块数组
#--------------------------------------------------------------------------
def layered_tiles(x, y)
[2, 1, 0].collect {|z| tile_id(x, y, z) }
end
#--------------------------------------------------------------------------
# ● 获取指定坐标处的所有图块数组(包括事件)
#--------------------------------------------------------------------------
def all_tiles(x, y)
tile_events_xy(x, y).collect {|ev| ev.tile_id } + layered_tiles(x, y)
end
#--------------------------------------------------------------------------
# ● 获取指定坐标处的自动原件种类
#--------------------------------------------------------------------------
def autotile_type(x, y, z)
tile_id(x, y, z) >= 2048 ? (tile_id(x, y, z) - 2048) / 48 : -1
end
#--------------------------------------------------------------------------
# ● 判定普通角色是否可以通行
# d : 方向(2,4,6,8)
# 判断该位置的图块指定方向的通行度。
#--------------------------------------------------------------------------
def passable?(x, y, d)
check_passage(x, y, (1 << (d / 2 - 1)) & 0x0f)
end
#--------------------------------------------------------------------------
# ● 判定小舟是否可以通行
#--------------------------------------------------------------------------
def boat_passable?(x, y)
check_passage(x, y, 0x0200)
end
#--------------------------------------------------------------------------
# ● 判定大船是否可以通行
#--------------------------------------------------------------------------
def ship_passable?(x, y)
check_passage(x, y, 0x0400)
end
#--------------------------------------------------------------------------
# ● 判定飞艇是否可以着陆
#--------------------------------------------------------------------------
def airship_land_ok?(x, y)
check_passage(x, y, 0x0800) && check_passage(x, y, 0x0f)
end
#--------------------------------------------------------------------------
# ● 判定指定坐标处所有层次的标志
#--------------------------------------------------------------------------
def layered_tiles_flag?(x, y, bit)
layered_tiles(x, y).any? {|tile_id| tileset.flags[tile_id] & bit != 0 }
end
#--------------------------------------------------------------------------
# ● 判定是否梯子
#--------------------------------------------------------------------------
def ladder?(x, y)
valid?(x, y) && layered_tiles_flag?(x, y, 0x20)
end
#--------------------------------------------------------------------------
# ● 判定是否草木茂密处
#--------------------------------------------------------------------------
def bush?(x, y)
valid?(x, y) && layered_tiles_flag?(x, y, 0x40)
end
#--------------------------------------------------------------------------
# ● 判定是否柜台属性
#--------------------------------------------------------------------------
def counter?(x, y)
valid?(x, y) && layered_tiles_flag?(x, y, 0x80)
end
#--------------------------------------------------------------------------
# ● 判定是否有害地形
#--------------------------------------------------------------------------
def damage_floor?(x, y)
valid?(x, y) && layered_tiles_flag?(x, y, 0x100)
end
#--------------------------------------------------------------------------
# ● 获取地形标志
#--------------------------------------------------------------------------
def terrain_tag(x, y)
return 0 unless valid?(x, y)
layered_tiles(x, y).each do |tile_id|
tag = tileset.flags[tile_id] >> 12
return tag if tag > 0
end
return 0
end
#--------------------------------------------------------------------------
# ● 获取区域 ID
#--------------------------------------------------------------------------
def region_id(x, y)
valid?(x, y) ? @map.data[x, y, 3] >> 8 : 0
end
#--------------------------------------------------------------------------
# ● 开始卷动
#--------------------------------------------------------------------------
def start_scroll(direction, distance, speed)
@scroll_direction = direction
@scroll_rest = distance
@scroll_speed = speed
end
#--------------------------------------------------------------------------
# ● 判定是否卷动中
#--------------------------------------------------------------------------
def scrolling?
@scroll_rest > 0
end
#--------------------------------------------------------------------------
# ● 更新画面
# main : 事件解释器更新的标志
#--------------------------------------------------------------------------
def update(main = false)
refresh if @need_refresh
update_interpreter if main
update_scroll
update_events
update_vehicles
update_parallax
@screen.update
end
#--------------------------------------------------------------------------
# ● 更新卷动
#--------------------------------------------------------------------------
def update_scroll
return unless scrolling?
last_x = @display_x
last_y = @display_y
do_scroll(@scroll_direction, scroll_distance)
if @display_x == last_x && @display_y == last_y
@scroll_rest = 0
else
@scroll_rest -= scroll_distance
end
end
#--------------------------------------------------------------------------
# ● 计算卷动的距离
#--------------------------------------------------------------------------
def scroll_distance
2 ** @scroll_speed / 256.0
end
#--------------------------------------------------------------------------
# ● 执行卷动
#--------------------------------------------------------------------------
def do_scroll(direction, distance)
case direction
when 2; scroll_down (distance)
when 4; scroll_left (distance)
when 6; scroll_right(distance)
when 8; scroll_up (distance)
end
end
#--------------------------------------------------------------------------
# ● 更新事件
#--------------------------------------------------------------------------
def update_events
@events.each_value {|event| event.update }
@common_events.each {|event| event.update }
end
#--------------------------------------------------------------------------
# ● 更新载具
#--------------------------------------------------------------------------
def update_vehicles
@vehicles.each {|vehicle| vehicle.update }
end
#--------------------------------------------------------------------------
# ● 更新远景图
#--------------------------------------------------------------------------
def update_parallax
@parallax_x += @parallax_sx / 64.0 if @parallax_loop_x
@parallax_y += @parallax_sy / 64.0 if @parallax_loop_y
end
#--------------------------------------------------------------------------
# ● 更改图块组
#--------------------------------------------------------------------------
def change_tileset(tileset_id)
@tileset_id = tileset_id
refresh
end
#--------------------------------------------------------------------------
# ● 更改战斗背景
#--------------------------------------------------------------------------
def change_battleback(battleback1_name, battleback2_name)
@battleback1_name = battleback1_name
@battleback2_name = battleback2_name
end
#--------------------------------------------------------------------------
# ● 更改远景图
#--------------------------------------------------------------------------
def change_parallax(name, loop_x, loop_y, sx, sy)
@parallax_name = name
@parallax_x = 0 if @parallax_loop_x && !loop_x
@parallax_y = 0 if @parallax_loop_y && !loop_y
@parallax_loop_x = loop_x
@parallax_loop_y = loop_y
@parallax_sx = sx
@parallax_sy = sy
end
#--------------------------------------------------------------------------
# ● 更新事件解释器
#--------------------------------------------------------------------------
def update_interpreter
loop do
@interpreter.update
return if @interpreter.running?
if @interpreter.event_id > 0
unlock_event(@interpreter.event_id)
@interpreter.clear
end
return unless setup_starting_event
end
end
#--------------------------------------------------------------------------
# ● 解锁事件
#--------------------------------------------------------------------------
def unlock_event(event_id)
@events[event_id].unlock if @events[event_id]
end
#--------------------------------------------------------------------------
# ● 设置启动中事件
#--------------------------------------------------------------------------
def setup_starting_event
refresh if @need_refresh
return true if @interpreter.setup_reserved_common_event
return true if setup_starting_map_event
return true if setup_autorun_common_event
return false
end
#--------------------------------------------------------------------------
# ● 判定是否拥有启动中地图事件
#--------------------------------------------------------------------------
def any_event_starting?
@events.values.any? {|event| event.starting }
end
#--------------------------------------------------------------------------
# ● 检测/设置启动中的地图事件
#--------------------------------------------------------------------------
def setup_starting_map_event
event = @events.values.find {|event| event.starting }
event.clear_starting_flag if event
@interpreter.setup(event.list, event.id) if event
event
end
#--------------------------------------------------------------------------
# ● 检测/设置自动执行的公共事件
#--------------------------------------------------------------------------
def setup_autorun_common_event
event = $data_common_events.find do |event|
event && event.autorun? && $game_switches[event.switch_id]
end
@interpreter.setup(event.list) if event
event
end
end