Merge branch 'dev' into ai

This commit is contained in:
Maruno17
2022-10-04 22:56:27 +01:00
56 changed files with 2341 additions and 912 deletions

View File

@@ -359,7 +359,7 @@ class CallbackWrapper
def execute(given_block = nil, *args)
execute_block = given_block || @code_block
@params.each do |key, value|
args.instance_variable_set("@#{key.to_s}", value)
args.instance_variable_set("@#{key}", value)
end
args.instance_eval(&execute_block)
end

View File

@@ -627,7 +627,11 @@ module PluginManager
# get the order of plugins to interpret
order, plugins = self.getPluginOrder
# compile if necessary
self.compilePlugins(order, plugins) if self.needCompiling?(order, plugins)
if self.needCompiling?(order, plugins)
self.compilePlugins(order, plugins)
else
Console.echoln_li "Plugins were not compiled."
end
# load plugins
scripts = load_data("Data/PluginScripts.rxdata")
echoed_plugins = []

View File

@@ -79,19 +79,4 @@ module SaveData
end
return hash
end
# Moves a save file from the old Saved Games folder to the new
# location specified by {FILE_PATH}. Does nothing if a save file
# already exists in {FILE_PATH}.
def self.move_old_windows_save
return if File.file?(FILE_PATH)
game_title = System.game_title.gsub(/[^\w ]/, "_")
home = ENV["HOME"] || ENV["HOMEPATH"]
return if home.nil?
old_location = File.join(home, "Saved Games", game_title)
return unless File.directory?(old_location)
old_file = File.join(old_location, "Game.rxdata")
return unless File.file?(old_file)
File.move(old_file, FILE_PATH)
end
end

View File

@@ -202,7 +202,7 @@ module SaveData
validate id => Symbol
@values.delete_if { |value| value.id == id }
end
# @param save_data [Hash] save data to validate
# @return [Boolean] whether the given save data is valid
def self.valid?(save_data)

View File

@@ -19,7 +19,6 @@ module Game
# Loads bootup data from save file (if it exists) or creates bootup data (if
# it doesn't).
def self.set_up_system
SaveData.move_old_windows_save if System.platform[/Windows/]
save_data = (SaveData.exists?) ? SaveData.read_from_file(SaveData::FILE_PATH) : {}
if save_data.empty?
SaveData.initialize_bootup_values

View File

@@ -474,7 +474,7 @@ class Game_Player < Game_Character
if !@moved_last_frame || @stopped_last_frame # Started a new step
if pbTerrainTag.ice
set_movement_type(:ice_sliding)
else#if !@move_route_forcing
else
faster = can_run?
if $PokemonGlobal&.diving
set_movement_type((faster) ? :diving_fast : :diving)

View File

@@ -278,14 +278,8 @@ class Game_FollowerFactory
facing_tile = $map_factory.getFacingTile
# Assumes player is 1x1 tile in size
each_follower do |event, follower|
if event.at_coordinate?($game_player.x, $game_player.y) # Underneath player
next if !event.over_trigger?
elsif facing_tile && event.map.map_id == facing_tile[0] &&
event.at_coordinate?(facing_tile[1], facing_tile[2]) # On facing tile
next if event.over_trigger?
else # Somewhere else
next
end
next if !facing_tile || event.map.map_id != facing_tile[0] ||
!event.at_coordinate?(facing_tile[1], facing_tile[2]) # Not on facing tile
next if event.jumping?
follower.interact(event)
end

View File

@@ -37,23 +37,38 @@ class TileDrawingHelper
36, 24, 36, 24, 21, 6, 21, 4, 36, 24, 36, 24, 20, 2, 20, 0
]
def self.tableNeighbors(data, x, y)
def self.tableNeighbors(data, x, y, layer = nil)
return 0 if x < 0 || x >= data.xsize
return 0 if y < 0 || y >= data.ysize
t = data[x, y]
if layer.nil?
t = data[x, y]
else
t = data[x, y, layer]
end
xp1 = [x + 1, data.xsize - 1].min
yp1 = [y + 1, data.ysize - 1].min
xm1 = [x - 1, 0].max
ym1 = [y - 1, 0].max
i = 0
i |= 0x01 if data[x, ym1] == t # N
i |= 0x02 if data[xp1, ym1] == t # NE
i |= 0x04 if data[xp1, y] == t # E
i |= 0x08 if data[xp1, yp1] == t # SE
i |= 0x10 if data[x, yp1] == t # S
i |= 0x20 if data[xm1, yp1] == t # SW
i |= 0x40 if data[xm1, y] == t # W
i |= 0x80 if data[xm1, ym1] == t # NW
if layer.nil?
i |= 0x01 if data[ x, ym1] == t # N
i |= 0x02 if data[xp1, ym1] == t # NE
i |= 0x04 if data[xp1, y] == t # E
i |= 0x08 if data[xp1, yp1] == t # SE
i |= 0x10 if data[ x, yp1] == t # S
i |= 0x20 if data[xm1, yp1] == t # SW
i |= 0x40 if data[xm1, y] == t # W
i |= 0x80 if data[xm1, ym1] == t # NW
else
i |= 0x01 if data[ x, ym1, layer] == t # N
i |= 0x02 if data[xp1, ym1, layer] == t # NE
i |= 0x04 if data[xp1, y, layer] == t # E
i |= 0x08 if data[xp1, yp1, layer] == t # SE
i |= 0x10 if data[ x, yp1, layer] == t # S
i |= 0x20 if data[xm1, yp1, layer] == t # SW
i |= 0x40 if data[xm1, y, layer] == t # W
i |= 0x80 if data[xm1, ym1, layer] == t # NW
end
return i
end

View File

@@ -935,58 +935,67 @@ def drawBitmapBuffer(chars)
end
def drawSingleFormattedChar(bitmap, ch)
if ch[5] # If a graphic
if ch[5] # If a graphic
graphic = Bitmap.new(ch[0])
graphicRect = ch[15]
bitmap.blt(ch[1], ch[2], graphic, graphicRect, ch[8].alpha)
graphic.dispose
return
end
bitmap.font.bold = ch[6] if bitmap.font.bold != ch[6]
bitmap.font.italic = ch[7] if bitmap.font.italic != ch[7]
bitmap.font.name = ch[12] if bitmap.font.name != ch[12]
bitmap.font.size = ch[13] if bitmap.font.size != ch[13]
if ch[9] # shadow
if ch[10] # underline
bitmap.fill_rect(ch[1], ch[2] + ch[4] - [(ch[4] - bitmap.font.size) / 2, 0].max - 2,
ch[3], 4, ch[9])
end
if ch[11] # strikeout
bitmap.fill_rect(ch[1], ch[2] + 2 + (ch[4] / 2), ch[3], 4, ch[9])
end
end
if ch[0] == "\n" || ch[0] == "\r" || ch[0] == " " || isWaitChar(ch[0])
bitmap.font.color = ch[8] if bitmap.font.color != ch[8]
else
bitmap.font.size = ch[13] if bitmap.font.size != ch[13]
if ch[0] != "\n" && ch[0] != "\r" && ch[0] != " " && !isWaitChar(ch[0])
bitmap.font.bold = ch[6] if bitmap.font.bold != ch[6]
bitmap.font.italic = ch[7] if bitmap.font.italic != ch[7]
bitmap.font.name = ch[12] if bitmap.font.name != ch[12]
offset = 0
if ch[9] # shadow
bitmap.font.color = ch[9]
if (ch[16] & 1) != 0 # outline
offset = 1
bitmap.draw_text(ch[1], ch[2], ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 1, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 2, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 1, ch[2], ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 1, ch[2] + 2, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2], ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2] + 1, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2] + 2, ch[3] + 2, ch[4], ch[0])
elsif (ch[16] & 2) != 0 # outline 2
offset = 2
bitmap.draw_text(ch[1], ch[2], ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 2, ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 4, ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2], ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2] + 4, ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 4, ch[2], ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 4, ch[2] + 2, ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 4, ch[2] + 4, ch[3] + 4, ch[4], ch[0])
else
bitmap.draw_text(ch[1] + 2, ch[2], ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 2, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2] + 2, ch[3] + 2, ch[4], ch[0])
end
offset = 0
if ch[9] # shadow
bitmap.font.color = ch[9]
if (ch[16] & 1) != 0 # outline
offset = 1
bitmap.draw_text(ch[1], ch[2], ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 1, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 2, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 1, ch[2], ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 1, ch[2] + 2, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2], ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2] + 1, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2] + 2, ch[3] + 2, ch[4], ch[0])
elsif (ch[16] & 2) != 0 # outline 2
offset = 2
bitmap.draw_text(ch[1], ch[2], ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 2, ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 4, ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2], ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2] + 4, ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 4, ch[2], ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 4, ch[2] + 2, ch[3] + 4, ch[4], ch[0])
bitmap.draw_text(ch[1] + 4, ch[2] + 4, ch[3] + 4, ch[4], ch[0])
else
bitmap.draw_text(ch[1] + 2, ch[2], ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1], ch[2] + 2, ch[3] + 2, ch[4], ch[0])
bitmap.draw_text(ch[1] + 2, ch[2] + 2, ch[3] + 2, ch[4], ch[0])
end
bitmap.font.color = ch[8] if bitmap.font.color != ch[8]
bitmap.draw_text(ch[1] + offset, ch[2] + offset, ch[3], ch[4], ch[0])
elsif bitmap.font.color != ch[8]
bitmap.font.color = ch[8]
end
if ch[10] # underline
bitmap.fill_rect(ch[1], ch[2] + ch[4] - 4 - [(ch[4] - bitmap.font.size) / 2, 0].max - 2,
ch[3] - 2, 2, ch[8])
end
if ch[11] # strikeout
bitmap.fill_rect(ch[1], ch[2] + (ch[4] / 2) - 4, ch[3] - 2, 2, ch[8])
end
bitmap.font.color = ch[8] if bitmap.font.color != ch[8]
bitmap.draw_text(ch[1] + offset, ch[2] + offset, ch[3], ch[4], ch[0])
end
if ch[10] # underline
bitmap.fill_rect(ch[1], ch[2] + ch[4] - [(ch[4] - bitmap.font.size) / 2, 0].max - 2,
ch[3] - 2, 2, ch[8])
end
if ch[11] # strikeout
bitmap.fill_rect(ch[1], ch[2] + 2 + (ch[4] / 2), ch[3] - 2, 2, ch[8])
end
end

View File

@@ -819,7 +819,7 @@ def pbShowCommandsWithHelp(msgwindow, commands, help, cmdIfCancel = 0, defaultCm
Input.update
end
msgwin.letterbyletter = oldlbl
msgwin.dispose if !msgwindow
pbDisposeMessageWindow(msgwin) if !msgwindow
return ret
end

View File

@@ -240,5 +240,8 @@ module GameData
Metadata.load
PlayerMetadata.load
MapMetadata.load
DungeonTileset.load
DungeonParameters.load
PhoneMessage.load
end
end

View File

@@ -3,7 +3,6 @@
#===============================================================================
class Game_Temp
attr_accessor :town_map_data
attr_accessor :phone_messages_data
attr_accessor :regional_dexes_data
attr_accessor :battle_animations_data
attr_accessor :move_to_battle_animation_data
@@ -13,7 +12,6 @@ end
def pbClearData
if $game_temp
$game_temp.town_map_data = nil
$game_temp.phone_messages_data = nil
$game_temp.regional_dexes_data = nil
$game_temp.battle_animations_data = nil
$game_temp.move_to_battle_animation_data = nil
@@ -37,17 +35,6 @@ def pbLoadTownMapData
return $game_temp.town_map_data
end
#===============================================================================
# Method to get phone call data.
#===============================================================================
def pbLoadPhoneData
$game_temp = Game_Temp.new if !$game_temp
if !$game_temp.phone_messages_data && pbRgssExists?("Data/phone.dat")
$game_temp.phone_messages_data = load_data("Data/phone.dat")
end
return $game_temp.phone_messages_data
end
#===============================================================================
# Method to get Regional Dexes data.
#===============================================================================

View File

@@ -1,24 +0,0 @@
#===============================================================================
# Phone data
#===============================================================================
class PhoneDatabase
attr_accessor :generics
attr_accessor :greetings
attr_accessor :greetingsMorning
attr_accessor :greetingsEvening
attr_accessor :bodies1
attr_accessor :bodies2
attr_accessor :battleRequests
attr_accessor :trainers
def initialize
@generics = []
@greetings = []
@greetingsMorning = []
@greetingsEvening = []
@bodies1 = []
@bodies2 = []
@battleRequests = []
@trainers = []
end
end

View File

@@ -57,6 +57,11 @@ module GameData
return (ret) ? ret : pbResolveBitmap("Graphics/Pokemon/Eggs/000")
end
def self.egg_cracks_sprite_filename(species, form)
ret = self.check_egg_graphic_file("Graphics/Pokemon/Eggs/", species, form, "_cracks")
return (ret) ? ret : pbResolveBitmap("Graphics/Pokemon/Eggs/000_cracks")
end
def self.sprite_filename(species, form = 0, gender = 0, shiny = false, shadow = false, back = false, egg = false)
return self.egg_sprite_filename(species, form) if egg
return self.back_sprite_filename(species, form, gender, shiny, shadow) if back

View File

@@ -0,0 +1,209 @@
module GameData
class DungeonTileset
attr_reader :id
attr_reader :tile_type_ids
attr_reader :snap_to_large_grid # "large" means 2x2 tiles
attr_reader :large_void_tiles # "large" means 2x2 tiles
attr_reader :large_wall_tiles # "large" means 1x2 or 2x1 tiles depending on side
attr_reader :large_floor_tiles # "large" means 2x2 tiles
attr_reader :double_walls
attr_reader :floor_patch_under_walls
attr_reader :thin_north_wall_offset
attr_reader :flags
DATA = {}
DATA_FILENAME = "dungeon_tilesets.dat"
SCHEMA = {
"Autotile" => [:autotile, "us"],
"Tile" => [:tile, "us"],
"SnapToLargeGrid" => [:snap_to_large_grid, "b"],
"LargeVoidTiles" => [:large_void_tiles, "b"],
"LargeWallTiles" => [:large_wall_tiles, "b"],
"LargeFloorTiles" => [:large_floor_tiles, "b"],
"DoubleWalls" => [:double_walls, "b"],
"FloorPatchUnderWalls" => [:floor_patch_under_walls, "b"],
"ThinNorthWallOffset" => [:thin_north_wall_offset, "i"],
"Flags" => [:flags, "*s"]
}
extend ClassMethodsIDNumbers
include InstanceMethods
# @param other [self, Integer]
# @return [self]
def self.try_get(other)
validate other => [Integer, self]
return other if other.is_a?(self)
return (self::DATA.has_key?(other)) ? self::DATA[other] : self.get(self::DATA.keys.first)
end
def initialize(hash)
@id = hash[:id]
@snap_to_large_grid = hash[:snap_to_large_grid] || false
@large_void_tiles = hash[:large_void_tiles] || false
@large_wall_tiles = hash[:large_wall_tiles] || false
@large_floor_tiles = hash[:large_floor_tiles] || false
@double_walls = hash[:double_walls] || false
@floor_patch_under_walls = hash[:floor_patch_under_walls] || false
@thin_north_wall_offset = hash[:thin_north_wall_offset] || 0
@flags = hash[:flags] || []
@tile_type_ids = {}
set_tile_type_ids(hash)
end
def set_tile_type_ids(hash)
[hash[:autotile], hash[:tile]].each_with_index do |array, i|
array.each do |tile_info|
next if !tile_info
tile_type = tile_info[1].downcase.to_sym
if tile_type == :walls
if @double_walls
if @large_wall_tiles
push_tile(:wall_1, 384 + tile_info[0] + 33)
push_tile(:wall_2, 384 + tile_info[0] + 34)
push_tile(:wall_3, 384 + tile_info[0] + 36)
push_tile(:wall_4, 384 + tile_info[0] + 17)
push_tile(:wall_6, 384 + tile_info[0] + 20)
push_tile(:wall_7, 384 + tile_info[0] + 9)
push_tile(:wall_8, 384 + tile_info[0] + 10)
push_tile(:wall_9, 384 + tile_info[0] + 12)
push_tile(:wall_in_1, 384 + tile_info[0] + 23)
push_tile(:wall_in_3, 384 + tile_info[0] + 22)
push_tile(:wall_in_7, 384 + tile_info[0] + 31)
push_tile(:wall_in_9, 384 + tile_info[0] + 30)
push_tile(:upper_wall_1, 384 + tile_info[0] + 40)
push_tile(:upper_wall_2, 384 + tile_info[0] + 42)
push_tile(:upper_wall_3, 384 + tile_info[0] + 45)
push_tile(:upper_wall_4, 384 + tile_info[0] + 16)
push_tile(:upper_wall_6, 384 + tile_info[0] + 21)
push_tile(:upper_wall_7, 384 + tile_info[0] + 0)
push_tile(:upper_wall_8, 384 + tile_info[0] + 2)
push_tile(:upper_wall_9, 384 + tile_info[0] + 5)
push_tile(:upper_wall_in_1, 384 + tile_info[0] + 7)
push_tile(:upper_wall_in_3, 384 + tile_info[0] + 6)
push_tile(:upper_wall_in_7, 384 + tile_info[0] + 15)
push_tile(:upper_wall_in_9, 384 + tile_info[0] + 14)
else
push_tile(:wall_1, 384 + tile_info[0] + 25)
push_tile(:wall_2, 384 + tile_info[0] + 26)
push_tile(:wall_3, 384 + tile_info[0] + 27)
push_tile(:wall_4, 384 + tile_info[0] + 17)
push_tile(:wall_6, 384 + tile_info[0] + 19)
push_tile(:wall_7, 384 + tile_info[0] + 9)
push_tile(:wall_8, 384 + tile_info[0] + 10)
push_tile(:wall_9, 384 + tile_info[0] + 11)
push_tile(:wall_in_1, 384 + tile_info[0] + 22)
push_tile(:wall_in_3, 384 + tile_info[0] + 21)
push_tile(:wall_in_7, 384 + tile_info[0] + 30)
push_tile(:wall_in_9, 384 + tile_info[0] + 29)
push_tile(:upper_wall_1, 384 + tile_info[0] + 32)
push_tile(:upper_wall_2, 384 + tile_info[0] + 34)
push_tile(:upper_wall_3, 384 + tile_info[0] + 36)
push_tile(:upper_wall_4, 384 + tile_info[0] + 16)
push_tile(:upper_wall_6, 384 + tile_info[0] + 20)
push_tile(:upper_wall_7, 384 + tile_info[0] + 0)
push_tile(:upper_wall_8, 384 + tile_info[0] + 2)
push_tile(:upper_wall_9, 384 + tile_info[0] + 4)
push_tile(:upper_wall_in_1, 384 + tile_info[0] + 6)
push_tile(:upper_wall_in_3, 384 + tile_info[0] + 5)
push_tile(:upper_wall_in_7, 384 + tile_info[0] + 14)
push_tile(:upper_wall_in_9, 384 + tile_info[0] + 13)
end
elsif @large_wall_tiles
push_tile(:wall_1, 384 + tile_info[0] + 24)
push_tile(:wall_2, 384 + tile_info[0] + 25)
push_tile(:wall_3, 384 + tile_info[0] + 27)
push_tile(:wall_4, 384 + tile_info[0] + 8)
push_tile(:wall_6, 384 + tile_info[0] + 11)
push_tile(:wall_7, 384 + tile_info[0] + 0)
push_tile(:wall_8, 384 + tile_info[0] + 1)
push_tile(:wall_9, 384 + tile_info[0] + 3)
push_tile(:wall_in_1, 384 + tile_info[0] + 5)
push_tile(:wall_in_3, 384 + tile_info[0] + 4)
push_tile(:wall_in_7, 384 + tile_info[0] + 13)
push_tile(:wall_in_9, 384 + tile_info[0] + 12)
else
push_tile(:wall_1, 384 + tile_info[0] + 16)
push_tile(:wall_2, 384 + tile_info[0] + 17)
push_tile(:wall_3, 384 + tile_info[0] + 18)
push_tile(:wall_4, 384 + tile_info[0] + 8)
push_tile(:wall_6, 384 + tile_info[0] + 10)
push_tile(:wall_7, 384 + tile_info[0] + 0)
push_tile(:wall_8, 384 + tile_info[0] + 1)
push_tile(:wall_9, 384 + tile_info[0] + 2)
push_tile(:wall_in_1, 384 + tile_info[0] + 4)
push_tile(:wall_in_3, 384 + tile_info[0] + 3)
push_tile(:wall_in_7, 384 + tile_info[0] + 12)
push_tile(:wall_in_9, 384 + tile_info[0] + 11)
end
end
id = (i == 0) ? tile_info[0] * 48 : 384 + tile_info[0]
push_tile(tile_type, id, false)
end
end
end
def push_tile(tile_type, id, auto = true)
@tile_type_ids[tile_type] ||= []
@tile_type_ids[tile_type].push([id, auto])
end
def has_flag?(flag)
return @flags.any? { |f| f.downcase == flag.downcase }
end
def has_decoration?(deco)
return @tile_type_ids.include?(deco) && @tile_type_ids[deco].length > 0
end
def get_random_tile_of_type(tile_type, dungeon, x, y, layer)
tiles = @tile_type_ids[tile_type]
return 0 if !tiles || tiles.empty?
ret = tiles.sample[0]
if ret < 384 # Autotile
nb = TileDrawingHelper.tableNeighbors(dungeon, x, y, layer)
variant = TileDrawingHelper::NEIGHBORS_TO_AUTOTILE_INDEX[nb]
ret += variant
else
case tile_type
when :void
if @large_void_tiles
ret += 1 if x.odd?
ret += 8 if y.odd?
end
when :floor
if large_floor_tiles
ret += 1 if x.odd?
ret += 8 if y.odd?
end
when :wall_2, :wall_8, :wall_top
ret += 1 if @large_wall_tiles && x.odd?
when :wall_4, :wall_6
ret += 8 if @large_wall_tiles && y.odd?
end
# Different wall tiles for northern walls if there's another wall directly
# north of them (i.e. tree tiles that shouldn't have shaded grass because
# there isn't a tree-enclosed area there)
if @thin_north_wall_offset != 0 && [:wall_7, :wall_8, :wall_9].include?(tile_type)
ret += @thin_north_wall_offset if dungeon.tile_is_wall?(dungeon[x, y - 1, 1])
end
end
return ret
end
def property_from_string(str)
case str
when "SnapToLargeGrid" then return @snap_to_large_grid
when "LargeVoidTiles" then return @large_void_tiles
when "LargeWallTiles" then return @large_wall_tiles
when "LargeFloorTiles" then return @large_floor_tiles
when "DoubleWalls" then return @double_walls
when "FloorPatchUnderWalls" then return @floor_patch_under_walls
when "ThinNorthWallOffset" then return @thin_north_wall_offset
when "Flags" then return @flags
end
return nil
end
end
end

View File

@@ -0,0 +1,138 @@
# TODO: Add tileset number in here?
module GameData
class DungeonParameters
attr_reader :id, :area, :version
attr_reader :cell_count_x, :cell_count_y
attr_reader :cell_width, :cell_height
attr_reader :room_min_width, :room_min_height
attr_reader :room_max_width, :room_max_height
attr_reader :corridor_width, :random_corridor_shift
# Layouts:
# :full - every node in the map
# :no_corners - every node except for one in each corner
# :ring - every node around the edge of the map
# :antiring - every node except one that touches an edge of the map
# :plus - every node in a plus (+) shape
# :diagonal_up - every node in a line from bottom left to top right (/)
# :diagonal_down - every node in a line from top left to bottom right (\)
# :cross - every node in a cross (x) shape
# :quadrants - every node except the middles of each edge (i.e. each corner bulges out)
attr_reader :node_layout, :room_layout
attr_reader :room_chance # Percentage of active roomable nodes that will become rooms
attr_reader :extra_connections_count
attr_reader :floor_patch_radius, :floor_patch_chance, :floor_patch_smooth_rate
attr_reader :floor_decoration_density, :floor_decoration_large_density
attr_reader :void_decoration_density, :void_decoration_large_density
attr_reader :rng_seed
attr_reader :flags
DATA = {}
DATA_FILENAME = "dungeon_parameters.dat"
SCHEMA = {
"DungeonSize" => [:dungeon_size, "vv"],
"CellSize" => [:cell_size, "vv"],
"MinRoomSize" => [:min_room_size, "vv"],
"MaxRoomSize" => [:max_room_size, "vv"],
"CorridorWidth" => [:corridor_width, "v"],
"ShiftCorridors" => [:shift_corridors, "b"],
"NodeLayout" => [:node_layout, "s"],
"RoomLayout" => [:room_layout, "s"],
"RoomChance" => [:room_chance, "v"],
"ExtraConnections" => [:extra_connections_count, "u"],
"FloorPatches" => [:floor_patches, "vvu"],
"FloorDecorations" => [:floor_decorations, "uu"],
"VoidDecorations" => [:void_decorations, "uu"],
"RNGSeed" => [:rng_seed, "u"],
"Flags" => [:flags, "*s"]
}
extend ClassMethodsSymbols
include InstanceMethods
# @param other [Symbol, String, self]
# @param version [Integer]
# @return [self]
def self.try_get(area, version = 0)
validate area => [Symbol, self, String]
validate version => Integer
area = area.id if area.is_a?(self)
area = area.to_sym if area.is_a?(String)
trial = sprintf("%s_%d", area, version).to_sym
area_version = (DATA[trial].nil?) ? area : trial
return (DATA.has_key?(area_version)) ? DATA[area_version] : self.new({})
end
def initialize(hash)
@id = hash[:id]
@area = hash[:area]
@version = hash[:version] || 0
@cell_count_x = (hash[:dungeon_size]) ? hash[:dungeon_size][0] : 5
@cell_count_y = (hash[:dungeon_size]) ? hash[:dungeon_size][1] : 5
@cell_width = (hash[:cell_size]) ? hash[:cell_size][0] : 10
@cell_height = (hash[:cell_size]) ? hash[:cell_size][1] : 10
@room_min_width = (hash[:min_room_size]) ? hash[:min_room_size][0] : 5
@room_min_height = (hash[:min_room_size]) ? hash[:min_room_size][1] : 5
@room_max_width = (hash[:max_room_size]) ? hash[:max_room_size][0] : @cell_width - 1
@room_max_height = (hash[:max_room_size]) ? hash[:max_room_size][1] : @cell_height - 1
@corridor_width = hash[:corridor_width] || 2
@random_corridor_shift = hash[:shift_corridors]
@node_layout = hash[:node_layout]&.downcase&.to_sym || :full
@room_layout = hash[:room_layout]&.downcase&.to_sym || :full
@room_chance = hash[:room_chance] || 70
@extra_connections_count = hash[:extra_connections_count] || 2
@floor_patch_radius = (hash[:floor_patches]) ? hash[:floor_patches][0] : 3
@floor_patch_chance = (hash[:floor_patches]) ? hash[:floor_patches][1] : 75
@floor_patch_smooth_rate = (hash[:floor_patches]) ? hash[:floor_patches][2] : 25
@floor_decoration_density = (hash[:floor_decorations]) ? hash[:floor_decorations][0] : 50
@floor_decoration_large_density = (hash[:floor_decorations]) ? hash[:floor_decorations][1] : 200
@void_decoration_density = (hash[:void_decorations]) ? hash[:void_decorations][0] : 50
@void_decoration_large_density = (hash[:void_decorations]) ? hash[:void_decorations][1] : 200
@rng_seed = hash[:rng_seed]
@flags = hash[:flags] || []
end
def has_flag?(flag)
return @flags.any? { |f| f.downcase == flag.downcase }
end
def rand_cell_center
x = (@cell_width / 2) + rand(-2..2)
y = (@cell_height / 2) + rand(-2..2)
return x, y
end
def rand_room_size
width = @room_min_width
if @room_max_width > @room_min_width
width = rand(@room_min_width..@room_max_width)
end
height = @room_min_height
if @room_max_height > @room_min_height
height = rand(@room_min_height..@room_max_height)
end
return width, height
end
def property_from_string(str)
case str
when "DungeonSize" then return [@cell_count_x, @cell_count_y]
when "CellSize" then return [@cell_width, @cell_height]
when "MinRoomSize" then return [@room_min_width, @room_min_height]
when "MaxRoomSize" then return [@room_max_width, @room_max_height]
when "CorridorWidth" then return @corridor_width
when "ShiftCorridors" then return @random_corridor_shift
when "NodeLayout" then return @node_layout
when "RoomLayout" then return @room_layout
when "RoomChance" then return @room_chance
when "ExtraConnections" then return @extra_connections_count
when "FloorPatches" then return [@floor_patch_radius, @floor_patch_chance, @floor_patch_smooth_rate]
when "FloorDecorations" then return [@floor_decoration_density, @floor_decoration_large_density]
when "VoidDecorations" then return [@void_decoration_density, @void_decoration_large_density]
when "RNGSeed" then return @rng_seed
when "Flags" then return @flags
end
return nil
end
end
end

View File

@@ -0,0 +1,96 @@
module GameData
class PhoneMessage
attr_reader :id
attr_reader :trainer_type, :real_name, :version
attr_reader :intro, :intro_morning, :intro_afternoon, :intro_evening
attr_reader :body, :body1, :body2
attr_reader :battle_request, :battle_remind
attr_reader :end
DATA = {}
DATA_FILENAME = "phone.dat"
SCHEMA = {
"Intro" => [:intro, "q"],
"IntroMorning" => [:intro_morning, "q"],
"IntroAfternoon" => [:intro_afternoon, "q"],
"IntroEvening" => [:intro_evening, "q"],
"Body" => [:body, "q"],
"Body1" => [:body1, "q"],
"Body2" => [:body2, "q"],
"BattleRequest" => [:battle_request, "q"],
"BattleRemind" => [:battle_remind, "q"],
"End" => [:end, "q"]
}
extend ClassMethodsSymbols
include InstanceMethods
# @param tr_type [Symbol, String]
# @param tr_name [String]
# @param tr_version [Integer, nil]
# @return [Boolean] whether the given other is defined as a self
def self.exists?(tr_type, tr_name, tr_version = 0)
validate tr_type => [Symbol, String]
validate tr_name => [String]
key = [tr_type.to_sym, tr_name, tr_version]
return !self::DATA[key].nil?
end
# @param tr_type [Symbol, String]
# @param tr_name [String]
# @param tr_version [Integer, nil]
# @return [self]
def self.get(tr_type, tr_name, tr_version = 0)
validate tr_type => [Symbol, String]
validate tr_name => [String]
key = [tr_type.to_sym, tr_name, tr_version]
raise "Phone messages not found for #{tr_type} #{tr_name} #{tr_version}." unless self::DATA.has_key?(key)
return self::DATA[key]
end
# @param tr_type [Symbol, String]
# @param tr_name [String]
# @param tr_version [Integer, nil]
# @return [self, nil]
def self.try_get(tr_type, tr_name, tr_version = 0)
validate tr_type => [Symbol, String]
validate tr_name => [String]
key = [tr_type.to_sym, tr_name, tr_version]
return (self::DATA.has_key?(key)) ? self::DATA[key] : nil
end
def initialize(hash)
@id = hash[:id]
@trainer_type = hash[:trainer_type]
@real_name = hash[:name]
@version = hash[:version] || 0
@intro = hash[:intro]
@intro_morning = hash[:intro_morning]
@intro_afternoon = hash[:intro_afternoon]
@intro_evening = hash[:intro_evening]
@body = hash[:body]
@body1 = hash[:body1]
@body2 = hash[:body2]
@battle_request = hash[:battle_request]
@battle_remind = hash[:battle_remind]
@end = hash[:end]
end
def property_from_string(str)
case str
when "Intro" then return @intro
when "IntroMorning" then return @intro_morning
when "IntroAfternoon" then return @intro_afternoon
when "IntroEvening" then return @intro_evening
when "Body" then return @body
when "Body1" then return @body1
when "Body2" then return @body2
when "BattleRequest" then return @battle_request
when "BattleRemind" then return @battle_remind
when "End" then return @end
end
return nil
end
end
end

View File

@@ -21,6 +21,36 @@ class Battle
return true
end
# Return values:
# -1: Chose not to end the battle via Debug means
# 0: Couldn't end the battle via Debug means; carry on trying to run
# 1: Ended the battle via Debug means
def pbDebugRun
return 0 if !$DEBUG || !Input.press?(Input::CTRL)
commands = [_INTL("Treat as a win"), _INTL("Treat as a loss"),
_INTL("Treat as a draw"), _INTL("Treat as running away/forfeit")]
commands.push(_INTL("Treat as a capture")) if wildBattle?
commands.push(_INTL("Cancel"))
case pbShowCommands(_INTL("Choose the outcome of this battle."), commands)
when 0 # Win
@decision = 1
when 1 # Loss
@decision = 2
when 2 # Draw
@decision = 5
when 3 # Run away/forfeit
pbSEPlay("Battle flee")
pbDisplayPaused(_INTL("You got away safely!"))
@decision = 3
when 4 # Capture
return -1 if trainerBattle?
@decision = 4
else
return -1
end
return 1
end
# Return values:
# -1: Failed fleeing
# 0: Wasn't possible to attempt fleeing, continue choosing action for the round
@@ -36,17 +66,12 @@ class Battle
@choices[idxBattler][2] = nil
return -1
end
# Fleeing from trainer battles
# Debug ending the battle
debug_ret = pbDebugRun
return debug_ret if debug_ret != 0
# Running from trainer battles
if trainerBattle?
if $DEBUG && Input.press?(Input::CTRL)
if pbDisplayConfirm(_INTL("Treat this battle as a win?"))
@decision = 1
return 1
elsif pbDisplayConfirm(_INTL("Treat this battle as a loss?"))
@decision = 2
return 1
end
elsif @internalBattle
if @internalBattle
pbDisplayPaused(_INTL("No! There's no running from a Trainer battle!"))
elsif pbDisplayConfirm(_INTL("Would you like to forfeit the match and quit now?"))
pbSEPlay("Battle flee")
@@ -56,13 +81,6 @@ class Battle
end
return 0
end
# Fleeing from wild battles
if $DEBUG && Input.press?(Input::CTRL)
pbSEPlay("Battle flee")
pbDisplayPaused(_INTL("You got away safely!"))
@decision = 3
return 1
end
if !@canRun
pbDisplayPaused(_INTL("You can't escape!"))
return 0

View File

@@ -36,6 +36,9 @@ class Battle
end
def pbCall(idxBattler)
# Debug ending the battle
return if pbDebugRun != 0
# Call the battler
battler = @battlers[idxBattler]
trainerName = pbGetOwnerName(idxBattler)
pbDisplay(_INTL("{1} called {2}!", trainerName, battler.pbThis(true)))

View File

@@ -24,7 +24,7 @@ class Battle::Battler
def pbCanInflictStatus?(newStatus, user, showMessages, move = nil, ignoreStatus = false)
return false if fainted?
selfInflicted = (user && user.index == @index)
self_inflicted = (user && user.index == @index) # Rest and Flame Orb/Toxic Orb only
# Already have that status problem
if self.status == newStatus && !ignoreStatus
if showMessages
@@ -41,13 +41,13 @@ class Battle::Battler
return false
end
# Trying to replace a status problem with another one
if self.status != :NONE && !ignoreStatus && !selfInflicted
if self.status != :NONE && !ignoreStatus && !(self_inflicted && move) # Rest can replace a status problem
@battle.pbDisplay(_INTL("It doesn't affect {1}...", pbThis(true))) if showMessages
return false
end
# Trying to inflict a status problem on a Pokémon behind a substitute
if @effects[PBEffects::Substitute] > 0 && !(move && move.ignoresSubstitute?(user)) &&
!selfInflicted
!self_inflicted
@battle.pbDisplay(_INTL("It doesn't affect {1}...", pbThis(true))) if showMessages
return false
end
@@ -105,7 +105,7 @@ class Battle::Battler
immAlly = nil
if Battle::AbilityEffects.triggerStatusImmunityNonIgnorable(self.ability, self, newStatus)
immuneByAbility = true
elsif selfInflicted || !@battle.moldBreaker
elsif self_inflicted || !@battle.moldBreaker
if abilityActive? && Battle::AbilityEffects.triggerStatusImmunity(self.ability, self, newStatus)
immuneByAbility = true
else
@@ -163,7 +163,7 @@ class Battle::Battler
return false
end
# Safeguard immunity
if pbOwnSide.effects[PBEffects::Safeguard] > 0 && !selfInflicted && move &&
if pbOwnSide.effects[PBEffects::Safeguard] > 0 && !self_inflicted && move &&
!(user && user.hasActiveAbility?(:INFILTRATOR))
@battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!", pbThis)) if showMessages
return false

View File

@@ -496,6 +496,12 @@ Battle::AbilityEffects::StatusImmunityFromAlly.add(:FLOWERVEIL,
}
)
Battle::AbilityEffects::StatusImmunityFromAlly.add(:PASTELVEIL,
proc { |ability, battler, status|
next true if status == :POISON
}
)
Battle::AbilityEffects::StatusImmunityFromAlly.add(:SWEETVEIL,
proc { |ability, battler, status|
next true if status == :SLEEP
@@ -561,6 +567,8 @@ Battle::AbilityEffects::StatusCure.add(:IMMUNITY,
}
)
Battle::AbilityEffects::StatusCure.copy(:IMMUNITY, :PASTELVEIL)
Battle::AbilityEffects::StatusCure.add(:INSOMNIA,
proc { |ability, battler|
next if battler.status != :SLEEP

View File

@@ -32,10 +32,10 @@ EventHandlers.add(:on_wild_pokemon_created, :level_depends_on_party,
# Note that you can only modify a partner trainer's Pokémon, and not the trainer
# themselves nor their items this way, as those are generated from scratch
# before each battle.
#EventHandlers.add(:on_trainer_load, :put_a_name_here,
# proc { |trainer|
# if trainer # An NPCTrainer object containing party/items/lose text, etc.
# YOUR CODE HERE
# end
# }
#)
# EventHandlers.add(:on_trainer_load, :put_a_name_here,
# proc { |trainer|
# if trainer # An NPCTrainer object containing party/items/lose text, etc.
# YOUR CODE HERE
# end
# }
# )

View File

@@ -259,7 +259,8 @@ class BerryPlantSprite
when 2 then @event.turn_down # X sprouted
when 3 then @event.turn_left # X taller
when 4 then @event.turn_right # X flowering
when 5 then @event.turn_up # X berries
else
@event.turn_up if berry_plant.growth_stage >= 5 # X berries
end
else
@event.character_name = "Object ball"

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,25 @@
# TODO: Add an information window with details of the person in a phone call.
# Make this work with common event calls (create and dispose the info
# window in start_message and end_message).
# TODO: Rewrite the Phone UI. Have more than one method. Choosable icons/marks
# for each contact? Show an icon representing phone signal.
# TODO: Look at the "ready to rematch" timers to see if they can be improved?
# Should they be limited to one trainer becoming ready every ~5 minutes?
# Should a rematch-ready contact become unready again after some time if
# they haven't told the player they're ready?
# TODO: See if incoming phone calls can be made optional somehow. Maybe just
# interrupt as normal with the start of the call and ask if the player
# wants to answer? Wait for a couple of seconds before asking to make sure
# the player doesn't accidentally skip/answer a call they didn't want to.
# TODO: Add a Debug way of upgrading old phone script calls to new ones, or at
# least to find events using old phone scripts for the dev to update.
# TODO: More Debug control over contacts (changing their "time to rebattle",
# unhiding hidden contacts, etc.) and the phone (time until next call).
# TODO: Add a trainer comment for giving a trainer a common event ID.
# TODO: Add calling a contact at a particular time forcing rematch readiness.
# Add trainer comments for this.
# TODO: Allow individual trainers to never arrange a rematch by themself, thus
# requiring the player to call them at their particular time of day/week.
# TODO: Be able to put the Phone on silent mode (prevent all phone calls from
# trainers, but allow scripted calls as normal).
# TODO: Better messages, more customisation of messages.
# TODO: Add a Debug way of upgrading old phone script calls to new ones, or at
# least to find events using old phone scripts for the dev to update.
#===============================================================================
#
#===============================================================================
@@ -70,8 +75,8 @@ class Phone
return true
end
# Event, trainer type, name, versions_count = 1, start_version = 0
# Map ID, event ID, trainer type, name, versions_count = 1, start_version = 0
# Event, trainer type, name, versions_count = 1, start_version = 0, common event ID = 0
# Map ID, event ID, trainer type, name, versions_count = 1, start_version = 0, common event ID = 0
# Map ID, name, common event ID
def add(*args)
if args[0].is_a?(Game_Event)
@@ -82,9 +87,11 @@ class Phone
contact = get(true, trainer_type, name, args[3] || 0)
if contact
contact.visible = true
@contacts.delete(contact)
@contacts.push(contact)
else
contact = Contact.new(true, args[0].map_id, args[0].id,
trainer_type, name, args[3] || 1, args[4] || 0)
trainer_type, name, args[3], args[4], args[5])
contact.increment_version
@contacts.push(contact)
end
@@ -96,9 +103,11 @@ class Phone
contact = get(true, trainer_type, name, args[4] || 0)
if contact
contact.visible = true
@contacts.delete(contact)
@contacts.push(contact)
else
contact = Contact.new(true, args[0], args[1],
trainer_type, name, args[4] || 1, args[5] || 0)
trainer_type, name, args[4], args[5], args[6])
contact.increment_version
@contacts.push(contact)
end
@@ -108,14 +117,30 @@ class Phone
contact = get(false, name)
if contact
contact.visible = true
@contacts.delete(contact)
@contacts.push(contact)
else
contact = Contact.new(false, *args)
@contacts.push(contact)
end
end
sort_contacts
return true
end
# Rearranges the list of phone contacts to put all visible contacts first,
# followed by all invisible contacts.
def sort_contacts
new_contacts = []
2.times do |i|
@contacts.each do |con|
next if (i == 0 && !con.visible?) || (i == 1 && con.visible?)
new_contacts.push(con)
end
end
@contacts = new_contacts
end
#=============================================================================
# Checks once every second.
@@ -233,7 +258,7 @@ class Phone
attr_accessor :trainer_type, :start_version, :versions_count, :version
attr_accessor :time_to_ready, :rematch_flag, :variant_beaten
attr_accessor :common_event_id
attr_accessor :visible
attr_reader :visible
# Map ID, event ID, trainer type, name, versions count = 1, start version = 0
# Map ID, name, common event ID
@@ -251,7 +276,7 @@ class Phone
@variant_beaten = 0
@time_to_ready = 0
@rematch_flag = 0 # 0=counting down, 1=ready for rematch, 2=ready and told player
@common_event_id = 0
@common_event_id = args[6] || 0
else
# Non-trainer
@map_id = args[0]
@@ -280,6 +305,10 @@ class Phone
end
end
def can_hide?
return trainer?
end
def common_event_call?
return @common_event_id > 0
end
@@ -373,7 +402,13 @@ class Phone
def make_incoming
return if !can_make?
contact = get_random_trainer_for_incoming_call
if contact
return if !contact
if contact.common_event_call?
if !pbCommonEvent(contact.common_event_id)
pbMessage(_INTL("{1}'s messages not defined.\nCouldn't call common event {2}.",
contact.display_name, contact.common_event_id))
end
else
call = generate_trainer_dialogue(contact)
play(call, contact)
end
@@ -403,7 +438,7 @@ class Phone
end
end
def start_message(contact)
def start_message(contact = nil)
pbMessage(_INTL("......\\wt[5] ......\\1"))
end
@@ -434,7 +469,7 @@ class Phone
end_message(contact)
end
def end_message(contact)
def end_message(contact = nil)
pbMessage(_INTL("Click!\\wt[10]\n......\\wt[5] ......\\1"))
end
@@ -442,36 +477,58 @@ class Phone
def generate_trainer_dialogue(contact)
validate contact => Phone::Contact
# Get the set of messages to be used by the contact
messages = GameData::PhoneMessage.try_get(contact.trainer_type, contact.name, contact.version)
messages = GameData::PhoneMessage.try_get(contact.trainer_type, contact.name, contact.start_version) if !messages
messages = GameData::PhoneMessage::DATA["default"] if !messages
# Create lambda for choosing a random message and translating it
get_random_message = lambda do |messages|
return "" if !messages
msg = messages.sample
return "" if !msg
return pbGetMessageFromHash(MessageTypes::PhoneMessages, msg)
end
phone_data = pbLoadPhoneData
# Choose random greeting depending on time of day
ret = get_random_message.call(phone_data.greetings)
ret = get_random_message.call(messages.intro)
time = pbGetTimeNow
if PBDayNight.isMorning?(time)
modcall = get_random_message.call(phone_data.greetingsMorning)
ret = modcall if !nil_or_empty?(modcall)
mod_call = get_random_message.call(messages.intro_morning)
ret = mod_call if !nil_or_empty?(mod_call)
elsif PBDayNight.isAfternoon?(time)
mod_call = get_random_message.call(messages.intro_afternoon)
ret = mod_call if !nil_or_empty?(mod_call)
elsif PBDayNight.isEvening?(time)
modcall = get_random_message.call(phone_data.greetingsEvening)
ret = modcall if !nil_or_empty?(modcall)
mod_call = get_random_message.call(messages.intro_evening)
ret = mod_call if !nil_or_empty?(mod_call)
end
ret += "\\m"
if Phone.rematches_enabled && (contact.rematch_flag == 1 ||
(contact.rematch_flag == 2 && rand(100) < 50))
# If ready for rematch, tell the player (50% chance to remind the player)
ret += get_random_message.call(phone_data.battleRequests)
contact.rematch_flag = 2 # Ready for rematch and told player
elsif rand(100) < 75
# Choose random body
ret += get_random_message.call(phone_data.bodies1)
ret += "\\m"
ret += get_random_message.call(phone_data.bodies2)
# Choose main message set
if Phone.rematches_enabled && contact.rematch_flag > 0
# Trainer is ready for a rematch, so tell/remind the player
if contact.rematch_flag == 1 # Tell the player
ret += get_random_message.call(messages.battle_request)
contact.rematch_flag = 2 # Ready for rematch and told player
elsif contact.rematch_flag == 2 # Remind the player
if messages.battle_remind
ret += get_random_message.call(messages.battle_remind)
else
ret += get_random_message.call(messages.battle_request)
end
end
else
# Choose random generic
ret += get_random_message.call(phone_data.generics)
# Standard messages
if messages.body1 && messages.body2 && (!messages.body || rand(100) < 75)
# Choose random pair of body messages
ret += get_random_message.call(messages.body1)
ret += "\\m"
ret += get_random_message.call(messages.body2)
else
# Choose random full body message
ret += get_random_message.call(messages.body)
end
# Choose end message
mod_call = get_random_message.call(messages.end)
ret += "\\m" + mod_call if !nil_or_empty?(mod_call)
end
return ret
end

View File

@@ -28,9 +28,7 @@ class PokemonEggHatch_Scene
@pokemon.form, @pokemon.shiny?,
false, false, true) # Egg sprite
# Load egg cracks bitmap
crackfilename = sprintf("Graphics/Pokemon/Eggs/%s_cracks", @pokemon.species)
crackfilename = sprintf("Graphics/Pokemon/Eggs/000_cracks") if !pbResolveBitmap(crackfilename)
crackfilename = pbResolveBitmap(crackfilename)
crackfilename = GameData::Species.egg_cracks_sprite_filename(@pokemon.species, @pokemon.form)
@hatchSheet = AnimatedBitmap.new(crackfilename)
# Create egg cracks sprite
@sprites["hatch"] = Sprite.new(@viewport)

View File

@@ -811,6 +811,7 @@ class PokemonParty_Scene
currentsel = Settings::MAX_PARTY_SIZE
elsif currentsel == numsprites
currentsel = 0
currentsel = numsprites - 1 if currentsel >= @party.length
end
when Input::UP
if currentsel >= Settings::MAX_PARTY_SIZE
@@ -818,6 +819,7 @@ class PokemonParty_Scene
while currentsel > 0 && currentsel < Settings::MAX_PARTY_SIZE && !@party[currentsel]
currentsel -= 1
end
currentsel = numsprites - 1 if currentsel >= @party.length
else
loop do
currentsel -= 2
@@ -839,6 +841,7 @@ class PokemonParty_Scene
currentsel = Settings::MAX_PARTY_SIZE
elsif currentsel >= numsprites
currentsel = 0
currentsel = numsprites - 1 if currentsel >= @party.length
end
end
return currentsel
@@ -853,7 +856,7 @@ class PokemonParty_Scene
@sprites["pokemon#{i}"].dispose
end
lastselected = @party.length - 1 if lastselected >= @party.length
lastselected = 0 if lastselected < 0
lastselected = Settings::MAX_PARTY_SIZE if lastselected < 0
Settings::MAX_PARTY_SIZE.times do |i|
if @party[i]
@sprites["pokemon#{i}"] = PokemonPartyPanel.new(@party[i], i, @viewport)

View File

@@ -182,7 +182,11 @@ MenuHandlers.add(:pokegear_menu, :phone, {
"order" => 20,
# "condition" => proc { next $PokemonGlobal.phone && $PokemonGlobal.phone.contacts.length > 0 },
"effect" => proc { |menu|
pbFadeOutIn { PokemonPhoneScene.new.start }
pbFadeOutIn {
scene = PokemonPhone_Scene.new
screen = PokemonPhoneScreen.new(scene)
screen.pbStartScreen
}
next false
}
})

View File

@@ -1,10 +1,13 @@
# TODO: Choosable icons/marks for each contact? Add a "sort by" option for these.
#===============================================================================
# Phone screen
# Phone list of contacts
#===============================================================================
class Window_PhoneList < Window_CommandPokemon
attr_accessor :switching
def drawCursor(index, rect)
selarrow = AnimatedBitmap.new("Graphics/UI/Phone/cursor")
if self.index == index
selarrow = AnimatedBitmap.new("Graphics/UI/Phone/cursor")
pbCopyBitmap(self.contents, selarrow.bitmap, rect.x, rect.y + 2)
end
return Rect.new(rect.x + 28, rect.y + 8, rect.width - 16, rect.height)
@@ -12,7 +15,13 @@ class Window_PhoneList < Window_CommandPokemon
def drawItem(index, count, rect)
return if index >= self.top_row + self.page_item_max
super
if self.index == index && @switching
rect = drawCursor(index, rect)
pbDrawShadowText(self.contents, rect.x, rect.y + (self.contents.text_offset_y || 0),
rect.width, rect.height, @commands[index], Color.new(224, 0, 0), Color.new(224, 144, 144))
else
super
end
drawCursor(index - 1, itemRect(index - 1))
end
end
@@ -20,17 +29,57 @@ end
#===============================================================================
#
#===============================================================================
class PokemonPhoneScene
def start
# Get list of contacts
class PokemonPhone_Scene
def pbStartScene
@sprites = {}
# Create viewport
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
@viewport.z = 99999
# Background
addBackgroundPlane(@sprites, "bg", "Phone/bg", @viewport)
# List of contacts
@sprites["list"] = Window_PhoneList.newEmpty(152, 32, Graphics.width - 142, Graphics.height - 80, @viewport)
@sprites["list"].windowskin = nil
# Rematch readiness icons
if Phone.rematches_enabled
@sprites["list"].page_item_max.times do |i|
@sprites["rematch_#{i}"] = IconSprite.new(468, 62 + (i * 32), @viewport)
end
end
# Phone signal icon
@sprites["signal"] = IconSprite.new(Graphics.width - 32, 0, @viewport)
if Phone::Call.can_make?
@sprites["signal"].setBitmap("Graphics/UI/Phone/icon_signal")
else
@sprites["signal"].setBitmap("Graphics/UI/Phone/icon_nosignal")
end
# Title text
@sprites["header"] = Window_UnformattedTextPokemon.newWithSize(
_INTL("Phone"), 2, -18, 128, 64, @viewport
)
@sprites["header"].baseColor = Color.new(248, 248, 248)
@sprites["header"].shadowColor = Color.black
@sprites["header"].windowskin = nil
# Info text about all contacts
@sprites["info"] = Window_AdvancedTextPokemon.newWithSize("", -8, 224, 180, 160, @viewport)
@sprites["info"].windowskin = nil
# Portrait of contact
@sprites["icon"] = IconSprite.new(70, 102, @viewport)
# Contact's location text
@sprites["bottom"] = Window_AdvancedTextPokemon.newWithSize(
"", 162, Graphics.height - 64, Graphics.width - 158, 64, @viewport
)
@sprites["bottom"].windowskin = nil
# Start scene
pbRefreshList
pbFadeInAndShow(@sprites) { pbUpdate }
end
def pbRefreshList
@contacts = []
$PokemonGlobal.phone.contacts.each do |contact|
@contacts.push(contact) if contact.visible?
end
if @contacts.length == 0
pbMessage(_INTL("There are no phone numbers stored."))
return
end
# Create list of commands (display names of contacts) and count rematches
commands = []
rematch_count = 0
@@ -38,95 +87,186 @@ class PokemonPhoneScene
commands.push(contact.display_name)
rematch_count += 1 if contact.can_rematch?
end
# Create viewport and sprites
@sprites = {}
@viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
@viewport.z = 99999
addBackgroundPlane(@sprites, "bg", "Phone/bg", @viewport)
@sprites["list"] = Window_PhoneList.newEmpty(152, 32, Graphics.width - 142, Graphics.height - 80, @viewport)
@sprites["list"].windowskin = nil
# Set list's commands
@sprites["list"].commands = commands
@sprites["list"].page_item_max.times do |i|
@sprites["rematch[#{i}]"] = IconSprite.new(468, 62 + (i * 32), @viewport)
j = i + @sprites["list"].top_item
if j < @contacts.length && @contacts[j].can_rematch?
@sprites["rematch[#{i}]"].setBitmap("Graphics/UI/Phone/icon_rematch")
end
@sprites["list"].index = commands.length - 1 if @sprites["list"].index >= commands.length
if @sprites["list"].top_row > @sprites["list"].itemCount - @sprites["list"].page_item_max
@sprites["list"].top_row = @sprites["list"].itemCount - @sprites["list"].page_item_max
end
@sprites["header"] = Window_UnformattedTextPokemon.newWithSize(
_INTL("Phone"), 2, -18, 128, 64, @viewport
)
@sprites["header"].baseColor = Color.new(248, 248, 248)
@sprites["header"].shadowColor = Color.black
@sprites["header"].windowskin = nil
@sprites["bottom"] = Window_AdvancedTextPokemon.newWithSize(
"", 162, Graphics.height - 64, Graphics.width - 158, 64, @viewport
)
@sprites["bottom"].windowskin = nil
map_name = (@contacts[0].map_id > 0) ? pbGetMapNameFromId(@contacts[0].map_id) : ""
@sprites["bottom"].text = "<ac>" + map_name
@sprites["info"] = Window_AdvancedTextPokemon.newWithSize("", -8, 224, 180, 160, @viewport)
@sprites["info"].windowskin = nil
# Set info text
infotext = _INTL("Registered<br>")
infotext += _INTL(" <r>{1}<br>", @sprites["list"].commands.length)
infotext += _INTL("Waiting for a rematch<r>{1}", rematch_count)
@sprites["info"].text = infotext
@sprites["icon"] = IconSprite.new(70, 102, @viewport)
if @contacts[0].trainer?
filename = GameData::TrainerType.charset_filename(@contacts[0].trainer_type)
else
filename = sprintf("Graphics/Characters/phone%03d", @contacts[0].common_event_id)
pbRefreshScreen
end
def pbRefreshScreen
@sprites["list"].refresh
# Redraw rematch readiness icons
if @sprites["rematch_0"]
@sprites["list"].page_item_max.times do |i|
@sprites["rematch_#{i}"].clearBitmaps
j = i + @sprites["list"].top_item
if j < @contacts.length && @contacts[j].can_rematch?
@sprites["rematch_#{i}"].setBitmap("Graphics/UI/Phone/icon_rematch")
end
end
end
@sprites["icon"].setBitmap(filename)
charwidth = @sprites["icon"].bitmap.width
charheight = @sprites["icon"].bitmap.height
@sprites["icon"].x = 86 - (charwidth / 8)
@sprites["icon"].y = 134 - (charheight / 8)
@sprites["icon"].src_rect = Rect.new(0, 0, charwidth / 4, charheight / 4)
# Start scene
pbFadeInAndShow(@sprites)
# Get the selected contact
contact = @contacts[@sprites["list"].index]
if contact
# Redraw contact's portrait
if contact.trainer?
filename = GameData::TrainerType.charset_filename(contact.trainer_type)
else
filename = sprintf("Graphics/Characters/phone%03d", contact.common_event_id)
end
@sprites["icon"].setBitmap(filename)
charwidth = @sprites["icon"].bitmap.width
charheight = @sprites["icon"].bitmap.height
@sprites["icon"].x = 86 - (charwidth / 8)
@sprites["icon"].y = 134 - (charheight / 8)
@sprites["icon"].src_rect = Rect.new(0, 0, charwidth / 4, charheight / 4)
# Redraw contact's location text
map_name = (contact.map_id > 0) ? pbGetMapNameFromId(contact.map_id) : ""
@sprites["bottom"].text = "<ac>" + map_name
else
@sprites["icon"].setBitmap(nil)
@sprites["bottom"].text = ""
end
end
def pbChooseContact
pbActivateWindow(@sprites, "list") {
oldindex = -1
index = -1
switch_index = -1
loop do
Graphics.update
Input.update
pbUpdateSpriteHash(@sprites)
# Cursor moved, update display
if @sprites["list"].index != oldindex
contact = @contacts[@sprites["list"].index]
if contact.trainer?
filename = GameData::TrainerType.charset_filename(contact.trainer_type)
if @sprites["list"].index != index
if switch_index >= 0
real_contacts = $PokemonGlobal.phone.contacts
real_contacts.insert(@sprites["list"].index, real_contacts.delete_at(index))
pbRefreshList
else
filename = sprintf("Graphics/Characters/phone%03d", contact.common_event_id)
end
@sprites["icon"].setBitmap(filename)
charwidth = @sprites["icon"].bitmap.width
charheight = @sprites["icon"].bitmap.height
@sprites["icon"].x = 86 - (charwidth / 8)
@sprites["icon"].y = 134 - (charheight / 8)
@sprites["icon"].src_rect = Rect.new(0, 0, charwidth / 4, charheight / 4)
map_name = (contact.map_id > 0) ? pbGetMapNameFromId(contact.map_id) : ""
@sprites["bottom"].text = "<ac>" + map_name
@sprites["list"].page_item_max.times do |i|
@sprites["rematch[#{i}]"].clearBitmaps
j = i + @sprites["list"].top_item
if j < @contacts.length && @contacts[j].can_rematch?
@sprites["rematch[#{i}]"].setBitmap("Graphics/UI/Phone/icon_rematch")
end
pbRefreshScreen
end
end
index = @sprites["list"].index
# Get inputs
if Input.trigger?(Input::BACK)
pbPlayCloseMenuSE
break
elsif Input.trigger?(Input::USE)
index = @sprites["list"].index
Phone::Call.make_outgoing(@contacts[index]) if index >= 0
if switch_index >= 0
if Input.trigger?(Input::ACTION) ||
Input.trigger?(Input::USE)
pbPlayDecisionSE
@sprites["list"].switching = false
switch_index = -1
pbRefreshScreen
elsif Input.trigger?(Input::BACK)
pbPlayCancelSE
real_contacts = $PokemonGlobal.phone.contacts
real_contacts.insert(switch_index, real_contacts.delete_at(@sprites["list"].index))
@sprites["list"].index = switch_index
@sprites["list"].switching = false
switch_index = -1
pbRefreshList
end
else
if Input.trigger?(Input::ACTION)
switch_index = @sprites["list"].index
@sprites["list"].switching = true
pbRefreshScreen
elsif Input.trigger?(Input::BACK)
pbPlayCloseMenuSE
return nil
elsif Input.trigger?(Input::USE)
return @contacts[index] if index >= 0
end
end
end
}
pbFadeOutAndHide(@sprites)
end
def pbEndScene
pbFadeOutAndHide(@sprites) { pbUpdate }
pbDisposeSpriteHash(@sprites)
@viewport.dispose
end
def pbUpdate
pbUpdateSpriteHash(@sprites)
end
end
#===============================================================================
#
#===============================================================================
class PokemonPhoneScreen
def initialize(scene)
@scene = scene
end
def pbStartScreen
if $PokemonGlobal.phone.contacts.none? { |con| con.visible? }
pbMessage(_INTL("There are no phone numbers stored."))
return
end
@scene.pbStartScene
loop do
contact = @scene.pbChooseContact
break if !contact
commands = []
commands.push(_INTL("Call"))
commands.push(_INTL("Delete")) if contact.can_hide?
commands.push(_INTL("Sort Contacts"))
commands.push(_INTL("Cancel"))
cmd = pbShowCommands(nil, commands, -1)
cmd += 1 if cmd >=1 && !contact.can_hide?
case cmd
when 0 # Call
Phone::Call.make_outgoing(contact)
when 1 # Delete
name = contact.display_name
if pbConfirmMessage(_INTL("Are you sure you want to delete {1} from your phone?", name))
contact.visible = false
$PokemonGlobal.phone.sort_contacts
@scene.pbRefreshList
pbMessage(_INTL("{1} was deleted from your phone contacts.", name))
if $PokemonGlobal.phone.contacts.none? { |con| con.visible? }
pbMessage(_INTL("There are no phone numbers stored."))
break
end
end
when 2 # Sort Contacts
case pbMessage(_INTL("How do you want to sort the contacts?"),
[_INTL("By name"),
_INTL("By Trainer type"),
_INTL("Special contacts first"),
_INTL("Cancel")], -1, nil, 0)
when 0 # By name
$PokemonGlobal.phone.contacts.sort! { |a, b| a.name <=> b.name }
$PokemonGlobal.phone.sort_contacts
@scene.pbRefreshList
when 1 # By trainer type
$PokemonGlobal.phone.contacts.sort! { |a, b| a.display_name <=> b.display_name }
$PokemonGlobal.phone.sort_contacts
@scene.pbRefreshList
when 2 # Special contacts first
new_contacts = []
2.times do |i|
$PokemonGlobal.phone.contacts.each do |con|
next if (i == 0 && con.trainer?) || (i == 1 && !con.trainer?)
new_contacts.push(con)
end
end
$PokemonGlobal.phone.contacts = new_contacts
$PokemonGlobal.phone.sort_contacts
@scene.pbRefreshList
end
end
end
@scene.pbEndScene
end
end

View File

@@ -79,7 +79,6 @@ class BugContestState
@contestMaps.push(map)
end
end
echoln "contest maps: #{@contestMaps}"
end
# Reception map is handled separately from contest map since the reception map
@@ -94,7 +93,6 @@ class BugContestState
@reception.push(map)
end
end
echoln "reception maps: #{@reception}"
end
def pbOffLimits?(map)

View File

@@ -70,7 +70,32 @@ def toCelsius(fahrenheit)
return ((fahrenheit - 32) * 5.0 / 9.0).round
end
#===============================================================================
# This class is designed to favor different values more than a uniform
# random generator does.
#===============================================================================
class AntiRandom
def initialize(size)
@old = []
@new = Array.new(size) { |i| i }
end
def get
if @new.length == 0 # No new values
@new = @old.clone
@old.clear
end
if @old.length > 0 && rand(7) == 0 # Get old value
return @old[rand(@old.length)]
end
if @new.length > 0 # Get new value
ret = @new.delete_at(rand(@new.length))
@old.push(ret)
return ret
end
return @old[rand(@old.length)] # Get old value
end
end
#===============================================================================
# Constants utilities
@@ -125,8 +150,6 @@ def getConstantNameOrValue(mod, value)
return value.inspect
end
#===============================================================================
# Event utilities
#===============================================================================
@@ -196,8 +219,6 @@ def pbNoticePlayer(event)
pbMoveTowardPlayer(event)
end
#===============================================================================
# Player-related utilities, random name generator
#===============================================================================
@@ -339,8 +360,6 @@ def getRandomName(maxLength = 100)
return getRandomNameEx(2, nil, nil, maxLength)
end
#===============================================================================
# Regional and National Pokédexes utilities
#===============================================================================
@@ -387,8 +406,6 @@ def pbGetRegionalDexLength(region_dex)
return (dex_list) ? dex_list.length : 0
end
#===============================================================================
# Other utilities
#===============================================================================

View File

@@ -233,7 +233,7 @@ MenuHandlers.add(:debug_menu, :test_wild_battle_advanced, {
params.setCancelValue(0)
level = pbMessageChooseNumber(_INTL("Set the wild {1}'s level.",
GameData::Species.get(species).name), params)
pkmn.push(Pokemon.new(species, level)) if level > 0
pkmn.push(pbGenerateWildPokemon(species, level)) if level > 0
end
else # Edit a Pokémon
if pbConfirmMessage(_INTL("Change this Pokémon?"))
@@ -336,6 +336,7 @@ MenuHandlers.add(:debug_menu, :test_trainer_battle_advanced, {
trainerdata = pbListScreen(_INTL("CHOOSE A TRAINER"), TrainerBattleLister.new(0, false))
if trainerdata
tr = pbLoadTrainer(trainerdata[0], trainerdata[1], trainerdata[2])
EventHandlers.trigger(:on_trainer_load, tr)
trainers.push([0, tr])
end
else # Edit a trainer
@@ -344,6 +345,7 @@ MenuHandlers.add(:debug_menu, :test_trainer_battle_advanced, {
TrainerBattleLister.new(trainers[trainerCmd][0], false))
if trainerdata
tr = pbLoadTrainer(trainerdata[0], trainerdata[1], trainerdata[2])
EventHandlers.trigger(:on_trainer_load, tr)
trainers[trainerCmd] = [0, tr]
end
elsif pbConfirmMessage(_INTL("Delete this trainer?"))
@@ -386,6 +388,17 @@ MenuHandlers.add(:debug_menu, :reset_trainers, {
}
})
MenuHandlers.add(:debug_menu, :toggle_rematches_possible, {
"name" => _INTL("Toggle Phone Rematches Possible"),
"parent" => :battle_menu,
"description" => _INTL("Toggles whether trainers in the phone can be rebattled."),
"effect" => proc {
Phone.rematches_enabled = !Phone.rematches_enabled
pbMessage(_INTL("Trainers in the phone can now be rebattled.")) if Phone.rematches_enabled
pbMessage(_INTL("Trainers in the phone cannot be rebattled.")) if !Phone.rematches_enabled
}
})
MenuHandlers.add(:debug_menu, :ready_rematches, {
"name" => _INTL("Ready All Phone Rematches"),
"parent" => :battle_menu,
@@ -1115,6 +1128,8 @@ MenuHandlers.add(:debug_menu, :create_pbs_files, {
"abilities.txt",
"battle_facility_lists.txt",
"berry_plants.txt",
"dungeon_parameters.txt",
"dungeon_tilesets.txt",
"encounters.txt",
"items.txt",
"map_connections.txt",
@@ -1140,23 +1155,25 @@ MenuHandlers.add(:debug_menu, :create_pbs_files, {
when 1 then Compiler.write_abilities
when 2 then Compiler.write_trainer_lists
when 3 then Compiler.write_berry_plants
when 4 then Compiler.write_encounters
when 5 then Compiler.write_items
when 6 then Compiler.write_connections
when 7 then Compiler.write_map_metadata
when 8 then Compiler.write_metadata
when 9 then Compiler.write_moves
when 10 then Compiler.write_phone
when 11 then Compiler.write_pokemon
when 12 then Compiler.write_pokemon_forms
when 13 then Compiler.write_pokemon_metrics
when 14 then Compiler.write_regional_dexes
when 15 then Compiler.write_ribbons
when 16 then Compiler.write_shadow_pokemon
when 17 then Compiler.write_town_map
when 18 then Compiler.write_trainer_types
when 19 then Compiler.write_trainers
when 20 then Compiler.write_types
when 4 then Compiler.write_dungeon_parameters
when 5 then Compiler.write_dungeon_tilesets
when 6 then Compiler.write_encounters
when 7 then Compiler.write_items
when 8 then Compiler.write_connections
when 9 then Compiler.write_map_metadata
when 10 then Compiler.write_metadata
when 11 then Compiler.write_moves
when 12 then Compiler.write_phone
when 13 then Compiler.write_pokemon
when 14 then Compiler.write_pokemon_forms
when 15 then Compiler.write_pokemon_metrics
when 16 then Compiler.write_regional_dexes
when 17 then Compiler.write_ribbons
when 18 then Compiler.write_shadow_pokemon
when 19 then Compiler.write_town_map
when 20 then Compiler.write_trainer_types
when 21 then Compiler.write_trainers
when 22 then Compiler.write_types
else break
end
pbMessage(_INTL("File written."))

View File

@@ -773,6 +773,11 @@ MenuHandlers.add(:pokemon_debug_menu, :species_and_form, {
end
if formcmds[0].length <= 1
screen.pbDisplay(_INTL("Species {1} only has one form.", pkmn.speciesName))
if pkmn.form != 0 && screen.pbConfirm(_INTL("Do you want to reset the form to 0?"))
pkmn.form = 0
$player.pokedex.register(pkmn) if !settingUpBattle && !pkmn.egg?
screen.pbRefreshSingle(pkmnid)
end
else
cmd2 = screen.pbShowCommands(_INTL("Set the Pokémon's form."), formcmds[1], cmd2)
next if cmd2 < 0

View File

@@ -757,7 +757,6 @@ module Compiler
modify_pbs_file_contents_before_compiling
compile_town_map
compile_connections
compile_phone
compile_types
compile_abilities
compile_moves # Depends on Type
@@ -775,12 +774,18 @@ module Compiler
compile_trainer_lists # Depends on TrainerType
compile_metadata # Depends on TrainerType
compile_map_metadata
compile_dungeon_tilesets
compile_dungeon_parameters
compile_phone # Depends on TrainerType
end
def compile_all(mustCompile)
return if !mustCompile
if !mustCompile
Console.echo_h1(_INTL("Game did not compile data"))
return
end
FileLineData.clear
Console.echo_h1 _INTL("Starting full compile")
Console.echo_h1 _INTL("Compiling all data")
compile_pbs_files
compile_animations
compile_trainer_events(mustCompile)
@@ -793,7 +798,7 @@ module Compiler
System.reload_cache
Console.echo_done(true)
echoln ""
Console.echo_h2("Successfully fully compiled", text: :green)
Console.echo_h2("Successfully compiled all data", text: :green)
end
def main
@@ -802,6 +807,8 @@ module Compiler
dataFiles = [
"abilities.dat",
"berry_plants.dat",
"dungeon_parameters.dat",
"dungeon_tilesets.dat",
"encounters.dat",
"items.dat",
"map_connections.dat",
@@ -825,6 +832,8 @@ module Compiler
"abilities.txt",
"battle_facility_lists.txt",
"berry_plants.txt",
"dungeon_parameters.txt",
"dungeon_tilesets.txt",
"encounters.txt",
"items.txt",
"map_connections.txt",

View File

@@ -102,37 +102,69 @@ module Compiler
def compile_phone(path = "PBS/phone.txt")
return if !safeExists?(path)
compile_pbs_file_message_start(path)
database = PhoneDatabase.new
sections = []
File.open(path, "rb") { |f|
pbEachSection(f) { |section, name|
case name
when "<Generics>"
database.generics = section
sections.concat(section)
when "<BattleRequests>"
database.battleRequests = section
sections.concat(section)
when "<GreetingsMorning>"
database.greetingsMorning = section
sections.concat(section)
when "<GreetingsEvening>"
database.greetingsEvening = section
sections.concat(section)
when "<Greetings>"
database.greetings = section
sections.concat(section)
when "<Bodies1>"
database.bodies1 = section
sections.concat(section)
when "<Bodies2>"
database.bodies2 = section
sections.concat(section)
GameData::PhoneMessage::DATA.clear
schema = GameData::PhoneMessage::SCHEMA
messages = []
contact_hash = nil
# Read each line of phone.txt at a time and compile it as a contact property
idx = 0
pbCompilerEachPreppedLine(path) { |line, line_no|
echo "." if idx % 50 == 0
idx += 1
Graphics.update if idx % 250 == 0
if line[/^\s*\[\s*(.+)\s*\]\s*$/]
# New section [trainer_type, name] or [trainer_type, name, version]
if contact_hash
# Add contact's data to records
if contact_hash[:trainer_type] == "default"
contact_hash[:id] = contact_hash[:trainer_type]
else
contact_hash[:id] = [contact_hash[:trainer_type], contact_hash[:name], contact_hash[:version]]
end
GameData::PhoneMessage.register(contact_hash)
end
}
# Construct contact hash
header = $~[1]
if header.strip.downcase == "default"
contact_hash = {
:trainer_type => "default"
}
else
line_data = pbGetCsvRecord($~[1], line_no, [0, "esU", :TrainerType])
contact_hash = {
:trainer_type => line_data[0],
:name => line_data[1],
:version => line_data[2] || 0
}
end
elsif line[/^\s*(\w+)\s*=\s*(.*)$/]
# XXX=YYY lines
if !contact_hash
raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport)
end
property_name = $~[1]
line_schema = schema[property_name]
next if !line_schema
property_value = pbGetCsvRecord($~[2], line_no, line_schema)
# Record XXX=YYY setting
contact_hash[line_schema[0]] ||= []
contact_hash[line_schema[0]].push(property_value)
messages.push(property_value)
end
}
MessageTypes.setMessagesAsHash(MessageTypes::PhoneMessages, sections)
save_data(database, "Data/phone.dat")
# Add last contact's data to records
if contact_hash
# Add contact's data to records
if contact_hash[:trainer_type] == "default"
contact_hash[:id] = contact_hash[:trainer_type]
else
contact_hash[:id] = [contact_hash[:trainer_type], contact_hash[:name], contact_hash[:version]]
end
GameData::PhoneMessage.register(contact_hash)
end
# Save all data
GameData::PhoneMessage.save
MessageTypes.setMessagesAsHash(MessageTypes::PhoneMessages, messages)
process_pbs_file_message_end
end
@@ -1816,6 +1848,110 @@ module Compiler
process_pbs_file_message_end
end
#=============================================================================
# Compile dungeon tileset data
#=============================================================================
def compile_dungeon_tilesets(path = "PBS/dungeon_tilesets.txt")
compile_pbs_file_message_start(path)
GameData::DungeonTileset::DATA.clear
schema = GameData::DungeonTileset::SCHEMA
tileset_hash = nil
# Read each line of dungeon_tilesets.txt at a time and compile it as a tileset property
idx = 0
pbCompilerEachPreppedLine(path) { |line, line_no|
echo "." if idx % 50 == 0
idx += 1
Graphics.update if idx % 250 == 0
if line[/^\s*\[\s*(.+)\s*\]\s*$/]
# New section
# Add tileset's data to records
GameData::DungeonTileset.register(tileset_hash) if tileset_hash
# Construct tileset hash
tileset_hash = {
:id => $~[1].to_i,
:tile => [],
:autotile => []
}
elsif line[/^\s*(\w+)\s*=\s*(.*)$/]
# XXX=YYY lines
if !tileset_hash
raise _INTL("Expected a section at the beginning of the file.\r\n{1}", FileLineData.linereport)
end
property_name = $~[1]
line_schema = schema[property_name]
next if !line_schema
property_value = pbGetCsvRecord($~[2], line_no, line_schema)
# Record XXX=YYY setting
case property_name
when "Tile", "Autotile"
tileset_hash[line_schema[0]].push(property_value)
else
tileset_hash[line_schema[0]] = property_value
end
end
}
# Add last tileset's data to records
GameData::DungeonTileset.register(tileset_hash) if tileset_hash
# Save all data
GameData::DungeonTileset.save
process_pbs_file_message_end
end
#=============================================================================
# Compile dungeon parameters data
#=============================================================================
def compile_dungeon_parameters(path = "PBS/dungeon_parameters.txt")
compile_pbs_file_message_start(path)
GameData::DungeonParameters::DATA.clear
schema = GameData::DungeonParameters::SCHEMA
# Read from PBS file
File.open(path, "rb") { |f|
FileLineData.file = path # For error reporting
# Read a whole section's lines at once, then run through this code.
# contents is a hash containing all the XXX=YYY lines in that section, where
# the keys are the XXX and the values are the YYY (as unprocessed strings).
idx = 0
pbEachFileSection(f) { |contents, section_name|
echo "." if idx % 50 == 0
idx += 1
Graphics.update if idx % 250 == 0
FileLineData.setSection(section_name, "header", nil) # For error reporting
# Split section_name into an area and version number
split_section_name = section_name.split(/[-,\s]/)
if split_section_name.length == 0 || split_section_name.length > 2
raise _INTL("Section name {1} is invalid ({2}). Expected syntax like [XXX] or [XXX,Y] (XXX=area, Y=version).", section_name, path)
end
area_symbol = split_section_name[0].downcase.to_sym
version = (split_section_name[1]) ? csvPosInt!(split_section_name[1]) : 0
# Construct parameters hash
area_version = (version > 0) ? sprintf("%s_%d", area_symbol.to_s, version).to_sym : area_symbol
parameters_hash = {
:id => area_version,
:area => area_symbol,
:version => version
}
# Go through schema hash of compilable data and compile this section
schema.each_key do |key|
# Skip empty properties (none are required)
if nil_or_empty?(contents[key])
contents[key] = nil
next
end
FileLineData.setSection(section_name, key, contents[key]) # For error reporting
# Compile value for key
value = pbGetCsvRecord(contents[key], key, schema[key])
value = nil if value.is_a?(Array) && value.length == 0
parameters_hash[schema[key][0]] = value
end
# Add parameters data to records
GameData::DungeonParameters.register(parameters_hash)
}
}
# Save all data
GameData::DungeonParameters.save
process_pbs_file_message_end
end
#=============================================================================
# Compile battle animations
#=============================================================================

View File

@@ -103,32 +103,26 @@ module Compiler
# Save phone messages to PBS file
#=============================================================================
def write_phone(path = "PBS/phone.txt")
data = load_data("Data/phone.dat") rescue nil
return if !data
write_pbs_file_message_start(path)
keys = GameData::PhoneMessage::SCHEMA.keys
File.open(path, "wb") { |f|
add_PBS_header_to_file(f)
f.write("\#-------------------------------\r\n")
f.write("[<Generics>]\r\n")
f.write(data.generics.join("\r\n") + "\r\n")
f.write("\#-------------------------------\r\n")
f.write("[<BattleRequests>]\r\n")
f.write(data.battleRequests.join("\r\n") + "\r\n")
f.write("\#-------------------------------\r\n")
f.write("[<GreetingsMorning>]\r\n")
f.write(data.greetingsMorning.join("\r\n") + "\r\n")
f.write("\#-------------------------------\r\n")
f.write("[<GreetingsEvening>]\r\n")
f.write(data.greetingsEvening.join("\r\n") + "\r\n")
f.write("\#-------------------------------\r\n")
f.write("[<Greetings>]\r\n")
f.write(data.greetings.join("\r\n") + "\r\n")
f.write("\#-------------------------------\r\n")
f.write("[<Bodies1>]\r\n")
f.write(data.bodies1.join("\r\n") + "\r\n")
f.write("\#-------------------------------\r\n")
f.write("[<Bodies2>]\r\n")
f.write(data.bodies2.join("\r\n") + "\r\n")
# Write message sets
GameData::PhoneMessage.each do |contact|
f.write("\#-------------------------------\r\n")
if contact.id == "default"
f.write("[Default]\r\n")
elsif contact.version > 0
f.write(sprintf("[%s,%s,%d]\r\n", contact.trainer_type, contact.real_name, contact.version))
else
f.write(sprintf("[%s,%s]\r\n", contact.trainer_type, contact.real_name))
end
keys.each do |key|
msgs = contact.property_from_string(key)
next if !msgs || msgs.length == 0
msgs.each { |msg| f.write(key + " = " + msg + "\r\n") }
end
end
}
process_pbs_file_message_end
end
@@ -879,6 +873,91 @@ module Compiler
process_pbs_file_message_end
end
#=============================================================================
# Save dungeon tileset contents data to PBS file
#=============================================================================
def write_dungeon_tilesets(path = "PBS/dungeon_tilesets.txt")
write_pbs_file_message_start(path)
tilesets = load_data("Data/Tilesets.rxdata")
schema = GameData::DungeonTileset::SCHEMA
keys = schema.keys
File.open(path, "wb") { |f|
idx = 0
add_PBS_header_to_file(f)
GameData::DungeonTileset.each do |tileset_data|
echo "." if idx % 50 == 0
idx += 1
Graphics.update if idx % 250 == 0
f.write("\#-------------------------------\r\n")
tileset_name = (tilesets && tilesets[tileset_data.id]) ? tilesets[tileset_data.id].name : nil
if tileset_name
f.write(sprintf("[%d] # %s\r\n", tileset_data.id, tileset_name))
else
f.write(sprintf("[%d]\r\n", tileset_data.id))
end
keys.each do |key|
if ["Autotile", "Tile"].include?(key)
tiles = tileset_data.tile_type_ids
tiles.each do |tile_type, tile_ids|
tile_ids.each do |tile|
next if tile[1] # Tile was auto-generated from "walls" property
if tile[0] < 384
next if key == "Tile"
f.write(sprintf("Autotile = %i,%s", tile[0] / 48, tile_type.to_s))
else
next if key == "Autotile"
f.write(sprintf("Tile = %i,%s", tile[0] - 384, tile_type.to_s))
end
f.write("\r\n")
end
end
else
record = tileset_data.property_from_string(key)
next if record.nil? || (record.is_a?(Array) && record.empty?)
next if record == false || record == 0
f.write(sprintf("%s = ", key))
pbWriteCsvRecord(record, f, schema[key])
f.write("\r\n")
end
end
end
}
process_pbs_file_message_end
end
#=============================================================================
# Save dungeon parameters to PBS file
#=============================================================================
def write_dungeon_parameters(path = "PBS/dungeon_parameters.txt")
write_pbs_file_message_start(path)
schema = GameData::DungeonParameters::SCHEMA
keys = schema.keys
# Write file
File.open(path, "wb") { |f|
idx = 0
add_PBS_header_to_file(f)
GameData::DungeonParameters.each do |parameters_data|
echo "." if idx % 50 == 0
idx += 1
Graphics.update if idx % 250 == 0
f.write("\#-------------------------------\r\n")
if parameters_data.version > 0
f.write(sprintf("[%s,%d]\r\n", parameters_data.area, parameters_data.version))
else
f.write(sprintf("[%s]\r\n", parameters_data.area))
end
keys.each do |key|
value = parameters_data.property_from_string(key)
next if !value || (value.is_a?(Array) && value.length == 0)
f.write(sprintf("%s = ", key))
pbWriteCsvRecord(value, f, schema[key])
f.write("\r\n")
end
end
}
process_pbs_file_message_end
end
#=============================================================================
# Save all data to PBS files
#=============================================================================
@@ -886,7 +965,6 @@ module Compiler
Console.echo_h1 _INTL("Writing all PBS files")
write_town_map
write_connections
write_phone
write_types
write_abilities
write_moves
@@ -904,6 +982,9 @@ module Compiler
write_trainer_lists
write_metadata
write_map_metadata
write_dungeon_tilesets
write_dungeon_parameters
write_phone
echoln ""
Console.echo_h2("Successfully rewrote all PBS files", text: :green)
end

View File

@@ -506,7 +506,7 @@ module Compiler
end
end
# Compile the trainer comments
rewriteComments = true #false # You can change this
rewriteComments = false # You can change this
battles = []
trtype = nil
trname = nil
@@ -519,6 +519,7 @@ module Compiler
endifswitch = []
vanishifswitch = []
regspeech = nil
common_event = 0
commands.each do |command|
if command[/^Battle\:\s*([\s\S]+)$/i]
battles.push($~[1])
@@ -558,6 +559,9 @@ module Compiler
elsif command[/^RegSpeech\:\s*([\s\S]+)$/i]
regspeech = $~[1].gsub(/^\s+/, "").gsub(/\s+$/, "")
push_comment(firstpage.list, command) if rewriteComments
elsif command[/^CommonEvent\:\s*(\d+)$/i]
common_event = $~[1].to_i
push_comment(firstpage.list, command) if rewriteComments
end
end
return nil if battles.length <= 0
@@ -600,10 +604,18 @@ module Compiler
push_text(firstpage.list, regspeech, 2)
push_choices(firstpage.list, ["Yes", "No"], 2, 2)
push_choice(firstpage.list, 0, "Yes", 3)
if battleid > 0
push_script(firstpage.list, sprintf("Phone.add(get_self,\n %s, %d, %d\n)", brieftrcombo, battles.length, battleid), 3)
if common_event > 0
if battleid > 0
push_script(firstpage.list, sprintf("Phone.add(get_self,\n %s, %d, %d, %d\n)", brieftrcombo, battles.length, battleid, common_event), 3)
else
push_script(firstpage.list, sprintf("Phone.add(get_self,\n %s, %d, nil, %d\n)", brieftrcombo, battles.length, common_event), 3)
end
else
push_script(firstpage.list, sprintf("Phone.add(get_self,\n %s, %d\n)", brieftrcombo, battles.length), 3)
if battleid > 0
push_script(firstpage.list, sprintf("Phone.add(get_self,\n %s, %d, %d\n)", brieftrcombo, battles.length, battleid), 3)
else
push_script(firstpage.list, sprintf("Phone.add(get_self,\n %s, %d\n)", brieftrcombo, battles.length), 3)
end
end
push_choice(firstpage.list, 1, "No", 3)
push_choices_end(firstpage.list, 3)
@@ -679,10 +691,18 @@ module Compiler
push_text(lastpage.list, regspeech, 1)
push_choices(lastpage.list, ["Yes", "No"], 2, 1)
push_choice(lastpage.list, 0, "Yes", 2)
if battleid > 0
push_script(lastpage.list, sprintf("Phone.add(get_self,\n %s, %d, %d\n)", brieftrcombo, battles.length, battleid), 2)
if common_event > 0
if battleid > 0
push_script(lastpage.list, sprintf("Phone.add(get_self,\n %s, %d, %d, %d\n)", brieftrcombo, battles.length, battleid, common_event), 2)
else
push_script(lastpage.list, sprintf("Phone.add(get_self,\n %s, %d, nil, %d\n)", brieftrcombo, battles.length, common_event), 2)
end
else
push_script(lastpage.list, sprintf("Phone.add(get_self,\n %s, %d\n)", brieftrcombo, battles.length), 2)
if battleid > 0
push_script(lastpage.list, sprintf("Phone.add(get_self,\n %s, %d, %d\n)", brieftrcombo, battles.length, battleid), 2)
else
push_script(lastpage.list, sprintf("Phone.add(get_self,\n %s, %d\n)", brieftrcombo, battles.length), 2)
end
end
push_choice(lastpage.list, 1, "No", 2)
push_choices_end(lastpage.list, 2)