Initial commit

This commit is contained in:
Maruno17
2020-09-04 22:00:59 +01:00
commit ba94119d02
300 changed files with 227558 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
# Audio and Graphics folders
Audio/
Graphics/
# Data folder, but not Data/Scripts folder
Data/*
!Data/Scripts.rxdata
!Data/Scripts/
# Files in the main project folder
Game.exe
Game.ini
Game.rxproj
RGSS*.dll

BIN
Data/Scripts.rxdata Normal file

Binary file not shown.

View File

@@ -0,0 +1,353 @@
#==============================================================================#
# Pokémon Essentials #
# Version 18 #
#==============================================================================#
#===============================================================================
# * The default screen width (at a zoom of 1.0; size is half this at zoom 0.5).
# * The default screen height (at a zoom of 1.0).
# * The default screen zoom. (1.0 means each tile is 32x32 pixels, 0.5 means
# each tile is 16x16 pixels, 2.0 means each tile is 64x64 pixels.)
# * Whether full-screen display lets the border graphic go outside the edges of
# the screen (true), or forces the border graphic to always be fully shown
# (false).
# * The width of each of the left and right sides of the screen border. This is
# added on to the screen width above, only if the border is turned on.
# * The height of each of the top and bottom sides of the screen border. This is
# added on to the screen height above, only if the border is turned on.
# * Map view mode (0=original, 1=custom, 2=perspective).
#===============================================================================
SCREEN_WIDTH = 512
SCREEN_HEIGHT = 384
SCREEN_ZOOM = 1.0
BORDER_FULLY_SHOWS = false
BORDER_WIDTH = 78
BORDER_HEIGHT = 78
MAP_VIEW_MODE = 1
# To forbid the player from changing the screen size themselves, quote out or
# delete the relevant bit of code in the PScreen_Options script section.
#===============================================================================
# * The maximum level Pokémon can reach.
# * The level of newly hatched Pokémon.
# * The odds of a newly generated Pokémon being shiny (out of 65536).
# * The odds of a wild Pokémon/bred egg having Pokérus (out of 65536).
#===============================================================================
MAXIMUM_LEVEL = 100
EGG_LEVEL = 1
SHINY_POKEMON_CHANCE = 8
POKERUS_CHANCE = 3
#===============================================================================
# * Whether outdoor maps should be shaded according to the time of day.
#===============================================================================
TIME_SHADING = true
#===============================================================================
# * Whether poisoned Pokémon will lose HP while walking around in the field.
# * Whether poisoned Pokémon will faint while walking around in the field
# (true), or survive the poisoning with 1HP (false).
# * Whether fishing automatically hooks the Pokémon (if false, there is a
# reaction test first).
# * Whether the player can surface from anywhere while diving (true), or only in
# spots where they could dive down from above (false).
# * Whether planted berries grow according to Gen 4 mechanics (true) or Gen 3
# mechanics (false).
# * Whether TMs can be used infinitely as in Gen 5 (true), or are one-use-only
# as in older Gens (false).
#===============================================================================
POISON_IN_FIELD = true
POISON_FAINT_IN_FIELD = false
FISHING_AUTO_HOOK = false
DIVING_SURFACE_ANYWHERE = false
NEW_BERRY_PLANTS = true
INFINITE_TMS = true
#===============================================================================
# * The number of steps allowed before a Safari Zone game is over (0=infinite).
# * The number of seconds a Bug Catching Contest lasts for (0=infinite).
#===============================================================================
SAFARI_STEPS = 600
BUG_CONTEST_TIME = 1200
#===============================================================================
# * Pairs of map IDs, where the location signpost isn't shown when moving from
# one of the maps in a pair to the other (and vice versa). Useful for
# single long routes/towns that are spread over multiple maps.
# e.g. [4,5,16,17,42,43] will be map pairs 4,5 and 16,17 and 42,43.
# Moving between two maps that have the exact same name won't show the
# location signpost anyway, so you don't need to list those maps here.
#===============================================================================
NO_SIGNPOSTS = []
#===============================================================================
# * The amount of money the player starts the game with.
# * The maximum amount of money the player can have.
# * The maximum number of Game Corner coins the player can have.
# * The maximum length, in characters, that the player's name can be.
#===============================================================================
INITIAL_MONEY = 3000
MAX_MONEY = 999999
MAX_COINS = 99999
MAX_PLAYER_NAME_SIZE = 10
#===============================================================================
# * A set of arrays each containing a trainer type followed by a Global Variable
# number. If the variable isn't set to 0, then all trainers with the
# associated trainer type will be named as whatever is in that variable.
#===============================================================================
RIVAL_NAMES = [
[:RIVAL1,12],
[:RIVAL2,12],
[:CHAMPION,12]
]
#===============================================================================
# * The minimum number of badges required to boost each stat of a player's
# Pokémon by 1.1x, while using moves in battle only.
# * Whether the badge restriction on using certain hidden moves is either owning
# at least a certain number of badges (true), or owning a particular badge
# (false).
# * Depending on FIELD_MOVES_COUNT_BADGES, either the number of badges required
# to use each hidden move, or the specific badge number required to use
# each move. Remember that badge 0 is the first badge, badge 1 is the
# second badge, etc.
# e.g. To require the second badge, put false and 1.
# To require at least 2 badges, put true and 2.
#===============================================================================
NUM_BADGES_BOOST_ATTACK = 1
NUM_BADGES_BOOST_DEFENSE = 5
NUM_BADGES_BOOST_SPATK = 7
NUM_BADGES_BOOST_SPDEF = 7
NUM_BADGES_BOOST_SPEED = 3
FIELD_MOVES_COUNT_BADGES = true
BADGE_FOR_CUT = 1
BADGE_FOR_FLASH = 2
BADGE_FOR_ROCKSMASH = 3
BADGE_FOR_SURF = 4
BADGE_FOR_FLY = 5
BADGE_FOR_STRENGTH = 6
BADGE_FOR_DIVE = 7
BADGE_FOR_WATERFALL = 8
#===============================================================================
# * Whether a move's physical/special category depends on the move itself as in
# newer Gens (true), or on its type as in older Gens (false).
# * Whether the battle mechanics mimic Gen 5 (false) or Gen 7 (true).
# * Whether the Exp gained from beating a Pokémon should be scaled depending on
# the gainer's level as in Gens 5/7 (true) or not as in other Gens (false).
# * Whether the Exp gained from beating a Pokémon should be divided equally
# between each participant (true), or whether each participant should gain
# that much Exp (false). This also applies to Exp gained via the Exp Share
# (held item version) being distributed to all Exp Share holders. This is
# true in Gen 6 and false otherwise.
# * Whether the critical capture mechanic applies (true) or not (false). Note
# that it is based on a total of 600+ species (i.e. that many species need
# to be caught to provide the greatest critical capture chance of 2.5x),
# and there may be fewer species in your game.
# * Whether Pokémon gain Exp for capturing a Pokémon (true) or not (false).
# * An array of items which act as Mega Rings for the player (NPCs don't need a
# Mega Ring item, just a Mega Stone held by their Pokémon).
#===============================================================================
MOVE_CATEGORY_PER_MOVE = true
NEWEST_BATTLE_MECHANICS = true
SCALED_EXP_FORMULA = true
SPLIT_EXP_BETWEEN_GAINERS = false
ENABLE_CRITICAL_CAPTURES = false
GAIN_EXP_FOR_CAPTURE = true
MEGA_RINGS = [:MEGARING,:MEGABRACELET,:MEGACUFF,:MEGACHARM]
#===============================================================================
# * The names of each pocket of the Bag. Leave the first entry blank.
# * The maximum number of slots per pocket (-1 means infinite number). Ignore
# the first number (0).
# * The maximum number of items each slot in the Bag can hold.
# * Whether each pocket in turn auto-sorts itself by item ID number. Ignore the
# first entry (the 0).
#===============================================================================
def pbPocketNames; return ["",
_INTL("Items"),
_INTL("Medicine"),
_INTL("Poké Balls"),
_INTL("TMs & HMs"),
_INTL("Berries"),
_INTL("Mail"),
_INTL("Battle Items"),
_INTL("Key Items")
]; end
BAG_MAX_POCKET_SIZE = [0,-1,-1,-1,-1,-1,-1,-1,-1]
BAG_MAX_PER_SLOT = 999
BAG_POCKET_AUTO_SORT = [0,false,false,false,true,true,false,false,false]
#===============================================================================
# * A set of arrays each containing details of a graphic to be shown on the
# region map if appropriate. The values for each array are as follows:
# - Region number.
# - Global Switch; the graphic is shown if this is ON (non-wall maps only).
# - X coordinate of the graphic on the map, in squares.
# - Y coordinate of the graphic on the map, in squares.
# - Name of the graphic, found in the Graphics/Pictures folder.
# - The graphic will always (true) or never (false) be shown on a wall map.
#===============================================================================
REGION_MAP_EXTRAS = [
[0,51,16,15,"mapHiddenBerth",false],
[0,52,20,14,"mapHiddenFaraday",false]
]
#===============================================================================
# * The name of the person who created the Pokémon storage system.
# * The number of boxes in Pokémon storage.
#===============================================================================
def pbStorageCreator
return _INTL("Bill")
end
NUM_STORAGE_BOXES = 30
#===============================================================================
# * Whether the Pokédex list shown is the one for the player's current region
# (true), or whether a menu pops up for the player to manually choose which
# Dex list to view if more than one is available (false).
# * The names of each Dex list in the game, in order and with National Dex at
# the end. This is also the order that $PokemonGlobal.pokedexUnlocked is
# in, which records which Dexes have been unlocked (first is unlocked by
# default).
# You can define which region a particular Dex list is linked to. This
# means the area map shown while viewing that Dex list will ALWAYS be that
# of the defined region, rather than whichever region the player is
# currently in. To define this, put the Dex name and the region number in
# an array, like the Kanto and Johto Dexes are. The National Dex isn't in
# an array with a region number, therefore its area map is whichever region
# the player is currently in.
# * Whether all forms of a given species will be immediately available to view
# in the Pokédex so long as that species has been seen at all (true), or
# whether each form needs to be seen specifically before that form appears
# in the Pokédex (false).
# * An array of numbers, where each number is that of a Dex list (National Dex
# is -1). All Dex lists included here have the species numbers in them
# reduced by 1, thus making the first listed species have a species number
# of 0 (e.g. Victini in Unova's Dex).
#===============================================================================
USE_CURRENT_REGION_DEX = false
def pbDexNames; return [
[_INTL("Kanto Pokédex"),0],
[_INTL("Johto Pokédex"),1],
_INTL("National Pokédex")
]; end
DEX_SHOWS_ALL_FORMS = false
DEXES_WITH_OFFSETS = []
#===============================================================================
# * A list of maps used by roaming Pokémon. Each map has an array of other maps
# it can lead to.
# * A set of arrays each containing the details of a roaming Pokémon. The
# information within is as follows:
# - Species.
# - Level.
# - Global Switch; the Pokémon roams while this is ON.
# - Encounter type (0=any, 1=grass/walking in cave, 2=surfing, 3=fishing,
# 4=surfing/fishing). See bottom of PField_RoamingPokemon for lists.
# - Name of BGM to play for that encounter (optional).
# - Roaming areas specifically for this Pokémon (optional).
#===============================================================================
RoamingAreas = {
5 => [21,28,31,39,41,44,47,66,69],
21 => [5,28,31,39,41,44,47,66,69],
28 => [5,21,31,39,41,44,47,66,69],
31 => [5,21,28,39,41,44,47,66,69],
39 => [5,21,28,31,41,44,47,66,69],
41 => [5,21,28,31,39,44,47,66,69],
44 => [5,21,28,31,39,41,47,66,69],
47 => [5,21,28,31,39,41,44,66,69],
66 => [5,21,28,31,39,41,44,47,69],
69 => [5,21,28,31,39,41,44,47,66]
}
RoamingSpecies = [
[:LATIAS, 30, 53, 0, "Battle roaming"],
[:LATIOS, 30, 53, 0, "Battle roaming"],
[:KYOGRE, 40, 54, 2, nil, {
2 => [21,31],
21 => [2,31,69],
31 => [2,21,69],
69 => [21,31]
}],
[:ENTEI, 40, 55, 1, nil]
]
#===============================================================================
# * A set of arrays each containing details of a wild encounter that can only
# occur via using the Poké Radar. The information within is as follows:
# - Map ID on which this encounter can occur.
# - Probability that this encounter will occur (as a percentage).
# - Species.
# - Minimum possible level.
# - Maximum possible level (optional).
#===============================================================================
POKE_RADAR_ENCOUNTERS = [
[5, 20, :STARLY, 12, 15],
[21, 10, :STANTLER, 14],
[28, 20, :BUTTERFREE, 15, 18],
[28, 20, :BEEDRILL, 15, 18]
]
#===============================================================================
# * The Global Switch that is set to ON when the player whites out.
# * The Global Switch that is set to ON when the player has seen Pokérus in the
# Poké Center, and doesn't need to be told about it again.
# * The Global Switch which, while ON, makes all wild Pokémon created be
# shiny.
# * The Global Switch which, while ON, makes all Pokémon created considered to
# be met via a fateful encounter.
# * The Global Switch which determines whether the player will lose money if
# they lose a battle (they can still gain money from trainers for winning).
# * The Global Switch which, while ON, prevents all Pokémon in battle from Mega
# Evolving even if they otherwise could.
#===============================================================================
STARTING_OVER_SWITCH = 1
SEEN_POKERUS_SWITCH = 2
SHINY_WILD_POKEMON_SWITCH = 31
FATEFUL_ENCOUNTER_SWITCH = 32
NO_MONEY_LOSS = 33
NO_MEGA_EVOLUTION = 34
#===============================================================================
# * The ID of the common event that runs when the player starts fishing (runs
# instead of showing the casting animation).
# * The ID of the common event that runs when the player stops fishing (runs
# instead of showing the reeling in animation).
#===============================================================================
FISHING_BEGIN_COMMON_EVENT = -1
FISHING_END_COMMON_EVENT = -1
#===============================================================================
# * The ID of the animation played when the player steps on grass (shows grass
# rustling).
# * The ID of the animation played when the player lands on the ground after
# hopping over a ledge (shows a dust impact).
# * The ID of the animation played when a trainer notices the player (an
# exclamation bubble).
# * The ID of the animation played when a patch of grass rustles due to using
# the Poké Radar.
# * The ID of the animation played when a patch of grass rustles vigorously due
# to using the Poké Radar. (Rarer species)
# * The ID of the animation played when a patch of grass rustles and shines due
# to using the Poké Radar. (Shiny encounter)
# * The ID of the animation played when a berry tree grows a stage while the
# player is on the map (for new plant growth mechanics only).
#===============================================================================
GRASS_ANIMATION_ID = 1
DUST_ANIMATION_ID = 2
EXCLAMATION_ANIMATION_ID = 3
RUSTLE_NORMAL_ANIMATION_ID = 1
RUSTLE_VIGOROUS_ANIMATION_ID = 5
RUSTLE_SHINY_ANIMATION_ID = 6
PLANT_SPARKLE_ANIMATION_ID = 7
#===============================================================================
# * An array of available languages in the game, and their corresponding
# message file in the Data folder. Edit only if you have 2 or more
# languages to choose from.
#===============================================================================
LANGUAGES = [
# ["English","english.dat"],
# ["Deutsch","deutsch.dat"]
]

View File

@@ -0,0 +1,211 @@
#===============================================================================
# class Class
#===============================================================================
class Class
def to_sym
return self.to_s.to_sym
end
end
#===============================================================================
# module Comparable
#===============================================================================
unless Comparable.method_defined? :clamp
module Comparable
def clamp(min, max)
if max-min<0
raise ArgumentError("min argument must be smaller than max argument")
end
return (self>max) ? max : (self<min) ? min : self
end
end
end
#===============================================================================
# class Boolean
#===============================================================================
class Boolean
def to_i
return self ? 1 : 0
end
end
#===============================================================================
# class String
#===============================================================================
class String
def starts_with?(str)
proc = (self[0...str.length] == str) if self.length >= str.length
return proc ? proc : false
end
def ends_with?(str)
e = self.length - 1
proc = (self[(e-str.length)...e] == str) if self.length >= str.length
return proc ? proc : false
end
def starts_with_vowel?
return ['a','e','i','o','u'].include?(self[0,1].downcase)
end
def first(n=1)
return self[0...n]
end
def last(n=1)
return self[-n..-1] || self
end
def bytesize
return self.size
end
def capitalize
proc = self.scan(/./)
proc[0] = proc[0].upcase
string = ""
for letter in proc
string += letter
end
return string
end
def capitalize!
self.replace(self.capitalize)
end
def blank?
blank = true
s = self.scan(/./)
for l in s
blank = false if l != ""
end
return blank
end
def cut(bitmap,width)
string = self
width -= bitmap.text_size("...").width
string_width = 0
text = []
for char in string.scan(/./)
wdh = bitmap.text_size(char).width
next if (wdh+string_width) > width
string_width += wdh
text.push(char)
end
text.push("...") if text.length < string.length
new_string = ""
for char in text
new_string += char
end
return new_string
end
end
#===============================================================================
# class Numeric
#===============================================================================
class Numeric
# Turns a number into a string formatted like 12,345,678.
def to_s_formatted
return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
end
end
#===============================================================================
# class Integer
#===============================================================================
class Integer
# Returns an array containing each digit of the number in turn.
def digits(base=10)
quotient, remainder = divmod(base)
(quotient==0) ? [remainder] : quotient.digits(base).push(remainder)
end
end
#===============================================================================
# class Array
#===============================================================================
class Array
def first
return self[0]
end
def last
return self[self.length-1]
end
def ^(other) # xor of two arrays
return (self|other)-(self&other)
end
def shuffle
dup.shuffle!
end unless method_defined? :shuffle
def shuffle!
(size-1).times do |i|
r = i+rand(size-i)
self[i], self[r] = self[r], self[i]
end
self
end unless method_defined? :shuffle!
end
#===============================================================================
# module Enumerable
#===============================================================================
module Enumerable
def transform
ret = []
self.each { |item| ret.push(yield(item)) }
return ret
end
end
#===============================================================================
# Kernel methods
#===============================================================================
def rand(*args)
Kernel.rand(*args)
end
class << Kernel
alias oldRand rand unless method_defined?(:oldRand)
def rand(a = nil, b = nil)
if a.is_a?(Range)
lo = a.min
hi = a.max
return lo + oldRand(hi - lo + 1)
elsif a.is_a?(Numeric)
if b.is_a?(Numeric)
return a + oldRand(b - a + 1)
else
return oldRand(a)
end
elsif a.nil?
return (b) ? oldRand(b) : oldRand(2)
end
end
end
def nil_or_empty?(string)
return string.nil? || !string.is_a?(String) || string.size == 0
end

View File

@@ -0,0 +1,608 @@
$TEST = true if $DEBUG
$DEBUG = true if $TEST
$scene = nil
Font.default_shadow = false if Font.respond_to?(:default_shadow)
Graphics.frame_rate = 40
=begin
class Win32API
class << self
unless defined?(debug_new)
alias debug_new new
end
def new(*args)
File.open("winapi.txt","ab") { |f| f.write("new(#{args[0]},#{args[1]})\r\n") }
b=debug_new(*args)
b.setDllName(args[0],args[1])
return b
end
end
unless defined?(debug_call)
alias debug_call call
end
def setDllName(a,b)
@w32dll=a
@w32name=b
end
def call(*args)
if @w32name!="GetAsyncKeyState"
File.open("winapi.txt","ab") { |f|
f.write("call(#{@w32dll},#{@w32name},#{args.inspect})\r\n")
}
end
debug_call(*args)
end
end
class Bitmap
class << self
unless defined?(debug_new)
alias debug_new new
end
def new(*args)
if args.length==1
File.open("winapib.txt","ab") { |f| f.write("new(#{args[0]})\r\n") }
end
debug_new(*args)
end
end
end
alias debug_load_data load_data
def load_data(*args)
File.open("winapif.txt","ab") { |f| f.write("load(#{args[0]})\r\n") }
debug_load_data(*args)
end
=end
class Hangup < Exception; end
if false
p (Tilemap.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
# no changes
p (Plane.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
# no changes
p (Viewport.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
p (Bitmap.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
# openness(=)
p (Window.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
p (Sprite.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
end
module RPG
class Animation
attr_accessor :id
attr_accessor :name
attr_accessor :animation_name
attr_accessor :animation_hue
attr_accessor :position
attr_accessor :frame_max
attr_accessor :frames
attr_accessor :timings
def initialize
@id = 0
@name = ""
@animation_name = ""
@animation_hue = 0
@position = 1
@frame_max = 1
@frames = [RPG::Animation::Frame.new]
@timings = []
end
end
end
module RPG
class Animation
class Frame
attr_accessor :cell_max
attr_accessor :cell_data
def initialize
@cell_max = 0
@cell_data = Table.new(0, 0)
end
end
end
end
module RPG
class Animation
class Timing
attr_accessor :frame
attr_accessor :se
attr_accessor :flash_scope
attr_accessor :flash_color
attr_accessor :flash_duration
attr_accessor :condition
def initialize
@frame = 0
@se = RPG::AudioFile.new("", 80)
@flash_scope = 0
@flash_color = Color.new(255,255,255,255)
@flash_duration = 5
@condition = 0
end
end
end
end
module RPG
class System
attr_accessor :magic_number
attr_accessor :party_members
attr_accessor :elements
attr_accessor :switches
attr_accessor :variables
attr_accessor :windowskin_name
attr_accessor :title_name
attr_accessor :gameover_name
attr_accessor :battle_transition
attr_accessor :title_bgm
attr_accessor :battle_bgm
attr_accessor :battle_end_me
attr_accessor :gameover_me
attr_accessor :cursor_se
attr_accessor :decision_se
attr_accessor :cancel_se
attr_accessor :buzzer_se
attr_accessor :equip_se
attr_accessor :shop_se
attr_accessor :save_se
attr_accessor :load_se
attr_accessor :battle_start_se
attr_accessor :escape_se
attr_accessor :actor_collapse_se
attr_accessor :enemy_collapse_se
attr_accessor :words
attr_accessor :test_battlers
attr_accessor :test_troop_id
attr_accessor :start_map_id
attr_accessor :start_x
attr_accessor :start_y
attr_accessor :battleback_name
attr_accessor :battler_name
attr_accessor :battler_hue
attr_accessor :edit_map_id
def initialize
@magic_number = 0
@party_members = [1]
@elements = [nil, ""]
@switches = [nil, ""]
@variables = [nil, ""]
@windowskin_name = ""
@title_name = ""
@gameover_name = ""
@battle_transition = ""
@title_bgm = RPG::AudioFile.new
@battle_bgm = RPG::AudioFile.new
@battle_end_me = RPG::AudioFile.new
@gameover_me = RPG::AudioFile.new
@cursor_se = RPG::AudioFile.new("", 80)
@decision_se = RPG::AudioFile.new("", 80)
@cancel_se = RPG::AudioFile.new("", 80)
@buzzer_se = RPG::AudioFile.new("", 80)
@equip_se = RPG::AudioFile.new("", 80)
@shop_se = RPG::AudioFile.new("", 80)
@save_se = RPG::AudioFile.new("", 80)
@load_se = RPG::AudioFile.new("", 80)
@battle_start_se = RPG::AudioFile.new("", 80)
@escape_se = RPG::AudioFile.new("", 80)
@actor_collapse_se = RPG::AudioFile.new("", 80)
@enemy_collapse_se = RPG::AudioFile.new("", 80)
@words = RPG::System::Words.new
@test_battlers = []
@test_troop_id = 1
@start_map_id = 1
@start_x = 0
@start_y = 0
@battleback_name = ""
@battler_name = ""
@battler_hue = 0
@edit_map_id = 1
end
end
end
module RPG
class Tileset
attr_accessor :id
attr_accessor :name
attr_accessor :tileset_name
attr_accessor :autotile_names
attr_accessor :panorama_name
attr_accessor :panorama_hue
attr_accessor :fog_name
attr_accessor :fog_hue
attr_accessor :fog_opacity
attr_accessor :fog_blend_type
attr_accessor :fog_zoom
attr_accessor :fog_sx
attr_accessor :fog_sy
attr_accessor :battleback_name
attr_accessor :passages
attr_accessor :priorities
attr_accessor :terrain_tags
def initialize
@id = 0
@name = ""
@tileset_name = ""
@autotile_names = [""]*7
@panorama_name = ""
@panorama_hue = 0
@fog_name = ""
@fog_hue = 0
@fog_opacity = 64
@fog_blend_type = 0
@fog_zoom = 200
@fog_sx = 0
@fog_sy = 0
@battleback_name = ""
@passages = Table.new(384)
@priorities = Table.new(384)
@priorities[0] = 5
@terrain_tags = Table.new(384)
end
end
end
module RPG
class CommonEvent
attr_accessor :id
attr_accessor :name
attr_accessor :trigger
attr_accessor :switch_id
attr_accessor :list
def initialize
@id = 0
@name = ""
@trigger = 0
@switch_id = 1
@list = [RPG::EventCommand.new]
end
end
end
module RPG
class Map
attr_accessor :tileset_id
attr_accessor :width
attr_accessor :height
attr_accessor :autoplay_bgm
attr_accessor :bgm
attr_accessor :autoplay_bgs
attr_accessor :bgs
attr_accessor :encounter_list
attr_accessor :encounter_step
attr_accessor :data
attr_accessor :events
def initialize(width, height)
@tileset_id = 1
@width = width
@height = height
@autoplay_bgm = false
@bgm = RPG::AudioFile.new
@autoplay_bgs = false
@bgs = RPG::AudioFile.new("", 80)
@encounter_list = []
@encounter_step = 30
@data = Table.new(width, height, 3)
@events = {}
end
end
end
module RPG
class MapInfo
attr_accessor :name
attr_accessor :parent_id
attr_accessor :order
attr_accessor :expanded
attr_accessor :scroll_x
attr_accessor :scroll_y
def initialize
@name = ""
@parent_id = 0
@order = 0
@expanded = false
@scroll_x = 0
@scroll_y = 0
end
end
end
module RPG
class Event
attr_accessor :id
attr_accessor :name
attr_accessor :x
attr_accessor :y
attr_accessor :pages
def initialize(x, y)
@id = 0
@name = ""
@x = x
@y = y
@pages = [RPG::Event::Page.new]
end
end
end
module RPG
class Event
class Page
attr_accessor :condition
attr_accessor :graphic
attr_accessor :move_type
attr_accessor :move_speed
attr_accessor :move_frequency
attr_accessor :move_route
attr_accessor :walk_anime
attr_accessor :step_anime
attr_accessor :direction_fix
attr_accessor :through
attr_accessor :always_on_top
attr_accessor :trigger
attr_accessor :list
def initialize
@condition = RPG::Event::Page::Condition.new
@graphic = RPG::Event::Page::Graphic.new
@move_type = 0
@move_speed = 3
@move_frequency = 3
@move_route = RPG::MoveRoute.new
@walk_anime = true
@step_anime = false
@direction_fix = false
@through = false
@always_on_top = false
@trigger = 0
@list = [RPG::EventCommand.new]
end
end
end
end
module RPG
class Event
class Page
class Condition
attr_accessor :switch1_valid
attr_accessor :switch2_valid
attr_accessor :variable_valid
attr_accessor :self_switch_valid
attr_accessor :switch1_id
attr_accessor :switch2_id
attr_accessor :variable_id
attr_accessor :variable_value
attr_accessor :self_switch_ch
def initialize
@switch1_valid = false
@switch2_valid = false
@variable_valid = false
@self_switch_valid = false
@switch1_id = 1
@switch2_id = 1
@variable_id = 1
@variable_value = 0
@self_switch_ch = "A"
end
end
end
end
end
module RPG
class Event
class Page
class Graphic
attr_accessor :tile_id
attr_accessor :character_name
attr_accessor :character_hue
attr_accessor :direction
attr_accessor :pattern
attr_accessor :opacity
attr_accessor :blend_type
def initialize
@tile_id = 0
@character_name = ""
@character_hue = 0
@direction = 2
@pattern = 0
@opacity = 255
@blend_type = 0
end
end
end
end
end
module RPG
class EventCommand
attr_accessor :code
attr_accessor :indent
attr_accessor :parameters
def initialize(code = 0, indent = 0, parameters = [])
@code = code
@indent = indent
@parameters = parameters
end
end
end
module RPG
class MoveRoute
attr_accessor :repeat
attr_accessor :skippable
attr_accessor :list
def initialize
@repeat = true
@skippable = false
@list = [RPG::MoveCommand.new]
end
end
end
module RPG
class MoveCommand
attr_accessor :code
attr_accessor :parameters
def initialize(code = 0, parameters = [])
@code = code
@parameters = parameters
end
end
end
module RPG
class System
class Words
attr_accessor :gold
attr_accessor :hp
attr_accessor :sp
attr_accessor :str
attr_accessor :dex
attr_accessor :agi
attr_accessor :int
attr_accessor :atk
attr_accessor :pdef
attr_accessor :mdef
attr_accessor :weapon
attr_accessor :armor1
attr_accessor :armor2
attr_accessor :armor3
attr_accessor :armor4
attr_accessor :attack
attr_accessor :skill
attr_accessor :guard
attr_accessor :item
attr_accessor :equip
def initialize
@gold = ""
@hp = ""
@sp = ""
@str = ""
@dex = ""
@agi = ""
@int = ""
@atk = ""
@pdef = ""
@mdef = ""
@weapon = ""
@armor1 = ""
@armor2 = ""
@armor3 = ""
@armor4 = ""
@attack = ""
@skill = ""
@guard = ""
@item = ""
@equip = ""
end
end
end
end
module RPG
class System
class TestBattler
attr_accessor :actor_id
attr_accessor :level
attr_accessor :weapon_id
attr_accessor :armor1_id
attr_accessor :armor2_id
attr_accessor :armor3_id
attr_accessor :armor4_id
def initialize
@actor_id = 1
@level = 1
@weapon_id = 0
@armor1_id = 0
@armor2_id = 0
@armor3_id = 0
@armor4_id = 0
end
end
end
end
module RPG
class AudioFile
attr_accessor :name
attr_accessor :volume
attr_accessor :pitch
def initialize(name = "", volume = 100, pitch = 100)
@name = name
@volume = volume
@pitch = pitch
end
# def play
# end
end
end

View File

@@ -0,0 +1,534 @@
class SpriteAnimation
@@_animations = []
@@_reference_count = {}
def initialize(sprite)
@sprite = sprite
end
%w[
x y ox oy viewport flash src_rect opacity tone
].each_with_index do |s, i|
eval <<-__END__
def #{s}(*arg)
@sprite.#{s}(*arg)
end
__END__
end
def self.clear
@@_animations.clear
end
def dispose
dispose_animation
dispose_loop_animation
end
def animation(animation, hit, height = 3)
dispose_animation
@_animation = animation
return if @_animation == nil
@_animation_hit = hit
@_animation_height = height
@_animation_duration = @_animation.frame_max
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_animation_frame_skip = Graphics.frame_rate / fr
animation_name = @_animation.animation_name
animation_hue = @_animation.animation_hue
bitmap = pbGetAnimation(animation_name, animation_hue)
if @@_reference_count.include?(bitmap)
@@_reference_count[bitmap] += 1
else
@@_reference_count[bitmap] = 1
end
@_animation_sprites = []
if @_animation.position != 3 or not @@_animations.include?(animation)
for i in 0..15
sprite = ::Sprite.new(self.viewport)
sprite.bitmap = bitmap
sprite.visible = false
@_animation_sprites.push(sprite)
end
unless @@_animations.include?(animation)
@@_animations.push(animation)
end
end
update_animation
end
def loop_animation(animation)
return if animation == @_loop_animation
dispose_loop_animation
@_loop_animation = animation
return if @_loop_animation == nil
@_loop_animation_index = 0
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_loop_animation_frame_skip = Graphics.frame_rate / fr
animation_name = @_loop_animation.animation_name
animation_hue = @_loop_animation.animation_hue
bitmap = pbGetAnimation(animation_name, animation_hue)
if @@_reference_count.include?(bitmap)
@@_reference_count[bitmap] += 1
else
@@_reference_count[bitmap] = 1
end
@_loop_animation_sprites = []
for i in 0..15
sprite = ::Sprite.new(self.viewport)
sprite.bitmap = bitmap
sprite.visible = false
@_loop_animation_sprites.push(sprite)
end
update_loop_animation
end
def dispose_animation
return if @_animation_sprites == nil
sprite = @_animation_sprites[0]
if sprite != nil
@@_reference_count[sprite.bitmap] -= 1
if @@_reference_count[sprite.bitmap] == 0
sprite.bitmap.dispose
end
end
for sprite in @_animation_sprites
sprite.dispose
end
@_animation_sprites = nil
@_animation = nil
end
def dispose_loop_animation
return if @_loop_animation_sprites == nil
sprite = @_loop_animation_sprites[0]
if sprite != nil
@@_reference_count[sprite.bitmap] -= 1
if @@_reference_count[sprite.bitmap] == 0
sprite.bitmap.dispose
end
end
for sprite in @_loop_animation_sprites
sprite.dispose
end
@_loop_animation_sprites = nil
@_loop_animation = nil
end
def active?
return @_loop_animation_sprites != nil || @_animation_sprites != nil
end
def effect?
return @_animation_duration > 0
end
def update
if @_animation != nil
quick_update = true
if Graphics.frame_count % @_animation_frame_skip == 0
@_animation_duration -= 1
quick_update = false
end
update_animation(quick_update)
end
if @_loop_animation != nil
quick_update = (Graphics.frame_count % @_loop_animation_frame_skip != 0)
update_loop_animation(quick_update)
if !quick_update
@_loop_animation_index += 1
@_loop_animation_index %= @_loop_animation.frame_max
end
end
end
def update_animation(quick_update = false)
if @_animation_duration <= 0
dispose_animation
return
end
frame_index = @_animation.frame_max - @_animation_duration
cell_data = @_animation.frames[frame_index].cell_data
position = @_animation.position
animation_set_sprites(@_animation_sprites, cell_data, position, quick_update)
return if quick_update
for timing in @_animation.timings
next if timing.frame != frame_index
animation_process_timing(timing, @_animation_hit)
end
end
def update_loop_animation(quick_update = false)
frame_index = @_loop_animation_index
cell_data = @_loop_animation.frames[frame_index].cell_data
position = @_loop_animation.position
animation_set_sprites(@_loop_animation_sprites, cell_data, position, quick_update)
return if quick_update
for timing in @_loop_animation.timings
next if timing.frame != frame_index
animation_process_timing(timing, true)
end
end
def animation_set_sprites(sprites, cell_data, position, quick_update = false)
sprite_x = 320
sprite_y = 240
if position == 3
if self.viewport != nil
sprite_x = self.viewport.rect.width / 2
sprite_y = self.viewport.rect.height - 160
end
else
sprite_x = self.x - self.ox + self.src_rect.width / 2
sprite_y = self.y - self.oy + self.src_rect.height / 2
sprite_y -= self.src_rect.height / 4 if position == 0
sprite_y += self.src_rect.height / 4 if position == 2
end
for i in 0..15
sprite = sprites[i]
pattern = cell_data[i, 0]
if sprite == nil or pattern == nil or pattern == -1
sprite.visible = false if sprite != nil
next
end
sprite.x = sprite_x + cell_data[i, 1]
sprite.y = sprite_y + cell_data[i, 2]
next if quick_update
sprite.visible = true
sprite.src_rect.set(pattern % 5 * 192, pattern / 5 * 192, 192, 192)
case @_animation_height
when 0; sprite.z = 1
when 1; sprite.z = sprite.y+32+15
when 2; sprite.z = sprite.y+32+32+17
else; sprite.z = 2000
end
sprite.ox = 96
sprite.oy = 96
sprite.zoom_x = cell_data[i, 3] / 100.0
sprite.zoom_y = cell_data[i, 3] / 100.0
sprite.angle = cell_data[i, 4]
sprite.mirror = (cell_data[i, 5] == 1)
sprite.tone = self.tone
sprite.opacity = cell_data[i, 6] * self.opacity / 255.0
sprite.blend_type = cell_data[i, 7]
end
end
def animation_process_timing(timing, hit)
if (timing.condition == 0) or
(timing.condition == 1 and hit == true) or
(timing.condition == 2 and hit == false)
if timing.se.name != ""
se = timing.se
pbSEPlay(se)
end
case timing.flash_scope
when 1
self.flash(timing.flash_color, timing.flash_duration * 2)
when 2
if self.viewport != nil
self.viewport.flash(timing.flash_color, timing.flash_duration * 2)
end
when 3
self.flash(nil, timing.flash_duration * 2)
end
end
end
def x=(x)
sx = x - self.x
return if sx == 0
if @_animation_sprites != nil
for i in 0..15
@_animation_sprites[i].x += sx
end
end
if @_loop_animation_sprites != nil
for i in 0..15
@_loop_animation_sprites[i].x += sx
end
end
end
def y=(y)
sy = y - self.y
return if sy == 0
if @_animation_sprites != nil
for i in 0..15
@_animation_sprites[i].y += sy
end
end
if @_loop_animation_sprites != nil
for i in 0..15
@_loop_animation_sprites[i].y += sy
end
end
end
end
module RPG
class Sprite < ::Sprite
def initialize(viewport = nil)
super(viewport)
@_whiten_duration = 0
@_appear_duration = 0
@_escape_duration = 0
@_collapse_duration = 0
@_damage_duration = 0
@_animation_duration = 0
@_blink = false
@animations = []
@loopAnimations = []
end
def dispose
dispose_damage
dispose_animation
dispose_loop_animation
super
end
def whiten
self.blend_type = 0
self.color.set(255, 255, 255, 128)
self.opacity = 255
@_whiten_duration = 16
@_appear_duration = 0
@_escape_duration = 0
@_collapse_duration = 0
end
def appear
self.blend_type = 0
self.color.set(0, 0, 0, 0)
self.opacity = 0
@_appear_duration = 16
@_whiten_duration = 0
@_escape_duration = 0
@_collapse_duration = 0
end
def escape
self.blend_type = 0
self.color.set(0, 0, 0, 0)
self.opacity = 255
@_escape_duration = 32
@_whiten_duration = 0
@_appear_duration = 0
@_collapse_duration = 0
end
def collapse
self.blend_type = 1
self.color.set(255, 64, 64, 255)
self.opacity = 255
@_collapse_duration = 48
@_whiten_duration = 0
@_appear_duration = 0
@_escape_duration = 0
end
def damage(value, critical)
dispose_damage
damage_string = (value.is_a?(Numeric)) ? value.abs.to_s : value.to_s
bitmap = Bitmap.new(160, 48)
bitmap.font.name = "Arial Black"
bitmap.font.size = 32
bitmap.font.color.set(0, 0, 0)
bitmap.draw_text(-1, 12-1, 160, 36, damage_string, 1)
bitmap.draw_text(+1, 12-1, 160, 36, damage_string, 1)
bitmap.draw_text(-1, 12+1, 160, 36, damage_string, 1)
bitmap.draw_text(+1, 12+1, 160, 36, damage_string, 1)
if value.is_a?(Numeric) and value < 0
bitmap.font.color.set(176, 255, 144)
else
bitmap.font.color.set(255, 255, 255)
end
bitmap.draw_text(0, 12, 160, 36, damage_string, 1)
if critical
bitmap.font.size = 20
bitmap.font.color.set(0, 0, 0)
bitmap.draw_text(-1, -1, 160, 20, "CRITICAL", 1)
bitmap.draw_text(+1, -1, 160, 20, "CRITICAL", 1)
bitmap.draw_text(-1, +1, 160, 20, "CRITICAL", 1)
bitmap.draw_text(+1, +1, 160, 20, "CRITICAL", 1)
bitmap.font.color.set(255, 255, 255)
bitmap.draw_text(0, 0, 160, 20, "CRITICAL", 1)
end
@_damage_sprite = ::Sprite.new(self.viewport)
@_damage_sprite.bitmap = bitmap
@_damage_sprite.ox = 80
@_damage_sprite.oy = 20
@_damage_sprite.x = self.x
@_damage_sprite.y = self.y - self.oy / 2
@_damage_sprite.z = 3000
@_damage_duration = 40
end
def pushAnimation(array, anim)
for i in 0...array.length
next if array[i] && array[i].active?
array[i] = anim
return
end
array.push(anim)
end
def animation(animation, hit, height = 3)
anim = SpriteAnimation.new(self)
anim.animation(animation,hit,height)
pushAnimation(@animations,anim)
end
def loop_animation(animation)
anim = SpriteAnimation.new(self)
anim.loop_animation(animation)
pushAnimation(@loopAnimations,anim)
end
def dispose_damage
return if @_damage_sprite == nil
@_damage_sprite.bitmap.dispose
@_damage_sprite.dispose
@_damage_sprite = nil
@_damage_duration = 0
end
def dispose_animation
for a in @animations
a.dispose_animation if a
end
@animations.clear
end
def dispose_loop_animation
for a in @loopAnimations
a.dispose_loop_animation if a
end
@loopAnimations.clear
end
def blink_on
return if @_blink
@_blink = true
@_blink_count = 0
end
def blink_off
return unless @_blink
@_blink = false
self.color.set(0, 0, 0, 0)
end
def blink?
return @_blink
end
def effect?
return true if @_whiten_duration > 0
return true if @_appear_duration > 0
return true if @_escape_duration > 0
return true if @_collapse_duration > 0
return true if @_damage_duration > 0
for a in @animations
return true if a.effect?
end
return false
end
def update
super
if @_whiten_duration > 0
@_whiten_duration -= 1
self.color.alpha = 128 - (16 - @_whiten_duration) * 10
end
if @_appear_duration > 0
@_appear_duration -= 1
self.opacity = (16 - @_appear_duration) * 16
end
if @_escape_duration > 0
@_escape_duration -= 1
self.opacity = 256 - (32 - @_escape_duration) * 10
end
if @_collapse_duration > 0
@_collapse_duration -= 1
self.opacity = 256 - (48 - @_collapse_duration) * 6
end
if @_damage_duration > 0
@_damage_duration -= 1
case @_damage_duration
when 38..39
@_damage_sprite.y -= 4
when 36..37
@_damage_sprite.y -= 2
when 34..35
@_damage_sprite.y += 2
when 28..33
@_damage_sprite.y += 4
end
@_damage_sprite.opacity = 256 - (12 - @_damage_duration) * 32
if @_damage_duration == 0
dispose_damage
end
end
for a in @animations
a.update
end
for a in @loopAnimations
a.update
end
if @_blink
@_blink_count = (@_blink_count + 1) % 32
if @_blink_count < 16
alpha = (16 - @_blink_count) * 6
else
alpha = (@_blink_count - 16) * 6
end
self.color.set(255, 255, 255, alpha)
end
SpriteAnimation.clear
end
def update_animation
for a in @animations
a.update_animation if a && a.active?
end
end
def update_loop_animation
for a in @loopAnimations
a.update_loop_animation if a && a.active?
end
end
def x=(x)
for a in @animations
a.x = x if a
end
for a in @loopAnimations
a.x = x if a
end
super
end
def y=(y)
for a in @animations
a.y = y if a
end
for a in @loopAnimations
a.y = y if a
end
super
end
end
end

View File

@@ -0,0 +1,104 @@
class Win32API
@@RGSSWINDOW = nil
@@GetCurrentThreadId = Win32API.new('kernel32','GetCurrentThreadId', '%w()','l')
@@GetWindowThreadProcessId = Win32API.new('user32','GetWindowThreadProcessId', '%w(l p)','l')
@@FindWindowEx = Win32API.new('user32','FindWindowEx', '%w(l l p p)','l')
def Win32API.SetWindowText(text)
hWnd = pbFindRgssWindow
swp = Win32API.new('user32','SetWindowTextA',%(l, p),'i')
swp.call(hWnd, text.to_s)
end
# Added by Peter O. as a more reliable way to get the RGSS window
def Win32API.pbFindRgssWindow
return @@RGSSWINDOW if @@RGSSWINDOW
processid = [0].pack('l')
threadid = @@GetCurrentThreadId.call
nextwindow = 0
begin
nextwindow = @@FindWindowEx.call(0,nextwindow,"RGSS Player",0)
if nextwindow!=0
wndthreadid = @@GetWindowThreadProcessId.call(nextwindow,processid)
if wndthreadid==threadid
@@RGSSWINDOW = nextwindow
return @@RGSSWINDOW
end
end
end until nextwindow==0
raise "Can't find RGSS player window"
return 0
end
def Win32API.SetWindowPos(w, h)
hWnd = pbFindRgssWindow
windowrect = Win32API.GetWindowRect
clientsize = Win32API.client_size
xExtra = windowrect.width-clientsize[0]
yExtra = windowrect.height-clientsize[1]
swp = Win32API.new('user32','SetWindowPos',%(l,l,i,i,i,i,i),'i')
win = swp.call(hWnd,0,windowrect.x,windowrect.y,w+xExtra,h+yExtra,0)
return win
end
def Win32API.client_size
hWnd = pbFindRgssWindow
rect = [0,0,0,0].pack('l4')
Win32API.new('user32','GetClientRect',%w(l p),'i').call(hWnd,rect)
width,height = rect.unpack('l4')[2..3]
return width,height
end
def Win32API.GetWindowRect
hWnd = pbFindRgssWindow
rect = [0,0,0,0].pack('l4')
Win32API.new('user32','GetWindowRect',%w(l p),'i').call(hWnd,rect)
x,y,width,height = rect.unpack('l4')
return Rect.new(x,y,width-x,height-y)
end
def Win32API.focusWindow
window = Win32API.new('user32','ShowWindow','LL','L')
hWnd = pbFindRgssWindow
window.call(hWnd,9)
end
def Win32API.fillScreen
setWindowLong = Win32API.new('user32','SetWindowLong','LLL','L')
setWindowPos = Win32API.new('user32','SetWindowPos','LLIIIII','I')
metrics = Win32API.new('user32', 'GetSystemMetrics', 'I', 'I')
hWnd = pbFindRgssWindow
width = metrics.call(0)
height = metrics.call(1)
setWindowLong.call(hWnd,-16,0x00000000)
setWindowPos.call(hWnd,0,0,0,width,height,0)
Win32API.focusWindow
return [width,height]
end
def Win32API.restoreScreen
setWindowLong = Win32API.new('user32','SetWindowLong','LLL','L')
setWindowPos = Win32API.new('user32','SetWindowPos','LLIIIII','I')
metrics = Win32API.new('user32','GetSystemMetrics','I','I')
hWnd = pbFindRgssWindow
width = SCREEN_WIDTH*$ResizeFactor
height = SCREEN_HEIGHT*$ResizeFactor
if $PokemonSystem && $PokemonSystem.border==1
width += BORDER_WIDTH*2*$ResizeFactor
height += BORDER_HEIGHT*2*$ResizeFactor
end
x = [(metrics.call(0)-width)/2,0].max
y = [(metrics.call(1)-height)/2,0].max
setWindowLong.call(hWnd,-16,0x14CA0000)
setWindowPos.call(hWnd,0,x,y,width+6,height+29,0)
Win32API.focusWindow
return [width,height]
end
end
# Well done for finding this place.
# DO NOT EDIT THESE
ESSENTIALS_VERSION = "18"
ERROR_TEXT = ""

View File

@@ -0,0 +1,696 @@
module Win32
def copymem(len)
buf = "\0" * len
Win32API.new("kernel32", "RtlMoveMemory", "ppl", "").call(buf, self, len)
buf
end
end
# Extends the numeric class.
class Numeric
include Win32
end
# Extends the string class.
class String
include Win32
end
module Winsock
DLL = "ws2_32"
#-----------------------------------------------------------------------------
# * Accept Connection
#-----------------------------------------------------------------------------
def self.accept(*args)
Win32API.new(DLL, "accept", "ppl", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Bind
#-----------------------------------------------------------------------------
def self.bind(*args)
Win32API.new(DLL, "bind", "ppl", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Close Socket
#-----------------------------------------------------------------------------
def self.closesocket(*args)
Win32API.new(DLL, "closesocket", "p", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Connect
#-----------------------------------------------------------------------------
def self.connect(*args)
Win32API.new(DLL, "connect", "ppl", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Get host (Using Adress)
#-----------------------------------------------------------------------------
def self.gethostbyaddr(*args)
Win32API.new(DLL, "gethostbyaddr", "pll", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Get host (Using Name)
#-----------------------------------------------------------------------------
def self.gethostbyname(*args)
Win32API.new(DLL, "gethostbyname", "p", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Get host's Name
#-----------------------------------------------------------------------------
def self.gethostname(*args)
Win32API.new(DLL, "gethostname", "pl", "").call(*args)
end
#-----------------------------------------------------------------------------
# * Get Server (Using Name)
#-----------------------------------------------------------------------------
def self.getservbyname(*args)
Win32API.new(DLL, "getservbyname", "pp", "p").call(*args)
end
#-----------------------------------------------------------------------------
# * Convert Host Long To Network Long
#-----------------------------------------------------------------------------
def self.htonl(*args)
Win32API.new(DLL, "htonl", "l", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Convert Host Short To Network Short
#-----------------------------------------------------------------------------
def self.htons(*args)
Win32API.new(DLL, "htons", "l", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Inet Adress
#-----------------------------------------------------------------------------
def self.inet_addr(*args)
Win32API.new(DLL, "inet_addr", "p", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Inet N To A
#-----------------------------------------------------------------------------
def self.inet_ntoa(*args)
Win32API.new(DLL, "inet_ntoa", "l", "p").call(*args)
end
#-----------------------------------------------------------------------------
# * Listen
#-----------------------------------------------------------------------------
def self.listen(*args)
Win32API.new(DLL, "listen", "pl", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Recieve
#-----------------------------------------------------------------------------
def self.recv(*args)
Win32API.new(DLL, "recv", "ppll", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Select
#-----------------------------------------------------------------------------
def self.select(*args)
Win32API.new(DLL, "select", "lpppp", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Send
#-----------------------------------------------------------------------------
def self.send(*args)
Win32API.new(DLL, "send", "ppll", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Set Socket Options
#-----------------------------------------------------------------------------
def self.setsockopt(*args)
Win32API.new(DLL, "setsockopt", "pllpl", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Shutdown
#-----------------------------------------------------------------------------
def self.shutdown(*args)
Win32API.new(DLL, "shutdown", "pl", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Socket
#-----------------------------------------------------------------------------
def self.socket(*args)
Win32API.new(DLL, "socket", "lll", "l").call(*args)
end
#-----------------------------------------------------------------------------
# * Get Last Error
#-----------------------------------------------------------------------------
def self.WSAGetLastError(*args)
Win32API.new(DLL, "WSAGetLastError", "", "l").call(*args)
end
end
if !Object.const_defined?(:Socket) # for compatibility
#===============================================================================
# ** Socket - Creates and manages sockets.
#-------------------------------------------------------------------------------
# Author Ruby
# Version 1.8.1
#===============================================================================
class Socket
#-----------------------------------------------------------------------------
# * Constants
#-----------------------------------------------------------------------------
AF_UNSPEC = 0
AF_UNIX = 1
AF_INET = 2
AF_IPX = 6
AF_APPLETALK = 16
PF_UNSPEC = 0
PF_UNIX = 1
PF_INET = 2
PF_IPX = 6
PF_APPLETALK = 16
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
SOCK_RDM = 4
SOCK_SEQPACKET = 5
IPPROTO_IP = 0
IPPROTO_ICMP = 1
IPPROTO_IGMP = 2
IPPROTO_GGP = 3
IPPROTO_TCP = 6
IPPROTO_PUP = 12
IPPROTO_UDP = 17
IPPROTO_IDP = 22
IPPROTO_ND = 77
IPPROTO_RAW = 255
IPPROTO_MAX = 256
SOL_SOCKET = 65535
SO_DEBUG = 1
SO_REUSEADDR = 4
SO_KEEPALIVE = 8
SO_DONTROUTE = 16
SO_BROADCAST = 32
SO_LINGER = 128
SO_OOBINLINE = 256
SO_RCVLOWAT = 4100
SO_SNDTIMEO = 4101
SO_RCVTIMEO = 4102
SO_ERROR = 4103
SO_TYPE = 4104
SO_SNDBUF = 4097
SO_RCVBUF = 4098
SO_SNDLOWAT = 4099
TCP_NODELAY = 1
MSG_OOB = 1
MSG_PEEK = 2
MSG_DONTROUTE = 4
IP_OPTIONS = 1
IP_DEFAULT_MULTICAST_LOOP = 1
IP_DEFAULT_MULTICAST_TTL = 1
IP_MULTICAST_IF = 2
IP_MULTICAST_TTL = 3
IP_MULTICAST_LOOP = 4
IP_ADD_MEMBERSHIP = 5
IP_DROP_MEMBERSHIP = 6
IP_TTL = 7
IP_TOS = 8
IP_MAX_MEMBERSHIPS = 20
EAI_ADDRFAMILY = 1
EAI_AGAIN = 2
EAI_BADFLAGS = 3
EAI_FAIL = 4
EAI_FAMILY = 5
EAI_MEMORY = 6
EAI_NODATA = 7
EAI_NONAME = 8
EAI_SERVICE = 9
EAI_SOCKTYPE = 10
EAI_SYSTEM = 11
EAI_BADHINTS = 12
EAI_PROTOCOL = 13
EAI_MAX = 14
AI_PASSIVE = 1
AI_CANONNAME = 2
AI_NUMERICHOST = 4
AI_MASK = 7
AI_ALL = 256
AI_V4MAPPED_CFG = 512
AI_ADDRCONFIG = 1024
AI_DEFAULT = 1536
AI_V4MAPPED = 2048
#--------------------------------------------------------------------------
# * Returns the associated IP address for the given hostname.
#--------------------------------------------------------------------------
def self.getaddress(host)
gethostbyname(host)[3].unpack("C4").join(".")
end
#--------------------------------------------------------------------------
# * Returns the associated IP address for the given hostname.
#--------------------------------------------------------------------------
def self.getservice(serv)
case serv
when Numeric
return serv
when String
return getservbyname(serv)
else
raise "Please use an integer or string for services."
end
end
#--------------------------------------------------------------------------
# * Returns information about the given hostname.
#--------------------------------------------------------------------------
def self.gethostbyname(name)
raise SocketError::ENOASSOCHOST if (ptr = Winsock.gethostbyname(name)) == 0
host = ptr.copymem(16).unpack("iissi")
[host[0].copymem(64).split("\0")[0], [], host[2], host[4].copymem(4).unpack("l")[0].copymem(4)]
end
#--------------------------------------------------------------------------
# * Returns the user's hostname.
#--------------------------------------------------------------------------
def self.gethostname
buf = "\0" * 256
Winsock.gethostname(buf, 256)
buf.strip
end
#--------------------------------------------------------------------------
# * Returns information about the given service.
#--------------------------------------------------------------------------
def self.getservbyname(name)
case name
when /echo/i
return 7
when /daytime/i
return 13
when /ftp/i
return 21
when /telnet/i
return 23
when /smtp/i
return 25
when /time/i
return 37
when /http/i
return 80
when /pop/i
return 110
else
#Network.testing? != 0 ? (Network.testresult(true)) : (raise "Service not recognized.")
#return if Network.testing? == 2
end
end
#--------------------------------------------------------------------------
# * Creates an INET-sockaddr struct.
#--------------------------------------------------------------------------
def self.sockaddr_in(port, host)
begin
[AF_INET, getservice(port)].pack("sn") + gethostbyname(host)[3] + [].pack("x8")
rescue
#Network.testing? != 0 ? (Network.testresult(true)): (nil)
#return if Network.testing? == 2
rescue Hangup
#Network.testing? != 0 ? (Network.testresult(true)): (nil)
#return if Network.testing? == 2
end
end
#--------------------------------------------------------------------------
# * Creates a new socket and connects it to the given host and port.
#--------------------------------------------------------------------------
def self.open(*args)
socket = new(*args)
if block_given?
begin
yield socket
ensure
socket.close
end
end
nil
end
#--------------------------------------------------------------------------
# * Creates a new socket.
#--------------------------------------------------------------------------
def initialize(domain, type, protocol)
SocketError.check if (@fd = Winsock.socket(domain, type, protocol)) == -1
@fd
end
#--------------------------------------------------------------------------
# * Accepts incoming connections.
#--------------------------------------------------------------------------
def accept(flags = 0)
buf = "\0" * 16
SocketError.check if Winsock.accept(@fd, buf, flags) == -1
buf
end
#--------------------------------------------------------------------------
# * Binds a socket to the given sockaddr.
#--------------------------------------------------------------------------
def bind(sockaddr)
SocketError.check if (ret = Winsock.bind(@fd, sockaddr, sockaddr.size)) == -1
ret
end
#--------------------------------------------------------------------------
# * Closes a socket.
#--------------------------------------------------------------------------
def close
SocketError.check if (ret = Winsock.closesocket(@fd)) == -1
ret
end
#--------------------------------------------------------------------------
# * Connects a socket to the given sockaddr.
#--------------------------------------------------------------------------
def connect(sockaddr)
#return if Network.testing? == 2
SocketError.check if (ret = Winsock.connect(@fd, sockaddr, sockaddr.size)) == -1
ret
end
#--------------------------------------------------------------------------
# * Listens for incoming connections.
#--------------------------------------------------------------------------
def listen(backlog)
SocketError.check if (ret = Winsock.listen(@fd, backlog)) == -1
ret
end
#--------------------------------------------------------------------------
# * Checks waiting data's status.
#--------------------------------------------------------------------------
def select(timeout) # timeout in seconds
SocketError.check if (ret = Winsock.select(1, [1, @fd].pack("ll"), 0, 0, [timeout.to_i,
(timeout * 1000000).to_i].pack("ll"))) == -1
ret
end
#--------------------------------------------------------------------------
# * Checks if data is waiting.
#--------------------------------------------------------------------------
def ready?
not select(0) == 0
end
#--------------------------------------------------------------------------
# * Reads data from socket.
#--------------------------------------------------------------------------
def read(len)
buf = "\0" * len
Win32API.new("msvcrt", "_read", "lpl", "l").call(@fd, buf, len)
buf
end
#--------------------------------------------------------------------------
# * Returns received data.
#--------------------------------------------------------------------------
def recv(len, flags = 0)
retString=""
remainLen=len
while remainLen > 0
buf = "\0" * remainLen
retval=Winsock.recv(@fd, buf, buf.size, flags)
SocketError.check if retval == -1
# Note: Return value may not equal requested length
remainLen-=retval
retString+=buf[0,retval]
end
return retString
end
#--------------------------------------------------------------------------
# * Sends data to a host.
#--------------------------------------------------------------------------
def send(data, flags = 0)
SocketError.check if (ret = Winsock.send(@fd, data, data.size, flags)) == -1
ret
end
#--------------------------------------------------------------------------
# * Recieves file from a socket
# size : file size
# scene : update scene boolean
#--------------------------------------------------------------------------
def recv_file(size,scene=false,file="")
data = []
size.times do |i|
if scene == true
$scene.recv_update(size,i,file) if i%((size/1000)+1)== 0
else
Graphics.update if i%1024 == 0
end
data << recv(1)
end
return data
end
def recvTimeout
if select(10)==0
raise Hangup.new("Timeout")
end
return recv(1)
end
#--------------------------------------------------------------------------
# * Gets
#--------------------------------------------------------------------------
def gets
# Create buffer
message = ""
# Loop Until "end of line"
count=0
while true
x=select(0.05)
if x==0
count+=1
Graphics.update if count%10==0
raise Errno::ETIMEOUT if count>200
next
end
ch = recv(1)
break if ch == "\n"
message += ch
end
# Return recieved data
return message
end
#--------------------------------------------------------------------------
# * Writes data to socket.
#--------------------------------------------------------------------------
def write(data)
Win32API.new("msvcrt", "_write", "lpl", "l").call(@fd, data, 1)
end
end
#===============================================================================
# ** TCPSocket - Creates and manages TCP sockets.
#-------------------------------------------------------------------------------
# Author Ruby
# Version 1.8.1
#===============================================================================
#-------------------------------------------------------------------------------
# Begin SDK Enabled Check
#-------------------------------------------------------------------------------
class TCPSocket < Socket
#--------------------------------------------------------------------------
# * Creates a new socket and connects it to the given host and port.
#--------------------------------------------------------------------------
def self.open(*args)
socket = new(*args)
if block_given?
begin
yield socket
ensure
socket.close
end
end
nil
end
#--------------------------------------------------------------------------
# * Creates a new socket and connects it to the given host and port.
#--------------------------------------------------------------------------
def initialize(host, port)
super(AF_INET, SOCK_STREAM, IPPROTO_TCP)
connect(Socket.sockaddr_in(port, host))
end
end
#==============================================================================
# ** SocketError
#------------------------------------------------------------------------------
# Default exception class for sockets.
#==============================================================================
class SocketError < StandardError
ENOASSOCHOST = "getaddrinfo: no address associated with hostname."
def self.check
errno = Winsock.WSAGetLastError
#if not Network.testing? == 1
raise Errno.const_get(Errno.constants.detect { |c| Errno.const_get(c).new.errno == errno })
#else
# errno != 0 ? (Network.testresult(true)) : (Network.testresult(false))
#end
end
end
end # !Object.const_defined?(:Socket)
#############################
#
# HTTP utility functions
#
#############################
def pbPostData(url, postdata, filename=nil, depth=0)
if url[/^http:\/\/([^\/]+)(.*)$/]
host = $1
path = $2
path = "/" if path.length==0
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
body = postdata.map { |key, value|
keyString = key.to_s
valueString = value.to_s
keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
valueString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
next "#{keyString}=#{valueString}"
}.join('&')
request = "POST #{path} HTTP/1.1\r\n"
request += "Host: #{host}\r\n"
request += "Proxy-Connection: Close\r\n"
request += "Content-Length: #{body.length}\r\n"
request += "Pragma: no-cache\r\n"
request += "User-Agent: #{userAgent}\r\n"
request += "Content-Type: application/x-www-form-urlencoded\r\n"
request += "\r\n"
request += body
return pbHttpRequest(host, request, filename, depth)
end
return ""
end
def pbDownloadData(url, filename=nil, depth=0)
raise "Redirection level too deep" if depth>10
if url[/^http:\/\/([^\/]+)(.*)$/]
host = $1
path = $2
path = "/" if path.length==0
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
request = "GET #{path} HTTP/1.1\r\n"
request += "User-Agent: #{userAgent}\r\n"
request += "Pragma: no-cache\r\n"
request += "Host: #{host}\r\n"
request += "Proxy-Connection: Close\r\n"
request += "\r\n"
return pbHttpRequest(host, request, filename, depth)
end
return ""
end
def pbHttpRequest(host, request, filename=nil, depth=0)
raise "Redirection level too deep" if depth>10
socket = ::TCPSocket.new(host, 80)
time = Time.now.to_i
begin
socket.send(request)
result = socket.gets
data = ""
# Get the HTTP result
if result[/^HTTP\/1\.[01] (\d+).*/]
errorcode = $1.to_i
raise "HTTP Error #{errorcode}" if errorcode>=400 && errorcode<500
headers = {}
# Get the response headers
while true
result = socket.gets.sub(/\r$/,"")
break if result==""
if result[/^([^:]+):\s*(.*)/]
headers[$1] = $2
end
end
length = -1
chunked = false
if headers["Content-Length"]
length = headers["Content-Length"].to_i
end
if headers["Transfer-Encoding"]=="chunked"
chunked = true
end
if headers["Location"] && errorcode>=300 && errorcode<400
socket.close rescue socket = nil
return pbDownloadData(headers["Location"],filename,depth+1)
end
if chunked
# Chunked content
while true
lengthline = socket.gets.sub(/\r$/,"")
length = lengthline.to_i(16)
break if length==0
while Time.now.to_i-time>=5 || socket.select(10)==0
time = Time.now.to_i
Graphics.update
end
data += socket.recv(length)
socket.gets
end
elsif length==-1
# No content length specified
while true
break if socket.select(500)==0
while Time.now.to_i-time>=5 || socket.select(10)==0
time = Time.now.to_i
Graphics.update
end
data += socket.recv(1)
end
else
# Content length specified
while length>0
chunk = [length,4096].min
while Time.now.to_i-time>=5 || socket.select(10)==0
time = Time.now.to_i
Graphics.update
end
data += socket.recv(chunk)
length -= chunk
end
end
end
return data if !filename
File.open(filename,"wb") { |f| f.write(data) }
ensure
socket.close rescue socket = nil
end
return ""
end
def pbDownloadToString(url)
begin
data = pbDownloadData(url)
return data
rescue
return ""
end
end
def pbDownloadToFile(url, file)
begin
pbDownloadData(url,file)
rescue
end
end
def pbPostToString(url, postdata)
begin
data = pbPostData(url, postdata)
return data
rescue
return ""
end
end
def pbPostToFile(url, postdata, file)
begin
pbPostData(url, postdata,file)
rescue
end
end

View File

@@ -0,0 +1,158 @@
module Console
attr_reader :bufferHandle
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
CONSOLE_TEXTMODE_BUFFER = 0x00000001
def Console::AllocConsole
return @apiAllocConsole.call
end
def Console::CreateConsoleScreenBuffer(dwDesiredAccess,dwShareMode,dwFlags)
return @apiCreateConsoleScreenBuffer.call(dwDesiredAccess,dwShareMode,nil,dwFlags,nil)
end
def Console::WriteConsole(lpBuffer)
hFile = @bufferHandle
return if !hFile
return @apiWriteConsole.call(hFile,lpBuffer,lpBuffer.size,0,0)
end
def Console::ReadConsole(lpBuffer)
hFile = @bufferHandle
return @apiReadConsole.call(hFile,lpBuffer,lpBuffer.size,0,0)
end
def Console::SetConsoleActiveScreenBuffer(hScreenBuffer)
return @apiSetConsoleActiveScreenBuffer.call(hScreenBuffer)
end
def Console::SetConsoleScreenBufferSize(hScreenBuffer,x,y)
return @apiSetConsoleScreenBufferSize.call(hScreenBuffer,[x,y].pack("vv"))
end
def Console::SetConsoleTitle(title)
return @apiSetConsoleTitle.call(title)
end
def self.setup_console
return unless $DEBUG
@apiAllocConsole = Win32API.new("kernel32","AllocConsole","","l")
@apiCreateConsoleScreenBuffer = Win32API.new("kernel32","CreateConsoleScreenBuffer","nnpnp","l")
@apiSetConsoleActiveScreenBuffer = Win32API.new("kernel32","SetConsoleActiveScreenBuffer","l","s")
@apiWriteConsole = Win32API.new("kernel32","WriteConsole","lpnnn","S")
@apiReadConsole = Win32API.new("kernel32","ReadConsole","lpnnn","S")
@apiSetConsoleScreenBufferSize = Win32API.new("kernel32","SetConsoleScreenBufferSize","lp","S")
@apiSetConsoleTitle = Win32API.new("kernel32","SetConsoleTitle","p","s")
access = (GENERIC_READ | GENERIC_WRITE)
sharemode = (FILE_SHARE_READ | FILE_SHARE_WRITE)
returnCode = AllocConsole()
@bufferHandle = CreateConsoleScreenBuffer(access,sharemode,CONSOLE_TEXTMODE_BUFFER)
f = File.open("Game.ini")
lines = f.readlines()
s = lines[3]
len = s.size
title = (s[6,len - 7])
SetConsoleScreenBufferSize(@bufferHandle,100,2000)
SetConsoleTitle("Debug Console -- #{title}")
echo "#{title} Output Window\n"
echo "-------------------------------\n"
echo "If you are seeing this window, you are running\n"
echo "#{title} in Debug Mode. This means\n"
echo "that you're either playing a Debug Version, or\n"
echo "you are playing from within RPG Maker XP.\n"
echo "\n"
echo "Closing this window will close the game. If \n"
echo "you want to get rid of this window, run the\n"
echo "program from the Shell, or download a Release\n"
echo "version.\n"
echo "\n"
echo "Gameplay will be paused while the console has\n"
echo "focus. To resume playing, switch to the Game\n"
echo "Window.\n"
echo "-------------------------------\n"
echo "Debug Output:\n"
echo "-------------------------------\n\n"
SetConsoleActiveScreenBuffer(@bufferHandle)
end
def self.readInput
length=20
buffer=0.chr*length
eventsread=0.chr*4
done=false
input=""
while !done
echo("waiting for input")
begin
@apiReadConsole.call(@bufferHandle,buffer,1,eventsread)
rescue Hangup
return
end
offset=0
events=eventsread.unpack("V")
echo("got input [eventsread #{events}")
for i in 0...events[0]
keyevent=buffer[offset,20]
keyevent=keyevent.unpack("vCvvvvV")
if keyevent[0]==1 && keyevent[1]>0
input+=keyevent[4].chr
if keyevent[4].chr=="\n"
done=true
break
end
end
offset+=20
end
end
return input
end
def self.readInput2
buffer=0.chr
done=false
input=""
eventsread=0.chr*4
while !done
if ReadConsole(buffer)==0
getlast = Win32API.new("kernel32","GetLastError","","n")
echo(sprintf("failed (%d)\r\n",getlast.call()))
break
end
offset=0
events=eventsread.unpack("V")
if events[0]>0
echo("got input [eventsread #{events}][buffer #{buffer}]\r\n")
key=buffer[0,events[0]]
input+=key
if key=="\n"
break
end
Graphics.update
end
end
return input
end
def self.get_input
echo self.readInput2
end
end
module Kernel
def echo(string)
unless $DEBUG
return
end
Console::WriteConsole(string.is_a?(String) ? string : string.inspect)
end
def echoln(string)
echo(string)
echo("\r\n")
end
end

View File

@@ -0,0 +1,757 @@
#===============================================================================
# Overriding Sprite, Viewport, and Plane to support resizing
# By Peter O.
# Modified by Luka S.J. and Maruno to support fullscreen and more sizes.
# -- This is a stand-alone RGSS script. --
#===============================================================================
$ResizeFactor = 1.0
$ResizeFactorMul = 100
$ResizeOffsetX = 0
$ResizeOffsetY = 0
$ResizeFactorSet = false
$HaveResizeBorder = false
if true # Disables using Alt+Enter to go fullscreen
regHotKey = Win32API.new('user32', 'RegisterHotKey', 'LIII', 'I')
regHotKey.call(0, 1, 1, 0x0D)
end
def pbSetResizeFactor(factor=1,norecalc=false)
factor = [0.5,1.0,2.0,-1][factor] if !norecalc
(factor<0) ? pbConfigureFullScreen : pbConfigureWindowedScreen(factor)
end
def pbSetResizeFactor2(factor,force=false)
if $ResizeFactor!=factor || force
$ResizeFactor = factor
$ResizeFactorMul = (factor*100).to_i
pbRefreshResizeFactor if $ResizeFactorSet
end
$ResizeFactorSet = true
$ResizeBorder.refresh if $HaveResizeBorder
begin
if Graphics.haveresizescreen
Graphics.oldresizescreen(
(Graphics.width+$ResizeOffsetX*2)*factor,
(Graphics.height+$ResizeOffsetY*2)*factor
)
end
Win32API.SetWindowPos(
(Graphics.width+$ResizeOffsetX*2)*factor,
(Graphics.height+$ResizeOffsetY*2)*factor
)
rescue
end
end
def pbRefreshResizeFactor
ObjectSpace.each_object(Sprite) { |o|
next if o.disposed?
o.x = o.x
o.y = o.y
o.ox = o.ox
o.oy = o.oy
o.zoom_x = o.zoom_x
o.zoom_y = o.zoom_y
}
ObjectSpace.each_object(Viewport) { |o|
begin
o.rect = o.rect
o.ox = o.ox
o.oy = o.oy
rescue RGSSError
end
}
ObjectSpace.each_object(Plane) { |o|
next if o.disposed?
o.zoom_x = o.zoom_x
o.zoom_y = o.zoom_y
}
end
def pbConfigureFullScreen
params = Win32API.fillScreen
fullgamew = gamew = SCREEN_WIDTH
fullgameh = gameh = SCREEN_HEIGHT
if !BORDER_FULLY_SHOWS && $PokemonSystem && $PokemonSystem.border==1
fullgamew += BORDER_WIDTH * 2
fullgameh += BORDER_HEIGHT * 2
end
# factor_x = ((2*params[0])/fullgamew).floor
# factor_y = ((2*params[1])/fullgameh).floor
# factor = [factor_x,factor_y].min/2.0
factor_x = (params[0]/fullgamew).floor
factor_y = (params[1]/fullgameh).floor
factor = [factor_x,factor_y].min
offset_x = (params[0]-gamew*factor)/(2*factor)
offset_y = (params[1]-gameh*factor)/(2*factor)
$ResizeOffsetX = offset_x
$ResizeOffsetY = offset_y
ObjectSpace.each_object(Viewport) { |o|
begin
next if o.rect.nil?
ox = o.rect.x-$ResizeOffsetX
oy = o.rect.y-$ResizeOffsetY
o.rect.x = ox+offset_x
o.rect.y = oy+offset_y
rescue RGSSError
end
}
pbSetResizeFactor2(factor,true)
end
def pbConfigureWindowedScreen(value)
border = $PokemonSystem ? $PokemonSystem.border : 0
$ResizeOffsetX = [0,BORDER_WIDTH][border]
$ResizeOffsetY = [0,BORDER_HEIGHT][border]
pbSetResizeFactor2(value,true)
Win32API.restoreScreen
end
def setScreenBorderName(border)
if !$HaveResizeBorder
$ResizeBorder = ScreenBorder.new
$HaveResizeBorder = true
end
$ResizeBorder.bordername = border if $ResizeBorder
end
module Graphics
## Nominal screen size
@@width = SCREEN_WIDTH
@@height = SCREEN_HEIGHT
def self.width
return @@width.to_i
end
def self.height
return @@height.to_i
end
@@fadeoutvp = Viewport.new(0,0,640,480)
@@fadeoutvp.z = 0x3FFFFFFF
@@fadeoutvp.color = Color.new(0,0,0,0)
def self.brightness
return 255-@@fadeoutvp.color.alpha
end
def self.brightness=(value)
value = 0 if value<0
value = 255 if value>255
@@fadeoutvp.color.alpha = 255-value
end
def self.fadein(frames)
return if frames<=0
curvalue = self.brightness
count = (255-self.brightness)
frames.times do |i|
self.brightness = curvalue+(count*i/frames)
self.update
end
end
def self.wait(frames)
return if frames<=0
frames.times do |i|
self.update
end
end
def self.fadeout(frames)
return if frames<=0
curvalue = self.brightness
count = self.brightness
frames.times do |i|
self.brightness = curvalue-(count*i/frames)
self.update
end
end
class << self
begin
x = @@haveresizescreen
rescue NameError # If exception is caught, the class
if !method_defined?(:oldresizescreen) # variable wasn't defined yet
begin
alias oldresizescreen resize_screen
@@haveresizescreen = true
rescue
@@haveresizescreen = false
end
else
@@haveresizescreen = false
end
end
def haveresizescreen
@@haveresizescreen
end
end
def self.resize_screen(w,h)
@@width = w
@@height = h
pbSetResizeFactor($ResizeFactor,true)
end
@@deletefailed = false
def self.snap_to_bitmap(resize=true)
tempPath = ENV["TEMP"]+"\\tempscreen.bmp"
if safeExists?(tempPath) && @@deletefailed
begin
File.delete(tempPath)
@@deletefailed = false
rescue Errno::EACCES
@@deletefailed = true
return nil
end
end
if safeExists?("./rubyscreen.dll")
takescreen = Win32API.new("rubyscreen.dll","TakeScreenshot","p","i")
takescreen.call(tempPath)
end
bm = nil
if safeExists?(tempPath)
bm = Bitmap.new(tempPath)
begin
File.delete(tempPath)
@@deletefailed = false
rescue Errno::EACCES
@@deletefailed = true
end
end
bm.asOpaque if bm && bm.get_pixel(0,0).alpha==0
if resize
if bm && $ResizeOffsetX && $ResizeOffsetY && ($ResizeOffsetX!=0 || $ResizeOffsetY!=0)
tmpbitmap = Bitmap.new(Graphics.width*$ResizeFactor,Graphics.height*$ResizeFactor)
tmpbitmap.blt(0,0,bm,Rect.new(
$ResizeOffsetX*$ResizeFactor,$ResizeOffsetY*$ResizeFactor,tmpbitmap.width,tmpbitmap.height))
bm.dispose
bm = tmpbitmap
end
if bm && (bm.width!=Graphics.width || bm.height!=Graphics.height)
newbitmap = Bitmap.new(Graphics.width,Graphics.height)
newbitmap.stretch_blt(newbitmap.rect,bm,Rect.new(0,0,bm.width,bm.height))
bm.dispose
bm = newbitmap
end
else
# Thise code is used only for taking screenshots with F8.
# Doesn't crop out the screen border, doesn't normalise to 1x zoom.
# Fixes screenshots being 1 pixel too tall.
fullw = (Graphics.width+$ResizeOffsetX*2)*$ResizeFactor
fullh = (Graphics.height+$ResizeOffsetY*2)*$ResizeFactor
if bm && $ResizeOffsetX && $ResizeOffsetY && $ResizeFactor &&
(bm.width!=fullw || bm.height!=fullh)
tmpbitmap = Bitmap.new(fullw,fullh)
tmpbitmap.blt(0,0,bm,Rect.new(0,0,fullw,fullh))
bm.dispose
bm = tmpbitmap
end
end
return bm
end
end
class Sprite
unless @SpriteResizerMethodsAliased
alias _initialize_SpriteResizer initialize
alias _x_SpriteResizer x
alias _y_SpriteResizer y
alias _ox_SpriteResizer ox
alias _oy_SpriteResizer oy
alias _zoomx_SpriteResizer zoom_x
alias _zoomy_SpriteResizer zoom_y
alias _xeq_SpriteResizer x=
alias _yeq_SpriteResizer y=
alias _oxeq_SpriteResizer ox=
alias _oyeq_SpriteResizer oy=
alias _zoomxeq_SpriteResizer zoom_x=
alias _zoomyeq_SpriteResizer zoom_y=
alias _bushdeptheq_SpriteResizer bush_depth=
@SpriteResizerMethodsAliased = true
end
def initialize(viewport=nil)
_initialize_SpriteResizer(viewport)
@resizedX=0
@resizedY=0
@resizedOx=0
@resizedOy=0
@resizedBushDepth=0
@resizedZoomX=1.0
@resizedZoomY=1.0
if $ResizeOffsetX!=0 && $ResizeOffsetY!=0 && !viewport
_xeq_SpriteResizer($ResizeOffsetX.to_f*$ResizeFactorMul/100)
_yeq_SpriteResizer($ResizeOffsetY.to_f*$ResizeFactorMul/100)
end
_zoomxeq_SpriteResizer(@resizedZoomX*$ResizeFactorMul/100)
_zoomyeq_SpriteResizer(@resizedZoomY*$ResizeFactorMul/100)
end
def x
return @resizedX
end
def y
return @resizedY
end
def x=(val)
if $ResizeFactorMul!=100
offset=(self.viewport) ? 0 : $ResizeOffsetX
value=(val+offset).to_f*$ResizeFactorMul/100
_xeq_SpriteResizer(value.to_i)
@resizedX=val.to_i
elsif self.viewport
_xeq_SpriteResizer(val)
@resizedX=val
else
_xeq_SpriteResizer(val + $ResizeOffsetX)
@resizedX=val
end
end
def y=(val)
if $ResizeFactorMul!=100
offset=(self.viewport) ? 0 : $ResizeOffsetY
value=(val+offset).to_f*$ResizeFactorMul/100
_yeq_SpriteResizer(value.to_i)
@resizedY=val.to_i
elsif self.viewport
_yeq_SpriteResizer(val)
@resizedY=val
else
_yeq_SpriteResizer(val + $ResizeOffsetY)
@resizedY=val
end
end
def ox
return @resizedOx
end
def oy
return @resizedOy
end
def ox=(val)
@resizedOx=val
_oxeq_SpriteResizer(val)
end
def oy=(val)
@resizedOy=val
_oyeq_SpriteResizer(val)
end
def zoom_x
return @resizedZoomX
end
def zoom_y
return @resizedZoomY
end
def zoom_x=(val)
value=val
if $ResizeFactorMul!=100
value=(val.to_f*$ResizeFactorMul/100)
if (value-0.5).abs<=0.001
value=0.5
elsif (value-1.0).abs<=0.001
value=1.0
elsif (value-1.5).abs<=0.001
value=1.5
elsif (value-2.0).abs<=0.001
value=2.0
end
end
_zoomxeq_SpriteResizer(value)
@resizedZoomX=val
end
def zoom_y=(val)
value=val
if $ResizeFactorMul!=100
value=(val.to_f*$ResizeFactorMul/100)
if (value-0.5).abs<=0.001
value=0.5
elsif (value-1.0).abs<=0.001
value=1.0
elsif (value-1.5).abs<=0.001
value=1.5
elsif (value-2.0).abs<=0.001
value=2.0
end
end
_zoomyeq_SpriteResizer(value)
@resizedZoomY=val
end
def bush_depth
return @resizedBushDepth
end
def bush_depth=(val)
value=((val.to_i)*$ResizeFactorMul/100)
_bushdeptheq_SpriteResizer(value.to_i)
@resizedBushDepth=val.to_i
end
end
class NotifiableRect < Rect
def setNotifyProc(proc)
@notifyProc = proc
end
def set(x,y,width,height)
super
@notifyProc.call(self) if @notifyProc
end
def x=(value)
super
@notifyProc.call(self) if @notifyProc
end
def y=(value)
super
@notifyProc.call(self) if @notifyProc
end
def width=(value)
super
@notifyProc.call(self) if @notifyProc
end
def height=(value)
super
@notifyProc.call(self) if @notifyProc
end
end
class Viewport
unless @SpriteResizerMethodsAliased
alias _initialize_SpriteResizer initialize
alias _rect_ViewportResizer rect
alias _recteq_SpriteResizer rect=
alias _oxeq_SpriteResizer ox=
alias _oyeq_SpriteResizer oy=
@SpriteResizerMethodsAliased=true
end
def initialize(*arg)
args=arg.clone
@oldrect=Rect.new(0,0,100,100)
_initialize_SpriteResizer(@oldrect)
newRect=NotifiableRect.new(0,0,0,0)
@resizedRectProc=Proc.new { |r|
if $ResizeFactorMul==100
@oldrect.set(
r.x.to_i+$ResizeOffsetX,
r.y.to_i+$ResizeOffsetY,
r.width.to_i,
r.height.to_i
)
self._recteq_SpriteResizer(@oldrect)
else
@oldrect.set(
((r.x+$ResizeOffsetX)*$ResizeFactorMul/100).to_i,
((r.y+$ResizeOffsetY)*$ResizeFactorMul/100).to_i,
(r.width*$ResizeFactorMul/100).to_i,
(r.height*$ResizeFactorMul/100).to_i
)
self._recteq_SpriteResizer(@oldrect)
end
}
newRect.setNotifyProc(@resizedRectProc)
if arg.length==1
newRect.set(args[0].x,args[0].y,args[0].width,args[0].height)
else
newRect.set(args[0],args[1],args[2],args[3])
end
@resizedRect=newRect
@resizedOx=0
@resizedOy=0
end
def ox
return @resizedOx
end
def ox=(val)
return if !val
_oxeq_SpriteResizer(val.to_f*$ResizeFactorMul/100)
@resizedOx=val
end
def oy
return @resizedOy
end
def oy=(val)
return if !val
_oyeq_SpriteResizer(val.to_f*$ResizeFactorMul/100)
@resizedOy=val
end
def rect
return @resizedRect
end
def rect=(val)
if val
newRect=NotifiableRect.new(0,0,100,100)
newRect.setNotifyProc(@resizedRectProc)
newRect.set(val.x.to_i,val.y.to_i,val.width.to_i,val.height.to_i)
@resizedRect=newRect
end
end
end
class Plane
unless @SpriteResizerMethodsAliased
alias _initialize_SpriteResizer initialize
alias _zoomxeq_SpriteResizer zoom_x=
alias _zoomyeq_SpriteResizer zoom_y=
alias _oxeq_SpriteResizer ox=
alias _oyeq_SpriteResizer oy=
@SpriteResizerMethodsAliased=true
end
def initialize(viewport=nil)
_initialize_SpriteResizer(viewport)
@resizedZoomX=1.0
@resizedZoomY=1.0
@resizedOx=0
@resizedOy=0
_zoomxeq_SpriteResizer(@resizedZoomX*$ResizeFactorMul/100)
_zoomyeq_SpriteResizer(@resizedZoomY*$ResizeFactorMul/100)
end
def ox
return @resizedOx
end
def ox=(val)
return if !val
_oxeq_SpriteResizer(val.to_f*$ResizeFactorMul/100)
@resizedOx=val
end
def oy
return @resizedOy
end
def oy=(val)
return if !val
_oyeq_SpriteResizer(val.to_f*$ResizeFactorMul/100)
@resizedOy=val
end
def zoom_x
return @resizedZoomX
end
def zoom_x=(val)
return if !val
_zoomxeq_SpriteResizer(val*$ResizeFactorMul/100)
@resizedZoomX=val
end
def zoom_y
return @resizedZoomY
end
def zoom_y=(val)
return if !val
_zoomyeq_SpriteResizer(val*$ResizeFactorMul/100)
@resizedZoomY=val
end
end
class ScreenBorder
def initialize
initializeInternal
refresh
end
def initializeInternal
@maximumZ=500000
@bordername=""
@sprite=IconSprite.new(0,0) rescue Sprite.new
@defaultwidth=640
@defaultheight=480
@defaultbitmap=Bitmap.new(@defaultwidth,@defaultheight)
end
def dispose
@borderbitmap.dispose if @borderbitmap
@defaultbitmap.dispose
@sprite.dispose
end
def adjustZ(z)
if z>=@maximumZ
@maximumZ=z+1
@sprite.z=@maximumZ
end
end
def bordername=(value)
@bordername=value
refresh
end
def refresh
@sprite.z=@maximumZ
@sprite.x=-BORDER_WIDTH
@sprite.y=-BORDER_HEIGHT
@sprite.visible=($PokemonSystem && $PokemonSystem.border==1)
@sprite.bitmap=nil
if @sprite.visible
if @bordername!=nil && @bordername!=""
setSpriteBitmap("Graphics/Pictures/"+@bordername)
else
setSpriteBitmap(nil)
@sprite.bitmap=@defaultbitmap
end
end
@defaultbitmap.clear
@defaultbitmap.fill_rect(0,0,@defaultwidth,$ResizeOffsetY,Color.new(0,0,0))
@defaultbitmap.fill_rect(0,$ResizeOffsetY,
$ResizeOffsetX,@defaultheight-$ResizeOffsetY,Color.new(0,0,0))
@defaultbitmap.fill_rect(@defaultwidth-$ResizeOffsetX,$ResizeOffsetY,
$ResizeOffsetX,@defaultheight-$ResizeOffsetY,Color.new(0,0,0))
@defaultbitmap.fill_rect($ResizeOffsetX,@defaultheight-$ResizeOffsetY,
@defaultwidth-$ResizeOffsetX*2,$ResizeOffsetY,Color.new(0,0,0))
end
private
def setSpriteBitmap(x)
if (@sprite.is_a?(IconSprite) rescue false)
@sprite.setBitmap(x)
else
@sprite.bitmap=x ? RPG::Cache.load_bitmap("",x) : nil
end
end
end
class Bitmap
# Fast methods for retrieving bitmap data
RtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
RtlMoveMemory_ip = Win32API.new('kernel32', 'RtlMoveMemory', 'ipi', 'i')
SwapRgb = Win32API.new('./rubyscreen.dll', 'SwapRgb', 'pi', '') rescue nil
def setData(x)
RtlMoveMemory_ip.call(self.address, x, x.length)
end
def getData
data = "rgba" * width * height
RtlMoveMemory_pi.call(data, self.address, data.length)
return data
end
def swap32(x)
return ((x>>24)&0x000000FF)|
((x>>8)&0x0000FF00)|
((x<<8)&0x00FF0000)|
((x<<24)&0xFF000000)
end
def asOpaque
data=getData
j=3
for i in 0...width*height
data[j]=0xFF
j+=4
end
setData(data)
end
def saveToPng(filename)
bytes=[
0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D
].pack("CCCCCCCCCCCC")
ihdr=[
0x49,0x48,0x44,0x52,swap32(self.width),swap32(self.height),
0x08,0x06,0x00,0x00,0x00
].pack("CCCCVVCCCCC")
crc=Zlib::crc32(ihdr)
ihdr+=[swap32(crc)].pack("V")
bytesPerScan=self.width*4
row=(self.height-1)*bytesPerScan
data=self.getData
data2=data.clone
width=self.width
x=""
len=bytesPerScan*self.height
ttt=Time.now
if SwapRgb
SwapRgb.call(data2,data2.length)
else
# the following is considerably slower
b=0;c=2;while b!=len
data2[b]=data[c]
data2[c]=data[b]
b+=4;c+=4;
end
end
#$times.push(Time.now-ttt)
filter="\0"
while row>=0
thisRow=data2[row,bytesPerScan]
x.concat(filter)
x.concat(thisRow)
row-=bytesPerScan
end
x=Zlib::Deflate.deflate(x)
length=x.length
x="IDAT"+x
crc=Zlib::crc32(x)
idat=[swap32(length)].pack("V")
idat.concat(x)
idat.concat([swap32(crc)].pack("V"))
idat.concat([0,0x49,0x45,0x4E,0x44,0xAE,0x42,0x60,0x82].pack("VCCCCCCCC"))
File.open(filename,"wb") { |f|
f.write(bytes)
f.write(ihdr)
f.write(idat)
}
end
def address
if !@address
buffer, ad = "rgba", object_id * 2 + 16
RtlMoveMemory_pi.call(buffer, ad, 4)
ad = buffer.unpack("L")[0] + 8
RtlMoveMemory_pi.call(buffer, ad, 4)
ad = buffer.unpack("L")[0] + 16
RtlMoveMemory_pi.call(buffer, ad, 4)
@address=buffer.unpack("L")[0]
end
return @address
end
end

View File

@@ -0,0 +1,376 @@
#==============================================================================#
# Plugin Manager #
# by Marin #
#------------------------------------------------------------------------------#
# Provides a simple interface that allows plugins to require dependencies #
# at specific versions, and to specify incompatibilities between plugins. #
#------------------------------------------------------------------------------#
# Usage: #
# #
# A Pokémon Essentials plugin should register itself using the PluginManager. #
# The simplest way to do so, for a plugin without dependencies, is as follows: #
# #
# PluginManager.register({ #
# :name => "Basic Plugin", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => "Marin" #
# }) #
# #
# The link portion here is optional, but recommended. This will be shown in #
# the error message if the PluginManager detects that this plugin needs to be #
# updated. #
# #
# A plugin's version is typically in the format X.Y.Z, but the number of #
# digits does not matter. You can also use Xa, Xb, Xc, Ya, etc. #
# What matters is that you use it consistently, so that it can be compared. #
# #
# #
# #
# Now let's say we create a new plugin titled "Simple Extension", which #
# requires our previously created "Basic Plugin" to work. #
# #
# PluginManager.register({ #
# :name => "Simple Extension", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => ["Marin", "Maruno"], #
# :dependencies => ["Basic Plugin"] #
# }) #
# #
# This plugin has two credits as an array, instead of one string. Furthermore, #
# this code will ensure that "Basic Plugin" is installed, ignoring its #
# version. If you have only one dependency, you can omit the array brackets #
# like so: #
# #
# :dependencies => "Basic Plugin" #
# #
# #
# #
# To require a minimum version of a dependency plugin, you should turn the #
# dependency's name into an array which contains the name and the version #
# (both as strings). For example, to require "Basic Plugin" version 1.2 or #
# higher, you would write: #
# #
# PluginManager.register({ #
# :name => "Simple Extension", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => "Marin", #
# :dependencies => [ #
# ["Basic Plugin", "1.2"] #
# ] #
# }) #
# #
# #
# #
# To require a specific version (no higher and no lower) of a dependency #
# plugin, you should add the :exact flag as the first thing in the array for #
# that dependency: #
# #
# PluginManager.register({ #
# :name => "Simple Extension", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => "Marin", #
# :dependencies => [ #
# [:exact, "Basic Plugin", "1.2"] #
# ] #
# }) #
# #
# #
# #
# If your plugin is known to be incompatible with another plugin, you should #
# list that other plugin as such. Only one of the two plugins needs to list #
# that it is incompatible with the other. #
# #
# PluginManager.register({ #
# :name => "QoL Improvements", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => "Marin", #
# :incompatibilities => [ #
# "Simple Extension" #
# ] #
# }) #
# #
# #
# #
# If your plugin can work without another plugin, but is known to be #
# incompatible with an old version of that other plugin, you should list it as #
# an optional dependency. If that other plugin is present in a game, then this #
# optional dependency will ensure it meets the minimum version required for #
# your plugin. Write it in the same way as any other dependency as described #
# above, but use the :optional flag instead. #
# You do not need to list a plugin as an optional dependency at all if all #
# versions of that other plugin are compatible with your plugin. #
# #
# PluginManager.register({ #
# :name => "Other Plugin", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => "Marin", #
# :dependencies => [ #
# [:optional, "QoL Improvements", "1.1"] #
# ] #
# }) #
# #
# The :optional_exact flag is a combination of :optional and :exact. #
#------------------------------------------------------------------------------#
# Please give credit when using this. #
#==============================================================================#
module PluginManager
# Win32API MessageBox function for custom errors.
MBOX = Win32API.new('user32', 'MessageBox', ['I','P','P','I'], 'I')
# Holds all registered plugin data.
@@Plugins = {}
# Registers a plugin and tests its dependencies and incompatibilities.
def self.register(options)
name = nil
version = nil
link = nil
dependencies = nil
incompats = nil
credits = []
order = [:name, :version, :link, :dependencies, :incompatibilities, :credits]
# Ensure it first reads the plugin's name, which is used in error reporting,
# by sorting the keys
keys = options.keys.sort do |a, b|
idx_a = order.index(a)
idx_a = order.size if idx_a == -1
idx_b = order.index(b)
idx_b = order.size if idx_b == -1
next idx_a <=> idx_b
end
for key in keys
value = options[key]
case key
when :name # Plugin name
if nil_or_empty?(value)
self.error("Plugin name must be a non-empty string.")
end
if !@@Plugins[value].nil?
self.error("A plugin called '#{value}' already exists.")
end
name = value
when :version # Plugin version
if nil_or_empty?(value)
self.error("Plugin version must be a string.")
end
version = value
when :link # Plugin website
if nil_or_empty?(value)
self.error("Plugin link must be a non-empty string.")
end
link = value
when :dependencies # Plugin dependencies
dependencies = value
dependencies = [dependencies] if !dependencies.is_a?(Array) || !dependencies[0].is_a?(Array)
for dep in value
if dep.is_a?(String) # "plugin name"
if !self.installed?(dep)
self.error("Plugin '#{name}' requires plugin '#{dep}' to be installed above it.")
end
elsif dep.is_a?(Array)
case dep.size
when 1 # ["plugin name"]
if dep[0].is_a?(String)
dep_name = dep[0]
if !self.installed?(dep_name)
self.error("Plugin '#{name}' requires plugin '#{dep_name}' to be installed above it.")
end
else
self.error("Expected the plugin name as a string, but got #{dep[0].inspect}.")
end
when 2 # ["plugin name", "version"]
if dep[0].is_a?(Symbol)
self.error("A plugin version comparator symbol was given but no version was given.")
elsif dep[0].is_a?(String) && dep[1].is_a?(String)
dep_name = dep[0]
dep_version = dep[1]
next if self.installed?(dep_name, dep_version)
if self.installed?(dep_name) # Have plugin but lower version
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} or higher, " +
"but the installed version is #{self.version(dep_name)}."
if dep_link = self.link(dep_name)
msg += "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
else # Don't have plugin
self.error("Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} " +
"or higher to be installed above it.")
end
end
when 3 # [:optional/:exact/:optional_exact, "plugin name", "version"]
if !dep[0].is_a?(Symbol)
self.error("Expected first dependency argument to be a symbol, but got #{dep[0].inspect}.")
end
if !dep[1].is_a?(String)
self.error("Expected second dependency argument to be a plugin name, but got #{dep[1].inspect}.")
end
if !dep[2].is_a?(String)
self.error("Expected third dependency argument to be the plugin version, but got #{dep[2].inspect}.")
end
dep_arg = dep[0]
dep_name = dep[1]
dep_version = dep[2]
optional = false
exact = false
case def_arg
when :optional; optional = true
when :exact; exact = true
when :optional_exact; optional = true; exact = true
else
self.error("Expected first dependency argument to be one of " +
":optional, :exact or :optional_exact, but got #{dep_arg.inspect}.")
end
if optional
if self.installed?(dep_name) && # Have plugin but lower version
!self.installed?(dep_name, dep_version, exact)
msg = "Plugin '#{name}' requires plugin '#{dep_name}', if installed, to be version #{dep_version}"
msg << " or higher" if !exact
msg << ", but the installed version was #{self.version(dep_name)}."
if dep_link = self.link(dep_name)
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
end
elsif !self.installed?(dep_name, dep_version, exact)
if self.installed?(dep_name) # Have plugin but lower version
msg = "Plugin '#{name}' requires plugin '#{dep_name}' to be version #{dep_version}"
msg << " or later" if !exact
msg << ", but the installed version was #{self.version(dep_name)}."
if dep_link = self.link(dep_name)
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
else # Don't have plugin
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} "
msg << "or later" if !exact
msg << "to be installed above it."
self.error(msg)
end
end
end
end
end
when :incompatibilities # Plugin incompatibilities
incompats = value
incompats = [incompats] if !incompats.is_a?(Array)
for incompat in incompats
if self.installed?(incompat)
self.error("Plugin '#{name}' is incompatible with '#{incompat}'. " +
"They cannot both be used at the same time.")
end
end
when :credits # Plugin credits
value = [value] if value.is_a?(String)
if value.is_a?(Array)
for entry in value
if !entry.is_a?(String)
self.error("Plugin '#{name}'s credits array contains a non-string value.")
else
credits << entry
end
end
else
self.error("Plugin '#{name}'s credits field must contain a string, or a string array.")
end
else
self.error("Invalid plugin registry key '#{key}'.")
end
end
for plugin in @@Plugins.values
if plugin[:incompatibilities] && plugin[:incompatibilities].include?(name)
self.error("Plugin '#{plugin[:name]}' is incompatible with '#{name}'. " +
"They cannot both be used at the same time.")
end
end
# Add plugin to class variable
@@Plugins[name] = {
:name => name,
:version => version,
:link => link,
:dependencies => dependencies,
:incompatibilities => incompats,
:credits => credits
}
end
# Throws a pure error message without stack trace or any other useless info.
def self.error(msg)
Graphics.update
t = Thread.new do
MBOX.call(Win32API.pbFindRgssWindow, msg, "Plugin Error", 0x10)
Thread.exit
end
while t.status
Graphics.update
end
Kernel.exit! true
end
# Returns true if the specified plugin is installed.
# If the version is specified, this version is taken into account.
# If mustequal is true, the version must be a match with the specified version.
def self.installed?(plugin_name, plugin_version = nil, mustequal = false)
plugin = @@Plugins[plugin_name]
return false if plugin.nil?
return true if plugin_version.nil?
comparison = compare_versions(plugin[:version], plugin_version)
return true if !mustequal && comparison >= 0
return true if mustequal && comparison == 0
end
# Returns the string names of all installed plugins.
def self.plugins
return @@Plugins.keys
end
# Returns the installed version of the specified plugin.
def self.version(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:version]
end
# Returns the link of the specified plugin.
def self.link(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:link]
end
# Returns the credits of the specified plugin.
def self.credits(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:credits]
end
# Compares two versions given in string form. v1 should be the plugin version
# you actually have, and v2 should be the minimum/desired plugin version.
# Return values:
# 1 if v1 is higher than v2
# 0 if v1 is equal to v2
# -1 if v1 is lower than v2
def self.compare_versions(v1, v2)
d1 = v1.split("")
d1.insert(0, "0") if d1[0] == "." # Turn ".123" into "0.123"
while d1[-1] == "."; d1 = d1[0..-2]; end # Turn "123." into "123"
d2 = v2.split("")
d2.insert(0, "0") if d2[0] == "." # Turn ".123" into "0.123"
while d2[-1] == "."; d2 = d2[0..-2]; end # Turn "123." into "123"
for i in 0...[d1.size, d2.size].max # Compare each digit in turn
c1 = d1[i]
c2 = d2[i]
if c1
return 1 if !c2
return 1 if c1.to_i(16) > c2.to_i(16)
return -1 if c1.to_i(16) < c2.to_i(16)
else
return -1 if c2
end
end
return 0
end
end

View File

@@ -0,0 +1,104 @@
#===============================================================================
# ** Game_Temp
#-------------------------------------------------------------------------------
# This class handles temporary data that is not included with save data.
# Refer to "$game_temp" for the instance of this class.
#===============================================================================
class Game_Temp
attr_accessor :map_bgm # map music (for battle memory)
attr_accessor :message_text # message text
attr_accessor :message_proc # message callback (Proc)
attr_accessor :choice_start # show choices: opening line
attr_accessor :choice_max # show choices: number of items
attr_accessor :choice_cancel_type # show choices: cancel
attr_accessor :choice_proc # show choices: callback (Proc)
attr_accessor :num_input_start # input number: opening line
attr_accessor :num_input_variable_id # input number: variable ID
attr_accessor :num_input_digits_max # input number: digit amount
attr_accessor :message_window_showing # message window showing
attr_accessor :common_event_id # common event ID
attr_accessor :in_battle # in-battle flag
attr_accessor :battle_calling # battle calling flag
attr_accessor :battle_troop_id # battle troop ID
attr_accessor :battle_can_escape # battle flag: escape possible
attr_accessor :battle_can_lose # battle flag: losing possible
attr_accessor :battle_proc # battle callback (Proc)
attr_accessor :battle_turn # number of battle turns
attr_accessor :battle_event_flags # battle event flags: completed
attr_accessor :battle_abort # battle flag: interrupt
attr_accessor :battle_main_phase # battle flag: main phase
attr_accessor :battleback_name # battleback file name
attr_accessor :forcing_battler # battler being forced into action
attr_accessor :shop_calling # shop calling flag
attr_accessor :shop_goods # list of shop goods
attr_accessor :name_calling # name input: calling flag
attr_accessor :name_actor_id # name input: actor ID
attr_accessor :name_max_char # name input: max character count
attr_accessor :menu_calling # menu calling flag
attr_accessor :menu_beep # menu: play sound effect flag
attr_accessor :in_menu # menu is open
attr_accessor :save_calling # save calling flag
attr_accessor :debug_calling # debug calling flag
attr_accessor :player_transferring # player place movement flag
attr_accessor :player_new_map_id # player destination: map ID
attr_accessor :player_new_x # player destination: x-coordinate
attr_accessor :player_new_y # player destination: y-coordinate
attr_accessor :player_new_direction # player destination: direction
attr_accessor :transition_processing # transition processing flag
attr_accessor :transition_name # transition file name
attr_accessor :gameover # game over flag
attr_accessor :to_title # return to title screen flag
attr_accessor :last_file_index # last save file no.
attr_accessor :map_refresh # map needs redrawing
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@map_bgm = nil
@message_text = nil
@message_proc = nil
@choice_start = 99
@choice_max = 0
@choice_cancel_type = 0
@choice_proc = nil
@num_input_start = 99
@num_input_variable_id = 0
@num_input_digits_max = 0
@message_window_showing = false
@common_event_id = 0
@in_battle = false
@battle_calling = false
@battle_troop_id = 0
@battle_can_escape = false
@battle_can_lose = false
@battle_proc = nil
@battle_turn = 0
@battle_event_flags = {}
@battle_abort = false
@battle_main_phase = false
@battleback_name = ''
@forcing_battler = nil
@shop_calling = false
@shop_id = 0
@name_calling = false
@name_actor_id = 0
@name_max_char = 0
@menu_calling = false
@menu_beep = false
@in_menu = false
@save_calling = false
@debug_calling = false
@player_transferring = false
@player_new_map_id = 0
@player_new_x = 0
@player_new_y = 0
@player_new_direction = 0
@transition_processing = false
@transition_name = ""
@gameover = false
@to_title = false
@last_file_index = 0
@debug_top_row = 0
@debug_index = 0
end
end

View File

@@ -0,0 +1,36 @@
#===============================================================================
# ** Game_Switches
#-------------------------------------------------------------------------------
# This class handles switches. It's a wrapper for the built-in class "Array."
# Refer to "$game_switches" for the instance of this class.
#===============================================================================
class Game_Switches
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@data = []
end
#-----------------------------------------------------------------------------
# * Get Switch
# switch_id : switch ID
#-----------------------------------------------------------------------------
def [](switch_id)
if switch_id<=5000 and @data[switch_id]!=nil
return @data[switch_id]
else
return false
end
end
#-----------------------------------------------------------------------------
# * Set Switch
# switch_id : switch ID
# value : ON (true) / OFF (false)
#-----------------------------------------------------------------------------
def []=(switch_id, value)
if switch_id<=5000
@data[switch_id] = value
end
end
end

View File

@@ -0,0 +1,36 @@
#===============================================================================
# ** Game_Variables
#-------------------------------------------------------------------------------
# This class handles variables. It's a wrapper for the built-in class "Array."
# Refer to "$game_variables" for the instance of this class.
#===============================================================================
class Game_Variables
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@data = []
end
#-----------------------------------------------------------------------------
# * Get Variable
# variable_id : variable ID
#-----------------------------------------------------------------------------
def [](variable_id)
if variable_id<=5000 and @data[variable_id]!=nil
return @data[variable_id]
else
return 0
end
end
#-----------------------------------------------------------------------------
# * Set Variable
# variable_id : variable ID
# value : the variable's value
#-----------------------------------------------------------------------------
def []=(variable_id, value)
if variable_id<=5000
@data[variable_id] = value
end
end
end

View File

@@ -0,0 +1,30 @@
#===============================================================================
# ** Game_SelfSwitches
#-------------------------------------------------------------------------------
# This class handles self switches. It's a wrapper for the built-in class
# "Hash." Refer to "$game_self_switches" for the instance of this class.
#===============================================================================
class Game_SelfSwitches
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@data = {}
end
#-----------------------------------------------------------------------------
# * Get Self Switch
# key : key
#-----------------------------------------------------------------------------
def [](key)
return (@data[key]==true) ? true : false
end
#-----------------------------------------------------------------------------
# * Set Self Switch
# key : key
# value : ON (true) / OFF (false)
#-----------------------------------------------------------------------------
def []=(key, value)
@data[key] = value
end
end

View File

@@ -0,0 +1,156 @@
#===============================================================================
# ** Game_Screen
#-------------------------------------------------------------------------------
# This class handles screen maintenance data, such as change in color tone,
# flashing, etc. Refer to "$game_screen" for the instance of this class.
#===============================================================================
class Game_Screen
#-----------------------------------------------------------------------------
# * Public Instance Variables
#-----------------------------------------------------------------------------
attr_reader :brightness # brightness
attr_reader :tone # color tone
attr_reader :flash_color # flash color
attr_reader :shake # shake positioning
attr_reader :pictures # pictures
attr_reader :weather_type # weather type
attr_reader :weather_max # max number of weather sprites
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@brightness = 255
@fadeout_duration = 0
@fadein_duration = 0
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@flash_color = Color.new(0, 0, 0, 0)
@flash_duration = 0
@shake_power = 0
@shake_speed = 0
@shake_duration = 0
@shake_direction = 1
@shake = 0
@pictures = [nil]
for i in 1..100
@pictures.push(Game_Picture.new(i))
end
@weather_type = 0
@weather_max = 0.0
@weather_type_target = 0
@weather_max_target = 0.0
@weather_duration = 0
end
#-----------------------------------------------------------------------------
# * Start Changing Color Tone
# tone : color tone
# duration : time
#-----------------------------------------------------------------------------
def start_tone_change(tone, duration)
@tone_target = tone.clone
@tone_duration = duration
if @tone_duration == 0
@tone = @tone_target.clone
end
end
#-----------------------------------------------------------------------------
# * Start Flashing
# color : color
# duration : time
#-----------------------------------------------------------------------------
def start_flash(color, duration)
@flash_color = color.clone
@flash_duration = duration
end
#-----------------------------------------------------------------------------
# * Start Shaking
# power : strength
# speed : speed
# duration : time
#-----------------------------------------------------------------------------
def start_shake(power, speed, duration)
@shake_power = power
@shake_speed = speed
@shake_duration = duration
end
#-----------------------------------------------------------------------------
# * Set Weather
# type : type
# power : strength
# duration : time
#-----------------------------------------------------------------------------
def weather(type, power, duration)
@weather_type_target = type
if @weather_type_target!=0
@weather_type = @weather_type_target
end
if @weather_type_target==0
@weather_max_target = 0.0
else
@weather_max_target = (power + 1) * 4.0
end
@weather_duration = duration
if @weather_duration==0
@weather_type = @weather_type_target
@weather_max = @weather_max_target
end
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
if @fadeout_duration && @fadeout_duration>=1
d = @fadeout_duration
@brightness = (@brightness*(d-1))/d
@fadeout_duration -= 1
end
if @fadein_duration && @fadein_duration>=1
d = @fadein_duration
@brightness = (@brightness*(d-1)+255)/d
@fadein_duration -= 1
end
if @tone_duration>=1
d = @tone_duration
@tone.red = (@tone.red*(d-1)+@tone_target.red)/d
@tone.green = (@tone.green*(d-1)+@tone_target.green)/d
@tone.blue = (@tone.blue*(d-1)+@tone_target.blue)/d
@tone.gray = (@tone.gray*(d-1)+@tone_target.gray)/d
@tone_duration -= 1
end
if @flash_duration>=1
d = @flash_duration
@flash_color.alpha = @flash_color.alpha*(d-1)/d
@flash_duration -= 1
end
if @shake_duration>=1 or @shake!=0
delta = (@shake_power*@shake_speed*@shake_direction)/10.0
if @shake_duration<=1 and @shake*(@shake+delta)<0
@shake = 0
else
@shake += delta
end
@shake_direction = -1 if @shake>@shake_power*2
@shake_direction = 1 if @shake<-@shake_power*2
@shake_duration -= 1 if @shake_duration>=1
end
if @weather_duration>=1
d = @weather_duration
@weather_max = (@weather_max*(d-1)+@weather_max_target)/d
@weather_duration -= 1
if @weather_duration==0
@weather_type = @weather_type_target
end
end
if $game_temp.in_battle
for i in 51..100
@pictures[i].update
end
else
for i in 1..50
@pictures[i].update
end
end
end
end

View File

@@ -0,0 +1,289 @@
#==============================================================================
# ** Game_System
#------------------------------------------------------------------------------
# This class handles data surrounding the system. Backround music, etc.
# is managed here as well. Refer to "$game_system" for the instance of
# this class.
#==============================================================================
class Game_System
attr_reader :map_interpreter # map event interpreter
attr_reader :battle_interpreter # battle event interpreter
attr_accessor :timer # timer
attr_accessor :timer_working # timer working flag
attr_accessor :save_disabled # save forbidden
attr_accessor :menu_disabled # menu forbidden
attr_accessor :encounter_disabled # encounter forbidden
attr_accessor :message_position # text option: positioning
attr_accessor :message_frame # text option: window frame
attr_accessor :save_count # save count
attr_accessor :magic_number # magic number
attr_accessor :autoscroll_x_speed
attr_accessor :autoscroll_y_speed
attr_accessor :bgm_position
def initialize
if $RPGVX
@map_interpreter = Game_Interpreter.new(0,true)
@battle_interpreter = Game_Interpreter.new(0,false)
else
@map_interpreter = Interpreter.new(0,true)
@battle_interpreter = Interpreter.new(0,false)
end
@timer = 0
@timer_working = false
@save_disabled = false
@menu_disabled = false
@encounter_disabled = false
@message_position = 2
@message_frame = 0
@save_count = 0
@magic_number = 0
@autoscroll_x_speed = 0
@autoscroll_y_speed = 0
@bgm_position = 0
@bgs_position = 0
end
################################################################################
def bgm_play(bgm)
bgm_play_internal(bgm,0)
end
def bgm_play_internal2(name,volume,pitch,position) # :nodoc:
vol = volume
vol *= $PokemonSystem.bgmvolume/100.0
vol = vol.to_i
begin
Audio.bgm_play(name,vol,pitch,position)
rescue ArgumentError
Audio.bgm_play(name,vol,pitch)
end
end
def bgm_play_internal(bgm,position) # :nodoc:
@bgm_position = position if !@bgm_paused
@playing_bgm = (bgm==nil) ? nil : bgm.clone
if bgm!=nil and bgm.name!=""
if FileTest.audio_exist?("Audio/BGM/"+bgm.name)
bgm_play_internal2("Audio/BGM/"+bgm.name,
bgm.volume,bgm.pitch,@bgm_position) if !@defaultBGM
end
else
@bgm_position = position if !@bgm_paused
@playing_bgm = nil
Audio.bgm_stop if !@defaultBGM
end
if @defaultBGM
bgm_play_internal2("Audio/BGM/"+@defaultBGM.name,
@defaultBGM.volume,@defaultBGM.pitch,@bgm_position)
end
Graphics.frame_reset
end
def bgm_pause(fadetime=0.0) # :nodoc:
pos = Audio.bgm_position rescue 0
self.bgm_fade(fadetime) if fadetime>0.0
@bgm_position = pos
@bgm_paused = true
end
def bgm_unpause # :nodoc:
@bgm_position = 0
@bgm_paused = false
end
def bgm_resume(bgm) # :nodoc:
if @bgm_paused
self.bgm_play_internal(bgm,@bgm_position)
@bgm_position = 0
@bgm_paused = false
end
end
def bgm_stop # :nodoc:
@bgm_position = 0 if !@bgm_paused
@playing_bgm = nil
Audio.bgm_stop if !@defaultBGM
end
def bgm_fade(time) # :nodoc:
@bgm_position = 0 if !@bgm_paused
@playing_bgm = nil
Audio.bgm_fade((time*1000).floor) if !@defaultBGM
end
def playing_bgm
return @playing_bgm
end
# Saves the currently playing background music for later playback.
def bgm_memorize
@memorized_bgm = @playing_bgm
end
# Plays the currently memorized background music
def bgm_restore
bgm_play(@memorized_bgm)
end
# Returns an RPG::AudioFile object for the currently playing background music
def getPlayingBGM
return (@playing_bgm) ? @playing_bgm.clone : nil
end
def setDefaultBGM(bgm,volume=80,pitch=100)
bgm = RPG::AudioFile.new(bgm,volume,pitch) if bgm.is_a?(String)
if bgm!=nil and bgm.name!=""
@defaultBGM = nil
self.bgm_play(bgm)
@defaultBGM = bgm.clone
else
@defaultBGM = nil
self.bgm_play(@playing_bgm)
end
end
################################################################################
def me_play(me)
me = RPG::AudioFile.new(me) if me.is_a?(String)
if me!=nil and me.name!=""
if FileTest.audio_exist?("Audio/ME/"+me.name)
vol = me.volume
vol *= $PokemonSystem.bgmvolume/100.0
vol = vol.to_i
Audio.me_play("Audio/ME/"+me.name,vol,me.pitch)
end
else
Audio.me_stop
end
Graphics.frame_reset
end
################################################################################
def bgs_play(bgs)
@playing_bgs = (bgs==nil) ? nil : bgs.clone
if bgs!=nil and bgs.name!=""
if FileTest.audio_exist?("Audio/BGS/"+bgs.name)
vol = bgs.volume
vol *= $PokemonSystem.sevolume/100.0
vol = vol.to_i
Audio.bgs_play("Audio/BGS/"+bgs.name,vol,bgs.pitch)
end
else
@bgs_position = 0
@playing_bgs = nil
Audio.bgs_stop
end
Graphics.frame_reset
end
def bgs_pause(fadetime=0.0) # :nodoc:
if fadetime>0.0
self.bgs_fade(fadetime)
else
self.bgs_stop
end
@bgs_paused = true
end
def bgs_unpause # :nodoc:
@bgs_paused = false
end
def bgs_resume(bgs) # :nodoc:
if @bgs_paused
self.bgs_play(bgs)
@bgs_paused = false
end
end
def bgs_stop
@bgs_position = 0
@playing_bgs = nil
Audio.bgs_stop
end
def bgs_fade(time)
@bgs_position = 0
@playing_bgs = nil
Audio.bgs_fade((time*1000).floor)
end
def playing_bgs
return @playing_bgs
end
def bgs_memorize
@memorized_bgs = @playing_bgs
end
def bgs_restore
bgs_play(@memorized_bgs)
end
def getPlayingBGS
return (@playing_bgs) ? @playing_bgs.clone : nil
end
################################################################################
def se_play(se)
se = RPG::AudioFile.new(se) if se.is_a?(String)
if se!=nil and se.name!="" && FileTest.audio_exist?("Audio/SE/"+se.name)
vol = se.volume
vol *= $PokemonSystem.sevolume/100.0
vol = vol.to_i
Audio.se_play("Audio/SE/"+se.name,vol,se.pitch)
end
end
def se_stop
Audio.se_stop
end
################################################################################
def battle_bgm
return (@battle_bgm) ? @battle_bgm : $data_system.battle_bgm
end
def battle_bgm=(battle_bgm)
@battle_bgm = battle_bgm
end
def battle_end_me
return (@battle_end_me) ? @battle_end_me : $data_system.battle_end_me
end
def battle_end_me=(battle_end_me)
@battle_end_me = battle_end_me
end
################################################################################
def windowskin_name
if @windowskin_name==nil
return $data_system.windowskin_name
else
return @windowskin_name
end
end
def windowskin_name=(windowskin_name)
@windowskin_name = windowskin_name
end
def update
@timer -= 1 if @timer_working and @timer>0
if Input.trigger?(Input::F5) && pbCurrentEventCommentInput(1,"Cut Scene")
event = @map_interpreter.get_character(0)
@map_interpreter.pbSetSelfSwitch(event.id,"A",true)
@map_interpreter.command_end
event.start
end
end
end

View File

@@ -0,0 +1,156 @@
#===============================================================================
# ** Game_Picture
#-------------------------------------------------------------------------------
# This class handles the picture. It's used within the Game_Screen class
# ($game_screen).
#===============================================================================
class Game_Picture
#-----------------------------------------------------------------------------
# * Public Instance Variables
#-----------------------------------------------------------------------------
attr_reader :number # picture number
attr_reader :name # file name
attr_reader :origin # starting point
attr_reader :x # x-coordinate
attr_reader :y # y-coordinate
attr_reader :zoom_x # x directional zoom rate
attr_reader :zoom_y # y directional zoom rate
attr_reader :opacity # opacity level
attr_reader :blend_type # blend method
attr_reader :tone # color tone
attr_reader :angle # rotation angle
#-----------------------------------------------------------------------------
# * Object Initialization
# number : picture number
#-----------------------------------------------------------------------------
def initialize(number)
@number = number
@name = ""
@origin = 0
@x = 0.0
@y = 0.0
@zoom_x = 100.0
@zoom_y = 100.0
@opacity = 255.0
@blend_type = 1
@duration = 0
@target_x = @x
@target_y = @y
@target_zoom_x = @zoom_x
@target_zoom_y = @zoom_y
@target_opacity = @opacity
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@angle = 0
@rotate_speed = 0
end
#-----------------------------------------------------------------------------
# * Show Picture
# name : file name
# origin : starting point
# x : x-coordinate
# y : y-coordinate
# zoom_x : x directional zoom rate
# zoom_y : y directional zoom rate
# opacity : opacity level
# blend_type : blend method
#-----------------------------------------------------------------------------
def show(name, origin, x, y, zoom_x, zoom_y, opacity, blend_type)
@name = name
@origin = origin
@x = x.to_f
@y = y.to_f
@zoom_x = zoom_x.to_f
@zoom_y = zoom_y.to_f
@opacity = opacity.to_f
@blend_type = blend_type ? blend_type : 0
@duration = 0
@target_x = @x
@target_y = @y
@target_zoom_x = @zoom_x
@target_zoom_y = @zoom_y
@target_opacity = @opacity
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@angle = 0
@rotate_speed = 0
end
#-----------------------------------------------------------------------------
# * Move Picture
# duration : time
# origin : starting point
# x : x-coordinate
# y : y-coordinate
# zoom_x : x directional zoom rate
# zoom_y : y directional zoom rate
# opacity : opacity level
# blend_type : blend method
#-----------------------------------------------------------------------------
def move(duration, origin, x, y, zoom_x, zoom_y, opacity, blend_type)
@duration = duration
@origin = origin
@target_x = x.to_f
@target_y = y.to_f
@target_zoom_x = zoom_x.to_f
@target_zoom_y = zoom_y.to_f
@target_opacity = opacity.to_f
@blend_type = blend_type ? blend_type : 0
end
#-----------------------------------------------------------------------------
# * Change Rotation Speed
# speed : rotation speed
#-----------------------------------------------------------------------------
def rotate(speed)
@rotate_speed = speed
end
#-----------------------------------------------------------------------------
# * Start Change of Color Tone
# tone : color tone
# duration : time
#-----------------------------------------------------------------------------
def start_tone_change(tone, duration)
@tone_target = tone.clone
@tone_duration = duration
if @tone_duration == 0
@tone = @tone_target.clone
end
end
#-----------------------------------------------------------------------------
# * Erase Picture
#-----------------------------------------------------------------------------
def erase
@name = ""
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
if @duration >= 1
d = @duration
@x = (@x * (d - 1) + @target_x) / d
@y = (@y * (d - 1) + @target_y) / d
@zoom_x = (@zoom_x * (d - 1) + @target_zoom_x) / d
@zoom_y = (@zoom_y * (d - 1) + @target_zoom_y) / d
@opacity = (@opacity * (d - 1) + @target_opacity) / d
@duration -= 1
end
if @tone_duration >= 1
d = @tone_duration
@tone.red = (@tone.red * (d - 1) + @tone_target.red) / d
@tone.green = (@tone.green * (d - 1) + @tone_target.green) / d
@tone.blue = (@tone.blue * (d - 1) + @tone_target.blue) / d
@tone.gray = (@tone.gray * (d - 1) + @tone_target.gray) / d
@tone_duration -= 1
end
if @rotate_speed != 0
@angle += @rotate_speed / 2.0
while @angle < 0
@angle += 360
end
@angle %= 360
end
end
end

View File

@@ -0,0 +1,81 @@
#===============================================================================
# ** Game_CommonEvent
#-------------------------------------------------------------------------------
# This class handles common events. It includes execution of parallel process
# event. This class is used within the Game_Map class ($game_map).
#===============================================================================
class Game_CommonEvent
#-----------------------------------------------------------------------------
# * Object Initialization
# common_event_id : common event ID
#-----------------------------------------------------------------------------
def initialize(common_event_id)
@common_event_id = common_event_id
@interpreter = nil
refresh
end
#-----------------------------------------------------------------------------
# * Get Name
#-----------------------------------------------------------------------------
def name
return $data_common_events[@common_event_id].name
end
#-----------------------------------------------------------------------------
# * Get Trigger
#-----------------------------------------------------------------------------
def trigger
return $data_common_events[@common_event_id].trigger
end
#-----------------------------------------------------------------------------
# * Get Condition Switch ID
#-----------------------------------------------------------------------------
def switch_id
return $data_common_events[@common_event_id].switch_id
end
#-----------------------------------------------------------------------------
# * Get List of Event Commands
#-----------------------------------------------------------------------------
def list
return $data_common_events[@common_event_id].list
end
#-----------------------------------------------------------------------------
# * Checks if switch is on
#-----------------------------------------------------------------------------
def switchIsOn?(id)
switchName = $data_system.switches[id]
return false if !switchName
if switchName[/^s\:/]
return eval($~.post_match)
else
return $game_switches[id]
end
end
#-----------------------------------------------------------------------------
# * Refresh
#-----------------------------------------------------------------------------
def refresh
# Create an interpreter for parallel process if necessary
if self.trigger == 2 and switchIsOn?(self.switch_id)
if @interpreter == nil
@interpreter = Interpreter.new
end
else
@interpreter = nil
end
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
# If parallel process is valid
if @interpreter != nil
# If not running
unless @interpreter.running?
# Set up event
@interpreter.setup(self.list, 0)
end
# Update interpreter
@interpreter.update
end
end
end

View File

@@ -0,0 +1,869 @@
class Game_Character
attr_reader :id
attr_reader :original_x
attr_reader :original_y
attr_reader :x
attr_reader :y
attr_reader :real_x
attr_reader :real_y
attr_accessor :sprite_size
attr_reader :tile_id
attr_accessor :character_name
attr_accessor :character_hue
attr_reader :opacity
attr_reader :blend_type
attr_reader :direction
attr_accessor :pattern
attr_reader :pattern_surf
attr_accessor :lock_pattern
attr_reader :move_route_forcing
attr_accessor :through
attr_accessor :animation_id
attr_accessor :transparent
attr_reader :move_speed
attr_accessor :walk_anime
attr_accessor :bob_height
def initialize(map=nil)
@map = map
@id = 0
@original_x = 0
@original_y = 0
@x = 0
@y = 0
@real_x = 0
@real_y = 0
@sprite_size = [Game_Map::TILE_WIDTH,Game_Map::TILE_HEIGHT]
@tile_id = 0
@character_name = ""
@character_hue = 0
@opacity = 255
@blend_type = 0
@direction = 2
@pattern = 0
@pattern_surf = 0
@lock_pattern = false
@move_route_forcing = false
@through = false
@animation_id = 0
@transparent = false
@original_direction = 2
@original_pattern = 0
@move_type = 0
self.move_speed = 4
self.move_frequency = 6
@move_route = nil
@move_route_index = 0
@original_move_route = nil
@original_move_route_index = 0
@walk_anime = true # Whether character should animate while moving
@step_anime = false # Whether character should animate while still
@direction_fix = false
@always_on_top = false
@anime_count = 0
@stop_count = 0
@jump_count = 0
@jump_peak = 0
@bob_height = 0
@wait_count = 0
@moved_this_frame = false
@locked = false
@prelock_direction = 0
end
def move_speed=(val)
return if val==@move_speed
@move_speed = val
# @move_speed_real is the number of quarter-pixels to move each frame. There
# are 128 quarter-pixels per tile. By default, it is calculated from
# @move_speed and has these values (assuming 40 fps):
# 1 => 1.6 # 80 frames per tile
# 2 => 3.2 # 40 frames per tile
# 3 => 6.4 # 20 frames per tile
# 4 => 12.8 # 10 frames per tile - walking speed
# 5 => 25.6 # 5 frames per tile - running speed (2x walking speed)
# 6 => 32 # 4 frames per tile - cycling speed (1.25x running speed)
self.move_speed_real = (val == 6) ? 32 : (1 << val) * 0.8
end
def move_speed_real
self.move_speed = @move_speed if !@move_speed_real
return @move_speed_real
end
def move_speed_real=(val)
@move_speed_real = val * 40.0 / Graphics.frame_rate
end
def move_frequency=(val)
return if val==@move_frequency
@move_frequency = val
# @move_frequency_real is the number of frames to wait between each action
# in a move route (not forced). Specifically, this is the number of frames
# to wait after the character stops moving because of the previous action.
# By default, it is calculated from @move_frequency and has these values
# (assuming 40 fps):
# 1 => 190 # 4.75 seconds
# 2 => 144 # 3.6 seconds
# 3 => 102 # 2.55 seconds
# 4 => 64 # 1.6 seconds
# 5 => 30 # 0.75 seconds
# 6 => 0 # 0 seconds, i.e. continuous movement
self.move_frequency_real = (40 - val * 2) * (6 - val)
end
def move_frequency_real
self.move_frequency = @move_frequency if !@move_frequency_real
return @move_frequency_real
end
def move_frequency_real=(val)
@move_frequency_real = val * Graphics.frame_rate / 40.0
end
def bob_height
@bob_height = 0 if !@bob_height
return @bob_height
end
def lock
return if @locked
@prelock_direction = 0 # Was @direction but disabled
turn_toward_player
@locked = true
end
def minilock
@prelock_direction = 0 # Was @direction but disabled
@locked = true
end
def lock?
return @locked
end
def unlock
return unless @locked
@locked = false
@direction = @prelock_direction if !@direction_fix and @prelock_direction != 0
end
#=============================================================================
# Information from map data
#=============================================================================
def map
return (@map) ? @map : $game_map
end
def terrain_tag
return self.map.terrain_tag(@x, @y)
end
def bush_depth
return 0 if @tile_id > 0 or @always_on_top or @jump_count > 0
xbehind = @x + (@direction==4 ? 1 : @direction==6 ? -1 : 0)
ybehind = @y + (@direction==8 ? 1 : @direction==2 ? -1 : 0)
return Game_Map::TILE_HEIGHT if self.map.deepBush?(@x, @y) and self.map.deepBush?(xbehind, ybehind)
return 12 if !moving? and self.map.bush?(@x, @y)
return 0
end
#=============================================================================
# Passability
#=============================================================================
def passableEx?(x, y, d, strict=false)
new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0)
new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0)
return false unless self.map.valid?(new_x, new_y)
return true if @through
if strict
return false unless self.map.passableStrict?(x, y, d, self)
return false unless self.map.passableStrict?(new_x, new_y, 10 - d, self)
else
return false unless self.map.passable?(x, y, d, self)
return false unless self.map.passable?(new_x, new_y, 10 - d, self)
end
for event in self.map.events.values
next if event.x != new_x || event.y != new_y || event.through
return false if self != $game_player || event.character_name != ""
end
if $game_player.x == new_x and $game_player.y == new_y
return false if !$game_player.through && @character_name != ""
end
return true
end
def passable?(x,y,d)
return passableEx?(x,y,d,false)
end
def passableStrict?(x,y,d)
return passableEx?(x,y,d,true)
end
#=============================================================================
# Screen position of the character
#=============================================================================
def screen_x
ret = ((@real_x - self.map.display_x) / Game_Map::X_SUBPIXELS).round
ret += Game_Map::TILE_WIDTH/2
return ret
end
def screen_y_ground
ret = ((@real_y - self.map.display_y) / Game_Map::Y_SUBPIXELS).round
ret += Game_Map::TILE_HEIGHT
return ret
end
def screen_y
ret = screen_y_ground
if jumping?
n = ((2 * @jump_count * 20 / Graphics.frame_rate) - @jump_peak).abs
return ret - (@jump_peak * @jump_peak - n * n) / 2
end
return ret
end
def screen_z(height = 0)
return 999 if @always_on_top
z = screen_y_ground
if @tile_id > 0
begin
return z + self.map.priorities[@tile_id] * 32
rescue
raise "Event's graphic is an out-of-range tile (event #{@id}, map #{self.map.map_id})"
end
end
# Add z if height exceeds 32
return z + ((height > Game_Map::TILE_HEIGHT) ? Game_Map::TILE_HEIGHT - 1 : 0)
end
#=============================================================================
# Movement
#=============================================================================
def moving?
return @real_x != @x * Game_Map::REAL_RES_X ||
@real_y != @y * Game_Map::REAL_RES_Y
end
def jumping?
return @jump_count > 0
end
def straighten
@pattern = 0 if @walk_anime or @step_anime
@anime_count = 0
@prelock_direction = 0
end
def force_move_route(move_route)
if @original_move_route == nil
@original_move_route = @move_route
@original_move_route_index = @move_route_index
end
@move_route = move_route
@move_route_index = 0
@move_route_forcing = true
@prelock_direction = 0
@wait_count = 0
move_type_custom
end
def moveto(x, y)
@x = x % self.map.width
@y = y % self.map.height
@real_x = @x * Game_Map::REAL_RES_X
@real_y = @y * Game_Map::REAL_RES_Y
@prelock_direction = 0
triggerLeaveTile
end
def triggerLeaveTile
if @oldX && @oldY && @oldMap &&
(@oldX!=self.x || @oldY!=self.y || @oldMap!=self.map.map_id)
Events.onLeaveTile.trigger(self,self,@oldMap,@oldX,@oldY)
end
@oldX = self.x
@oldY = self.y
@oldMap = self.map.map_id
end
def increase_steps
@stop_count = 0
triggerLeaveTile
end
#=============================================================================
# Movement commands
#=============================================================================
def move_type_random
case rand(6)
when 0..3; move_random
when 4; move_forward
when 5; @stop_count = 0
end
end
def move_type_toward_player
sx = @x - $game_player.x
sy = @y - $game_player.y
if sx.abs + sy.abs >= 20
move_random
return
end
case rand(6)
when 0..3; move_toward_player
when 4; move_random
when 5; move_forward
end
end
def move_type_custom
return if jumping? or moving?
while @move_route_index < @move_route.list.size
command = @move_route.list[@move_route_index]
if command.code == 0
if @move_route.repeat
@move_route_index = 0
else
if @move_route_forcing
@move_route_forcing = false
@move_route = @original_move_route
@move_route_index = @original_move_route_index
@original_move_route = nil
end
@stop_count = 0
end
return
end
if command.code <= 14
case command.code
when 1; move_down
when 2; move_left
when 3; move_right
when 4; move_up
when 5; move_lower_left
when 6; move_lower_right
when 7; move_upper_left
when 8; move_upper_right
when 9; move_random
when 10; move_toward_player
when 11; move_away_from_player
when 12; move_forward
when 13; move_backward
when 14; jump(command.parameters[0], command.parameters[1])
end
@move_route_index += 1 if @move_route.skippable or moving? or jumping?
return
end
if command.code == 15 # Wait
@wait_count = (command.parameters[0] * Graphics.frame_rate / 20) - 1
@move_route_index += 1
return
end
if command.code >= 16 and command.code <= 26
case command.code
when 16; turn_down
when 17; turn_left
when 18; turn_right
when 19; turn_up
when 20; turn_right_90
when 21; turn_left_90
when 22; turn_180
when 23; turn_right_or_left_90
when 24; turn_random
when 25; turn_toward_player
when 26; turn_away_from_player
end
@move_route_index += 1
return
end
if command.code >= 27
case command.code
when 27
$game_switches[command.parameters[0]] = true
self.map.need_refresh = true
when 28
$game_switches[command.parameters[0]] = false
self.map.need_refresh = true
when 29; self.move_speed = command.parameters[0]
when 30; self.move_frequency = command.parameters[0]
when 31; @walk_anime = true
when 32; @walk_anime = false
when 33; @step_anime = true
when 34; @step_anime = false
when 35; @direction_fix = true
when 36; @direction_fix = false
when 37; @through = true
when 38; @through = false
when 39; @always_on_top = true
when 40; @always_on_top = false
when 41
@tile_id = 0
@character_name = command.parameters[0]
@character_hue = command.parameters[1]
if @original_direction != command.parameters[2]
@direction = command.parameters[2]
@original_direction = @direction
@prelock_direction = 0
end
if @original_pattern != command.parameters[3]
@pattern = command.parameters[3]
@original_pattern = @pattern
end
when 42; @opacity = command.parameters[0]
when 43; @blend_type = command.parameters[0]
when 44; pbSEPlay(command.parameters[0])
when 45; result = eval(command.parameters[0])
end
@move_route_index += 1
end
end
end
def move_up(turn_enabled = true)
turn_up if turn_enabled
if passable?(@x, @y, 8)
turn_up
@y -= 1
increase_steps
else
check_event_trigger_touch(@x, @y-1)
end
end
def move_down(turn_enabled = true)
turn_down if turn_enabled
if passable?(@x, @y, 2)
turn_down
@y += 1
increase_steps
else
check_event_trigger_touch(@x, @y+1)
end
end
def move_left(turn_enabled = true)
turn_left if turn_enabled
if passable?(@x, @y, 4)
turn_left
@x -= 1
increase_steps
else
check_event_trigger_touch(@x-1, @y)
end
end
def move_right(turn_enabled = true)
turn_right if turn_enabled
if passable?(@x, @y, 6)
turn_right
@x += 1
increase_steps
else
check_event_trigger_touch(@x+1, @y)
end
end
def move_upper_left
unless @direction_fix
@direction = (@direction == 6 ? 4 : @direction == 2 ? 8 : @direction)
end
if (passable?(@x, @y, 8) and passable?(@x, @y - 1, 4)) or
(passable?(@x, @y, 4) and passable?(@x - 1, @y, 8))
@x -= 1
@y -= 1
increase_steps
end
end
def move_upper_right
unless @direction_fix
@direction = (@direction == 4 ? 6 : @direction == 2 ? 8 : @direction)
end
if (passable?(@x, @y, 8) and passable?(@x, @y - 1, 6)) or
(passable?(@x, @y, 6) and passable?(@x + 1, @y, 8))
@x += 1
@y -= 1
increase_steps
end
end
def move_lower_left
unless @direction_fix
@direction = (@direction == 6 ? 4 : @direction == 8 ? 2 : @direction)
end
if (passable?(@x, @y, 2) and passable?(@x, @y + 1, 4)) or
(passable?(@x, @y, 4) and passable?(@x - 1, @y, 2))
@x -= 1
@y += 1
increase_steps
end
end
def move_lower_right
unless @direction_fix
@direction = (@direction == 4 ? 6 : @direction == 8 ? 2 : @direction)
end
if (passable?(@x, @y, 2) and passable?(@x, @y + 1, 6)) or
(passable?(@x, @y, 6) and passable?(@x + 1, @y, 2))
@x += 1
@y += 1
increase_steps
end
end
def moveLeft90 # anticlockwise
case self.direction
when 2; move_right # down
when 4; move_down # left
when 6; move_up # right
when 8; move_left # up
end
end
def moveRight90 # clockwise
case self.direction
when 2; move_left # down
when 4; move_up # left
when 6; move_down # right
when 8; move_right # up
end
end
def move_random
case rand(4)
when 0; move_down(false)
when 1; move_left(false)
when 2; move_right(false)
when 3; move_up(false)
end
end
def move_random_range(xrange=-1,yrange=-1)
dirs = [] # 0=down, 1=left, 2=right, 3=up
if xrange<0
dirs.push(1); dirs.push(2)
elsif xrange>0
dirs.push(1) if @x > @original_x - xrange
dirs.push(2) if @x < @original_x + xrange
end
if yrange<0
dirs.push(0); dirs.push(3)
elsif yrange>0
dirs.push(0) if @y < @original_y + yrange
dirs.push(3) if @y > @original_y - yrange
end
return if dirs.length==0
case dirs[rand(dirs.length)]
when 0; move_down(false)
when 1; move_left(false)
when 2; move_right(false)
when 3; move_up(false)
end
end
def move_random_UD(range=-1)
move_random_range(0,range)
end
def move_random_LR(range=-1)
move_random_range(range,0)
end
def move_toward_player
sx = @x - $game_player.x
sy = @y - $game_player.y
return if sx == 0 and sy == 0
abs_sx = sx.abs
abs_sy = sy.abs
if abs_sx == abs_sy
(rand(2) == 0) ? abs_sx += 1 : abs_sy += 1
end
if abs_sx > abs_sy
(sx > 0) ? move_left : move_right
if not moving? and sy != 0
(sy > 0) ? move_up : move_down
end
else
(sy > 0) ? move_up : move_down
if not moving? and sx != 0
(sx > 0) ? move_left : move_right
end
end
end
def move_away_from_player
sx = @x - $game_player.x
sy = @y - $game_player.y
return if sx == 0 and sy == 0
abs_sx = sx.abs
abs_sy = sy.abs
if abs_sx == abs_sy
(rand(2) == 0) ? abs_sx += 1 : abs_sy += 1
end
if abs_sx > abs_sy
(sx > 0) ? move_right : move_left
if not moving? and sy != 0
(sy > 0) ? move_down : move_up
end
else
(sy > 0) ? move_down : move_up
if not moving? and sx != 0
(sx > 0) ? move_right : move_left
end
end
end
def move_forward
case @direction
when 2; move_down(false)
when 4; move_left(false)
when 6; move_right(false)
when 8; move_up(false)
end
end
def move_backward
last_direction_fix = @direction_fix
@direction_fix = true
case @direction
when 2; move_up(false)
when 4; move_right(false)
when 6; move_left(false)
when 8; move_down(false)
end
@direction_fix = last_direction_fix
end
def jump(x_plus, y_plus)
if x_plus != 0 or y_plus != 0
if x_plus.abs > y_plus.abs
(x_plus < 0) ? turn_left : turn_right
else
(y_plus < 0) ? turn_up : turn_down
end
end
new_x = @x + x_plus
new_y = @y + y_plus
if (x_plus == 0 and y_plus == 0) || passable?(new_x, new_y, 0)
straighten
@x = new_x
@y = new_y
distance = [4, x_plus * x_plus + y_plus * y_plus].max
@jump_peak = (6 + distance - move_speed).floor
@jump_count = @jump_peak * Graphics.frame_rate / 20
@stop_count = 0
if self.is_a?(Game_Player)
$PokemonTemp.dependentEvents.pbMoveDependentEvents
end
triggerLeaveTile
end
end
def jumpForward
case self.direction
when 2; jump(0,1) # down
when 4; jump(-1,0) # left
when 6; jump(1,0) # right
when 8; jump(0,-1) # up
end
end
def jumpBackward
case self.direction
when 2; jump(0,-1) # down
when 4; jump(1,0) # left
when 6; jump(-1,0) # right
when 8; jump(0,1) # up
end
end
def turnGeneric(dir)
return if @direction_fix
oldDirection = @direction
@direction = dir
@stop_count = 0
pbCheckEventTriggerAfterTurning if dir != oldDirection
end
def turn_up; turnGeneric(8); end
def turn_down; turnGeneric(2); end
def turn_left; turnGeneric(4); end
def turn_right; turnGeneric(6); end
def turn_right_90
case @direction
when 2; turn_left
when 4; turn_up
when 6; turn_down
when 8; turn_right
end
end
def turn_left_90
case @direction
when 2; turn_right
when 4; turn_down
when 6; turn_up
when 8; turn_left
end
end
def turn_180
case @direction
when 2; turn_up
when 4; turn_right
when 6; turn_left
when 8; turn_down
end
end
def turn_right_or_left_90
(rand(2) == 0) ? turn_right_90 : turn_left_90
end
def turn_random
case rand(4)
when 0; turn_up
when 1; turn_right
when 2; turn_left
when 3; turn_down
end
end
def turn_toward_player
sx = @x - $game_player.x
sy = @y - $game_player.y
return if sx == 0 and sy == 0
if sx.abs > sy.abs
(sx > 0) ? turn_left : turn_right
else
(sy > 0) ? turn_up : turn_down
end
end
def turn_away_from_player
sx = @x - $game_player.x
sy = @y - $game_player.y
return if sx == 0 and sy == 0
if sx.abs > sy.abs
(sx > 0) ? turn_right : turn_left
else
(sy > 0) ? turn_down : turn_up
end
end
#=============================================================================
# Updating
#=============================================================================
def update
@moved_last_frame = @moved_this_frame
if !$game_temp.in_menu
# Update command
update_command
# Update movement
if jumping?; update_jump
elsif moving?; update_move
else; update_stop
end
end
# Update animation
update_pattern
end
def update_command
if @wait_count > 0
@wait_count -= 1
elsif @move_route_forcing
move_type_custom
elsif !@starting && !lock? && !moving? && !jumping?
update_command_new
end
end
def update_command_new
# @stop_count is the number of frames since the last movement finished.
# @move_frequency has these values:
# 1 => @stop_count > 190 # 4.75 seconds
# 2 => @stop_count > 144 # 3.6 seconds
# 3 => @stop_count > 102 # 2.55 seconds
# 4 => @stop_count > 64 # 1.6 seconds
# 5 => @stop_count > 30 # 0.75 seconds
# 6 => @stop_count > 0 # 0 seconds
if @stop_count >= self.move_frequency_real
case @move_type
when 1; move_type_random
when 2; move_type_toward_player
when 3; move_type_custom
end
end
end
def update_jump
@jump_count -= 1
@real_x = (@real_x * @jump_count + @x * Game_Map::REAL_RES_X) / (@jump_count + 1)
@real_y = (@real_y * @jump_count + @y * Game_Map::REAL_RES_Y) / (@jump_count + 1)
@moved_this_frame = true
# End of a jump, so perform events that happen at this time
Events.onStepTakenFieldMovement.trigger(self,self) if !jumping? && !moving?
end
def update_move
# Move the character (the 0.1 catches rounding errors)
distance = move_speed_real
dest_x = @x * Game_Map::REAL_RES_X
dest_y = @y * Game_Map::REAL_RES_Y
if @real_x < dest_x
@real_x += distance
@real_x = dest_x if @real_x > dest_x - 0.1
else
@real_x -= distance
@real_x = dest_x if @real_x < dest_x + 0.1
end
if @real_y < dest_y
@real_y += distance
@real_y = dest_y if @real_y > dest_y - 0.1
else
@real_y -= distance
@real_y = dest_y if @real_y < dest_y + 0.1
end
# End of a step, so perform events that happen at this time
Events.onStepTakenFieldMovement.trigger(self,self) if !jumping? && !moving?
# Increment animation counter
@anime_count += 1 if @walk_anime || @step_anime
@moved_this_frame = true
end
def update_stop
@anime_count += 1 if @step_anime
@stop_count += 1 if !@starting && !lock?
@moved_this_frame = false
end
def update_pattern
return if @lock_pattern
# Character has stopped moving, return to original pattern
if @moved_last_frame && !@moved_this_frame && !@step_anime
@pattern = @original_pattern
@anime_count = 0
return
end
# Character has started to move, change pattern immediately
if !@moved_last_frame && @moved_this_frame && !jumping? && !@step_anime
@pattern = (@pattern + 1) % 4 if @walk_anime
@anime_count = 0
return
end
# Calculate how many frames each pattern should display for, i.e. the time
# it takes to move half a tile (or a whole tile if cycling). We assume the
# game uses square tiles.
frames_per_pattern = Game_Map::REAL_RES_X / (move_speed_real * 2.0)
frames_per_pattern *= 2 if move_speed == 6 # Cycling/fastest speed
return if @anime_count < frames_per_pattern
# Advance to the next animation frame
@pattern = (@pattern + 1) % 4
@anime_count -= frames_per_pattern
end
end

View File

@@ -0,0 +1,247 @@
class Game_Event < Game_Character
attr_reader :map_id
attr_reader :trigger
attr_reader :list
attr_reader :starting
attr_reader :tempSwitches # Temporary self-switches
attr_accessor :need_refresh
def initialize(map_id, event, map=nil)
super(map)
@map_id = map_id
@event = event
@id = @event.id
@original_x = @event.x
@original_y = @event.y
@erased = false
@starting = false
@need_refresh = false
@route_erased = false
@through = true
@to_update = true
@tempSwitches = {}
moveto(@event.x, @event.y) if map
refresh
end
def id; return @event.id; end
def name; return @event.name; end
def clear_starting
@starting = false
end
def start
@starting = true if @list.size > 1
end
def erase
@erased = true
refresh
end
def erase_route
@route_erased = true
refresh
end
def tsOn?(c)
return @tempSwitches && @tempSwitches[c]==true
end
def tsOff?(c)
return !@tempSwitches || !@tempSwitches[c]
end
def setTempSwitchOn(c)
@tempSwitches[c]=true
refresh
end
def setTempSwitchOff(c)
@tempSwitches[c]=false
refresh
end
def isOff?(c)
return !$game_self_switches[[@map_id,@event.id,c]]
end
def switchIsOn?(id)
switchname = $data_system.switches[id]
return false if !switchname
if switchname[/^s\:/]
return eval($~.post_match)
else
return $game_switches[id]
end
end
def variable
return nil if !$PokemonGlobal.eventvars
return $PokemonGlobal.eventvars[[@map_id,@event.id]]
end
def setVariable(variable)
$PokemonGlobal.eventvars[[@map_id,@event.id]]=variable
end
def varAsInt
return 0 if !$PokemonGlobal.eventvars
return $PokemonGlobal.eventvars[[@map_id,@event.id]].to_i
end
def expired?(secs=86400)
ontime=self.variable
time=pbGetTimeNow
return ontime && (time.to_i>ontime+secs)
end
def expiredDays?(days=1)
ontime=self.variable.to_i
return false if !ontime
now=pbGetTimeNow
elapsed=(now.to_i-ontime)/86400
elapsed+=1 if (now.to_i-ontime)%86400>(now.hour*3600+now.min*60+now.sec)
return elapsed>=days
end
def onEvent?
return @map_id==$game_map.map_id &&
$game_player.x==self.x && $game_player.y==self.y
end
def over_trigger?
return false if @character_name!="" and not @through
return false if @event.name[/hiddenitem/i]
return false if !self.map.passable?(@x, @y, 0, $game_player)
return true
end
def pbCheckEventTriggerAfterTurning
return if $game_system.map_interpreter.running? || @starting
if @event.name[/trainer\((\d+)\)/i]
distance = $~[1].to_i
if @trigger==2 && pbEventCanReachPlayer?(self,$game_player,distance)
start if !jumping? && !over_trigger?
end
end
end
def check_event_trigger_touch(x, y)
return if $game_system.map_interpreter.running?
return if @trigger!=2
return if x != $game_player.x || y != $game_player.y
return if jumping? || over_trigger?
start
end
def check_event_trigger_auto
if @trigger == 2 # Event touch
if @x == $game_player.x and @y == $game_player.y
start if not jumping? and over_trigger?
end
elsif @trigger == 3 # Autorun
start
end
end
def refresh
new_page = nil
unless @erased
for page in @event.pages.reverse
c = page.condition
next if c.switch1_valid && !switchIsOn?(c.switch1_id)
next if c.switch2_valid && !switchIsOn?(c.switch2_id)
next if c.variable_valid && $game_variables[c.variable_id] < c.variable_value
if c.self_switch_valid
key = [@map_id, @event.id, c.self_switch_ch]
next if $game_self_switches[key] != true
end
new_page = page
break
end
end
return if new_page == @page
@page = new_page
clear_starting
if @page == nil
@tile_id = 0
@character_name = ""
@character_hue = 0
@move_type = 0
@through = true
@trigger = nil
@list = nil
@interpreter = nil
return
end
@tile_id = @page.graphic.tile_id
@character_name = @page.graphic.character_name
@character_hue = @page.graphic.character_hue
if @original_direction != @page.graphic.direction
@direction = @page.graphic.direction
@original_direction = @direction
@prelock_direction = 0
end
if @original_pattern != @page.graphic.pattern
@pattern = @page.graphic.pattern
@original_pattern = @pattern
end
@opacity = @page.graphic.opacity
@blend_type = @page.graphic.blend_type
@move_type = @page.move_type
self.move_speed = @page.move_speed
self.move_frequency = @page.move_frequency
@move_route = (@route_erased) ? RPG::MoveRoute.new : @page.move_route
@move_route_index = 0
@move_route_forcing = false
@walk_anime = @page.walk_anime
@step_anime = @page.step_anime
@direction_fix = @page.direction_fix
@through = @page.through
@always_on_top = @page.always_on_top
@trigger = @page.trigger
@list = @page.list
@interpreter = nil
if @trigger == 4 # Parallel Process
@interpreter = Interpreter.new
end
check_event_trigger_auto
end
def should_update?(recalc=false)
return @to_update if !recalc
return true if $PokemonSystem.tilemap==2
return true if @trigger && (@trigger == 3 || @trigger == 4)
return true if @move_route_forcing
return true if @event.name[/update/i]
range = 2 # Number of tiles
return false if self.screen_x - @sprite_size[0]/2 > Graphics.width + range * Game_Map::TILE_WIDTH
return false if self.screen_x + @sprite_size[0]/2 < -range * Game_Map::TILE_WIDTH
return false if self.screen_y_ground - @sprite_size[1] > Graphics.height + range * Game_Map::TILE_HEIGHT
return false if self.screen_y_ground < -range * Game_Map::TILE_HEIGHT
return true
end
def update
@to_update = should_update?(true)
return if !@to_update
last_moving = moving?
super
if !moving? && last_moving
$game_player.pbCheckEventTriggerFromDistance([2])
end
if @need_refresh
@need_refresh = false
refresh
end
check_event_trigger_auto
if @interpreter != nil
unless @interpreter.running?
@interpreter.setup(@list, @event.id, @map_id)
end
@interpreter.update
end
end
end

View File

@@ -0,0 +1,505 @@
#===============================================================================
# ** Game_Player
#-------------------------------------------------------------------------------
# This class handles the player. Its functions include event starting
# determinants and map scrolling. Refer to "$game_player" for the one
# instance of this class.
#===============================================================================
class Game_Player < Game_Character
attr_accessor :bump_se
attr_accessor :charsetData
attr_accessor :encounter_count
def initialize(*arg)
super(*arg)
@lastdir=0
@lastdirframe=0
@bump_se=0
end
def map
@map = nil
return $game_map
end
def bush_depth
return 0 if @tile_id > 0 or @always_on_top
xbehind = (@direction==4) ? @x+1 : (@direction==6) ? @x-1 : @x
ybehind = (@direction==8) ? @y+1 : (@direction==2) ? @y-1 : @y
# Both current tile and previous tile are on the same map; just return super
return super if $game_map.valid?(@x,@y) && $game_map.valid?(xbehind,ybehind)
# The current or the previous tile is on a different map; consult MapFactory
return 0 if !$MapFactory
# Get map and coordinates of the current tile
if $game_map.valid?(@x,@y)
heremap = self.map; herex = @x; herey = @y
else
newhere = $MapFactory.getNewMap(@x,@y)
return 0 unless newhere && newhere[0] # Map not found
heremap = newhere[0]; herex = newhere[1]; herey = newhere[2]
end
# Get map and coordinates of the previous tile
newbehind = $MapFactory.getNewMap(xbehind,ybehind)
if $game_map.valid?(xbehind,ybehind)
behindmap = self.map; behindx = xbehind; behindy = ybehind
else
return 0 unless newbehind && newbehind[0] # Map not found
behindmap = newbehind[0]; behindx = newbehind[1]; behindy = newbehind[2]
end
# Return bush depth
if @jump_count <= 0
return 32 if heremap.deepBush?(herex, herey) && behindmap.deepBush?(behindx, behindy)
return 12 if heremap.bush?(herex, herey) && !moving?
end
return 0
end
def pbHasDependentEvents?
return $PokemonGlobal.dependentEvents.length>0
end
def bump_into_object
return if @bump_se && @bump_se>0
pbSEPlay("Player bump")
@bump_se = Graphics.frame_rate/4
end
def move_down(turn_enabled = true)
turn_down if turn_enabled
if passable?(@x, @y, 2)
return if pbLedge(0,1)
return if pbEndSurf(0,1)
turn_down
@y += 1
$PokemonTemp.dependentEvents.pbMoveDependentEvents
increase_steps
else
if !check_event_trigger_touch(@x, @y+1)
bump_into_object
end
end
end
def move_left(turn_enabled = true)
turn_left if turn_enabled
if passable?(@x, @y, 4)
return if pbLedge(-1,0)
return if pbEndSurf(-1,0)
turn_left
@x -= 1
$PokemonTemp.dependentEvents.pbMoveDependentEvents
increase_steps
else
if !check_event_trigger_touch(@x-1, @y)
bump_into_object
end
end
end
def move_right(turn_enabled = true)
turn_right if turn_enabled
if passable?(@x, @y, 6)
return if pbLedge(1,0)
return if pbEndSurf(1,0)
turn_right
@x += 1
$PokemonTemp.dependentEvents.pbMoveDependentEvents
increase_steps
else
if !check_event_trigger_touch(@x+1, @y)
bump_into_object
end
end
end
def move_up(turn_enabled = true)
turn_up if turn_enabled
if passable?(@x, @y, 8)
return if pbLedge(0,-1)
return if pbEndSurf(0,-1)
turn_up
@y -= 1
$PokemonTemp.dependentEvents.pbMoveDependentEvents
increase_steps
else
if !check_event_trigger_touch(@x, @y-1)
bump_into_object
end
end
end
def pbTriggeredTrainerEvents(triggers,checkIfRunning=true)
result = []
# If event is running
return result if checkIfRunning && $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
next if !event.name[/trainer\((\d+)\)/i]
distance = $~[1].to_i
# If event coordinates and triggers are consistent
if pbEventCanReachPlayer?(event,self,distance) and triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
result.push(event) if not event.jumping? and not event.over_trigger?
end
end
return result
end
def pbTriggeredCounterEvents(triggers,checkIfRunning=true)
result = []
# If event is running
return result if checkIfRunning && $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
next if !event.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
# If event coordinates and triggers are consistent
if pbEventFacesPlayer?(event,self,distance) and triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
result.push(event) if not event.jumping? and not event.over_trigger?
end
end
return result
end
def pbCheckEventTriggerAfterTurning
end
def pbCheckEventTriggerFromDistance(triggers)
ret = pbTriggeredTrainerEvents(triggers)
ret.concat(pbTriggeredCounterEvents(triggers))
return false if ret.length==0
for event in ret
event.start
end
return true
end
def pbFacingEvent(ignoreInterpreter=false)
return nil if $game_system.map_interpreter.running? && !ignoreInterpreter
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return nil if !$game_map.valid?(new_x, new_y)
for event in $game_map.events.values
next if event.x != new_x || event.y != new_y
next if event.jumping? || event.over_trigger?
return event
end
if $game_map.counter?(new_x, new_y)
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
for event in $game_map.events.values
next if event.x != new_x || event.y != new_y
next if event.jumping? || event.over_trigger?
return event
end
end
return nil
end
#-----------------------------------------------------------------------------
# * Passable Determinants
# x : x-coordinate
# y : y-coordinate
# d : direction (0,2,4,6,8)
# * 0 = Determines if all directions are impassable (for jumping)
#-----------------------------------------------------------------------------
def passable?(x, y, d)
# Get new coordinates
new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0)
new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0)
# If coordinates are outside of map
return false if !$game_map.validLax?(new_x, new_y)
if !$game_map.valid?(new_x, new_y)
return false if !$MapFactory
return $MapFactory.isPassableFromEdge?(new_x, new_y)
end
# If debug mode is ON and Ctrl key was pressed
return true if $DEBUG and Input.press?(Input::CTRL)
return super
end
#-----------------------------------------------------------------------------
# * Set Map Display Position to Center of Screen
#-----------------------------------------------------------------------------
def center(x, y)
center_x = (Graphics.width/2 - Game_Map::TILE_WIDTH/2) * Game_Map::X_SUBPIXELS
center_y = (Graphics.height/2 - Game_Map::TILE_HEIGHT/2) * Game_Map::Y_SUBPIXELS
max_x = (self.map.width - Graphics.width*1.0/Game_Map::TILE_WIDTH) * Game_Map::REAL_RES_X
max_y = (self.map.height - Graphics.height*1.0/Game_Map::TILE_HEIGHT) * Game_Map::REAL_RES_Y
dispx = x * Game_Map::REAL_RES_X - center_x
dispy = y * Game_Map::REAL_RES_Y - center_y
self.map.display_x = dispx
self.map.display_y = dispy
end
#-----------------------------------------------------------------------------
# * Move to Designated Position
# x : x-coordinate
# y : y-coordinate
#-----------------------------------------------------------------------------
def moveto(x, y)
super
# Centering
center(x, y)
# Make encounter count
make_encounter_count
end
#-----------------------------------------------------------------------------
# * Make Encounter Count
#-----------------------------------------------------------------------------
def make_encounter_count
# Image of two dice rolling
if $game_map.map_id != 0
n = $game_map.encounter_step
@encounter_count = rand(n) + rand(n) + 1
end
end
#-----------------------------------------------------------------------------
# * Refresh
#-----------------------------------------------------------------------------
def refresh
@opacity = 255
@blend_type = 0
end
#-----------------------------------------------------------------------------
# * Same Position Starting Determinant
#-----------------------------------------------------------------------------
def check_event_trigger_here(triggers)
result = false
# If event is running
return result if $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
# If event coordinates and triggers are consistent
next if event.x != @x || event.y != @y
next if !triggers.include?(event.trigger)
# If starting determinant is same position event (other than jumping)
next if event.jumping? || !event.over_trigger?
event.start
result = true
end
return result
end
#-----------------------------------------------------------------------------
# * Front Event Starting Determinant
#-----------------------------------------------------------------------------
def check_event_trigger_there(triggers)
result = false
# If event is running
return result if $game_system.map_interpreter.running?
# Calculate front event coordinates
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return false if !$game_map.valid?(new_x, new_y)
# All event loops
for event in $game_map.events.values
# If event coordinates and triggers are consistent
next if event.x != new_x || event.y != new_y
next if !triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true
end
# If fitting event is not found
if result == false
# If front tile is a counter
if $game_map.counter?(new_x, new_y)
# Calculate coordinates of 1 tile further away
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return false if !$game_map.valid?(new_x, new_y)
# All event loops
for event in $game_map.events.values
# If event coordinates and triggers are consistent
next if event.x != new_x || event.y != new_y
next if !triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true
end
end
end
return result
end
#-----------------------------------------------------------------------------
# * Touch Event Starting Determinant
#-----------------------------------------------------------------------------
def check_event_trigger_touch(x, y)
result = false
# If event is running
return result if $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
# If event coordinates and triggers are consistent
next if event.x != x || event.y != y
if event.name[/trainer\((\d+)\)/i]
distance = $~[1].to_i
next if !pbEventCanReachPlayer?(event,self,distance)
elsif event.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
next if !pbEventFacesPlayer?(event,self,distance)
end
next if ![1,2].include?(event.trigger)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true
end
return result
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
last_real_x = @real_x
last_real_y = @real_y
super
update_screen_position(last_real_x, last_real_y)
# Update dependent events
$PokemonTemp.dependentEvents.updateDependentEvents
# Count down the time between allowed bump sounds
@bump_se -= 1 if @bump_se && @bump_se>0
# Finish up dismounting from surfing
if $PokemonTemp.endSurf && !moving?
pbCancelVehicles
$PokemonTemp.surfJump = nil
$PokemonTemp.endSurf = false
end
update_event_triggering
end
def update_command_new
dir = Input.dir4
unless pbMapInterpreterRunning? or $game_temp.message_window_showing or
$PokemonTemp.miniupdate or $game_temp.in_menu
# Move player in the direction the directional button is being pressed
if dir==@lastdir && Graphics.frame_count-@lastdirframe>Graphics.frame_rate/20
case dir
when 2; move_down
when 4; move_left
when 6; move_right
when 8; move_up
end
elsif dir!=@lastdir
case dir
when 2; turn_down
when 4; turn_left
when 6; turn_right
when 8; turn_up
end
end
end
# Record last direction input
@lastdirframe = Graphics.frame_count if dir!=@lastdir
@lastdir = dir
end
# Center player on-screen
def update_screen_position(last_real_x, last_real_y)
return if !@moved_this_frame
center_x = (Graphics.width/2 - Game_Map::TILE_WIDTH/2) * Game_Map::X_SUBPIXELS
center_y = (Graphics.height/2 - Game_Map::TILE_HEIGHT/2) * Game_Map::Y_SUBPIXELS
if @real_y < last_real_y and @real_y - $game_map.display_y < center_y
$game_map.scroll_up(last_real_y - @real_y)
end
if @real_y > last_real_y and @real_y - $game_map.display_y > center_y
$game_map.scroll_down(@real_y - last_real_y)
end
if @real_x < last_real_x and @real_x - $game_map.display_x < center_x
$game_map.scroll_left(last_real_x - @real_x)
end
if @real_x > last_real_x and @real_x - $game_map.display_x > center_x
$game_map.scroll_right(@real_x - last_real_x)
end
end
def update_event_triggering
return if moving?
# Try triggering events upon walking into them/in front of them
if @moved_this_frame
$PokemonTemp.dependentEvents.pbTurnDependentEvents
result = pbCheckEventTriggerFromDistance([2])
# Event determinant is via touch of same position event
result |= check_event_trigger_here([1,2])
# No events triggered, try other event triggers upon finishing a step
pbOnStepTaken(result)
end
# If C button was pressed, try to manually interact with events
if Input.trigger?(Input::C) && !$PokemonTemp.miniupdate
# Same position and front event determinant
check_event_trigger_here([0])
check_event_trigger_there([0,2])
end
end
end
def pbGetPlayerCharset(meta,charset,trainer=nil,force=false)
trainer = $Trainer if !trainer
outfit = (trainer) ? trainer.outfit : 0
if $game_player && $game_player.charsetData && !force
return nil if $game_player.charsetData[0]==$PokemonGlobal.playerID &&
$game_player.charsetData[1]==charset &&
$game_player.charsetData[2]==outfit
end
$game_player.charsetData = [$PokemonGlobal.playerID,charset,outfit] if $game_player
ret = meta[charset]
ret = meta[1] if !ret || ret==""
if pbResolveBitmap("Graphics/Characters/"+ret+"_"+outfit.to_s)
ret = ret+"_"+outfit.to_s
end
return ret
end
def pbUpdateVehicle
meta = pbGetMetadata(0,MetadataPlayerA+$PokemonGlobal.playerID)
if meta
newCharName = nil
charset = 1 # Regular graphic
if $PokemonGlobal.diving; charset = 5 # Diving graphic
elsif $PokemonGlobal.surfing; charset = 3 # Surfing graphic
elsif $PokemonGlobal.bicycle; charset = 2 # Bicycle graphic
end
newCharName = pbGetPlayerCharset(meta,charset)
$game_player.character_name = newCharName if newCharName
end
end
def pbCancelVehicles(destination=nil)
$PokemonGlobal.surfing = false
$PokemonGlobal.diving = false
$PokemonGlobal.bicycle = false if !destination || !pbCanUseBike?(destination)
pbUpdateVehicle
end
def pbCanUseBike?(mapid)
return true if pbGetMetadata(mapid,MetadataBicycleAlways)
val = pbGetMetadata(mapid,MetadataBicycle)
val = pbGetMetadata(mapid,MetadataOutdoor) if val==nil
return (val) ? true : false
end
def pbMountBike
return if $PokemonGlobal.bicycle
$PokemonGlobal.bicycle = true
pbUpdateVehicle
bikebgm = pbGetMetadata(0,MetadataBicycleBGM)
pbCueBGM(bikebgm,0.5) if bikebgm
end
def pbDismountBike
return if !$PokemonGlobal.bicycle
$PokemonGlobal.bicycle = false
pbUpdateVehicle
$game_map.autoplayAsCue
end

View File

@@ -0,0 +1,103 @@
class Game_Player < Game_Character
@@bobFrameSpeed = 1.0/15
def fullPattern
case self.direction
when 2; return self.pattern
when 4; return 4+self.pattern
when 6; return 8+self.pattern
when 8; return 12+self.pattern
end
return 0
end
def setDefaultCharName(chname,pattern,lockpattern=false)
return if pattern<0 || pattern>=16
@defaultCharacterName = chname
@direction = [2,4,6,8][pattern/4]
@pattern = pattern%4
@lock_pattern = lockpattern
end
def pbCanRun?
return false if $game_temp.in_menu || $game_temp.in_battle ||
@move_route_forcing || $game_temp.message_window_showing ||
pbMapInterpreterRunning?
terrain = pbGetTerrainTag
input = ($PokemonSystem.runstyle==1) ? $PokemonGlobal.runtoggle : Input.press?(Input::A)
return input && $PokemonGlobal.runningShoes &&
!$PokemonGlobal.diving && !$PokemonGlobal.surfing &&
!$PokemonGlobal.bicycle && !PBTerrain.onlyWalk?(terrain)
end
def pbIsRunning?
return moving? && !@move_route_forcing && pbCanRun?
end
def character_name
@defaultCharacterName = "" if !@defaultCharacterName
return @defaultCharacterName if @defaultCharacterName!=""
if !@move_route_forcing && $PokemonGlobal.playerID>=0
meta = pbGetMetadata(0,MetadataPlayerA+$PokemonGlobal.playerID)
if meta && !$PokemonGlobal.bicycle && !$PokemonGlobal.diving && !$PokemonGlobal.surfing
charset = 1 # Display normal character sprite
if pbCanRun? && (moving? || @wasmoving) && Input.dir4!=0 && meta[4] && meta[4]!=""
charset = 4 # Display running character sprite
end
newCharName = pbGetPlayerCharset(meta,charset)
@character_name = newCharName if newCharName
@wasmoving = moving?
end
end
return @character_name
end
def update_command
if PBTerrain.isIce?(pbGetTerrainTag)
self.move_speed = 5 # Sliding on ice
elsif !moving? && !@move_route_forcing && $PokemonGlobal
if $PokemonGlobal.bicycle
self.move_speed = 6 # Cycling
elsif pbCanRun? || $PokemonGlobal.surfing || $PokemonGlobal.diving
self.move_speed = 5 # Running, surfing or diving
else
self.move_speed = 4 # Walking
end
end
super
end
def update_pattern
if $PokemonGlobal.surfing || $PokemonGlobal.diving
p = ((Graphics.frame_count%60)*@@bobFrameSpeed).floor
@pattern = p if !@lock_pattern
@pattern_surf = p
@bob_height = (p>=2) ? 2 : 0
else
@bob_height = 0
super
end
end
end
=begin
class Game_Character
alias update_old2 update
def update
if self.is_a?(Game_Event)
if @dependentEvents
for i in 0...@dependentEvents.length
if @dependentEvents[i][0]==$game_map.map_id &&
@dependentEvents[i][1]==self.id
self.move_speed_real = $game_player.move_speed_real
break
end
end
end
end
update_old2
end
end
=end

View File

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

View File

@@ -0,0 +1,194 @@
#===============================================================================
# ** Map Autoscroll
#-------------------------------------------------------------------------------
# Wachunga
# Version 1.02
# 2005-12-18
#===============================================================================
=begin
This script supplements the built-in "Scroll Map" event command with the
aim of simplifying cutscenes (and map scrolling in general). Whereas the
normal event command requires a direction and number of tiles to scroll,
Map Autoscroll scrolls the map to center on the tile whose x and y
coordinates are given.
FEATURES
- automatic map scrolling to given x,y coordinate (or player)
- destination is fixed, so it's possible to scroll to same place even if
origin is variable (e.g. moving NPC)
- variable speed (just like "Scroll Map" event command)
- diagonal scrolling supported
SETUP
Instead of a "Scroll Map" event command, use the "Call Script" command
and enter on the following on the first line:
autoscroll(x,y)
(replacing "x" and "y" with the x and y coordinates of the tile to scroll to)
To specify a scroll speed other than the default (4), use:
autoscroll(x,y,speed)
(now also replacing "speed" with the scroll speed from 1-6)
Diagonal scrolling happens automatically when the destination is diagonal
relative to the starting point (i.e., not directly up, down, left or right).
To scroll to the player, instead use the following:
autoscroll_player(speed)
Note: because of how the interpreter and the "Call Script" event command
are setup, the call to autoscroll(...) can only be on the first line of
the "Call Script" event command (and not flowing down to subsequent lines).
For example, the following call may not work as expected:
autoscroll($game_variables[1],
$game_variables[2])
(since the long argument names require dropping down to a second line)
A work-around is to setup new variables with shorter names in a preceding
(separate) "Call Script" event command:
@x = $game_variables[1]
@y = $game_variables[2]
and then use those as arguments:
autoscroll(@x,@y)
The renaming must be in a separate "Call Script" because otherwise
the call to autoscroll(...) isn't on the first line.
Originally requested by militantmilo80:
http://www.rmxp.net/forums/index.php?showtopic=29519
=end
class Interpreter
SCROLL_SPEED_DEFAULT = 4
#-----------------------------------------------------------------------------
# * Map Autoscroll to Coordinates
# x : x coordinate to scroll to and center on
# y : y coordinate to scroll to and center on
# speed : (optional) scroll speed (from 1-6, default being 4)
#-----------------------------------------------------------------------------
def autoscroll(x,y,speed=SCROLL_SPEED_DEFAULT)
if $game_map.scrolling?
return false
elsif not $game_map.valid?(x,y)
print 'Map Autoscroll: given x,y is invalid'
return command_skip
elsif not (1..6).include?(speed)
print 'Map Autoscroll: invalid speed (1-6 only)'
return command_skip
end
center_x = (Graphics.width/2 - Game_Map::TILE_WIDTH/2) * 4 # X coordinate in the center of the screen
center_y = (Graphics.height/2 - Game_Map::TILE_HEIGHT/2) * 4 # Y coordinate in the center of the screen
max_x = ($game_map.width - Graphics.width*1.0/Game_Map::TILE_WIDTH) * 4 * Game_Map::TILE_WIDTH
max_y = ($game_map.height - Graphics.height*1.0/Game_Map::TILE_HEIGHT) * 4 * Game_Map::TILE_HEIGHT
count_x = ($game_map.display_x - [0,[x*Game_Map::REAL_RES_X-center_x,max_x].min].max)/Game_Map::REAL_RES_X
count_y = ($game_map.display_y - [0,[y*Game_Map::REAL_RES_Y-center_y,max_y].min].max)/Game_Map::REAL_RES_Y
if not @diag
@diag = true
dir = nil
if count_x > 0
if count_y > 0
dir = 7
elsif count_y < 0
dir = 1
end
elsif count_x < 0
if count_y > 0
dir = 9
elsif count_y < 0
dir = 3
end
end
count = [count_x.abs,count_y.abs].min
else
@diag = false
dir = nil
if count_x != 0 and count_y != 0
return false
elsif count_x > 0
dir = 4
elsif count_x < 0
dir = 6
elsif count_y > 0
dir = 8
elsif count_y < 0
dir = 2
end
count = count_x != 0 ? count_x.abs : count_y.abs
end
$game_map.start_scroll(dir, count, speed) if dir != nil
if @diag
return false
else
return true
end
end
#-----------------------------------------------------------------------------
# * Map Autoscroll (to Player)
# speed : (optional) scroll speed (from 1-6, default being 4)
#-----------------------------------------------------------------------------
def autoscroll_player(speed=SCROLL_SPEED_DEFAULT)
autoscroll($game_player.x,$game_player.y,speed)
end
end
class Game_Map
def scroll_downright(distance)
@display_x = [@display_x + distance,
(self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X].min
@display_y = [@display_y + distance,
(self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y].min
end
def scroll_downleft(distance)
@display_x = [@display_x - distance, 0].max
@display_y = [@display_y + distance,
(self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y].min
end
def scroll_upright(distance)
@display_x = [@display_x + distance,
(self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X].min
@display_y = [@display_y - distance, 0].max
end
def scroll_upleft(distance)
@display_x = [@display_x - distance, 0].max
@display_y = [@display_y - distance, 0].max
end
def update_scrolling
# If scrolling
if @scroll_rest > 0
# Change from scroll speed to distance in map coordinates
distance = (1<<@scroll_speed)*40/Graphics.frame_rate
distance = @scroll_rest if distance>@scroll_rest
# Execute scrolling
case @scroll_direction
when 1; scroll_downleft(distance)
when 2; scroll_down(distance)
when 3; scroll_downright(distance)
when 4; scroll_left(distance)
when 6; scroll_right(distance)
when 7; scroll_upleft(distance)
when 8; scroll_up(distance)
when 9; scroll_upright(distance)
end
# Subtract distance scrolled
@scroll_rest -= distance
end
end
end

View File

@@ -0,0 +1,488 @@
#===============================================================================
# Map Factory (allows multiple maps to be loaded at once and connected)
#===============================================================================
class PokemonMapFactory
attr_reader :maps
def initialize(id)
@maps = []
@fixup = false
@mapChanged = false # transient instance variable
setup(id)
end
# Clears all maps and sets up the current map with id. This function also sets
# the positions of neighboring maps and notifies the game system of a map
# change.
def setup(id)
@maps.clear
@maps[0] = Game_Map.new
@mapIndex = 0
oldID = ($game_map) ? $game_map.map_id : 0
setMapChanging(id,@maps[0]) if oldID!=0 && oldID!=@maps[0].map_id
$game_map = @maps[0]
@maps[0].setup(id)
setMapsInRange
setMapChanged(oldID)
end
def map
@mapIndex = 0 if !@mapIndex || @mapIndex<0
return @maps[@mapIndex] if @maps[@mapIndex]
raise "No maps in save file... (mapIndex=#{@mapIndex})" if @maps.length==0
for i in 0...@maps.length
if @maps[i]
echo("Using next map, may be incorrect (mapIndex=#{@mapIndex}, length=#{@maps.length})")
return @maps[i]
end
raise "No maps in save file... (all maps empty; mapIndex=#{@mapIndex})"
end
end
def hasMap?(id)
for map in @maps
return true if map.map_id==id
end
return false
end
def getMapIndex(id)
for i in 0...@maps.length
return i if @maps[i].map_id==id
end
return -1
end
def getMap(id,add=true)
for map in @maps
return map if map.map_id==id
end
map = Game_Map.new
map.setup(id)
@maps.push(map) if add
return map
end
def getMapNoAdd(id)
return getMap(id,false)
end
def getNewMap(playerX,playerY)
id = $game_map.map_id
conns = MapFactoryHelper.getMapConnections
for conn in conns
next if conn[0]!=id && conn[3]!=id
mapidB = nil
newx = 0
newy = 0
if conn[0]==id
mapidB = conn[3]
mapB = MapFactoryHelper.getMapDims(conn[3])
newx = conn[4] - conn[1] + playerX
newy = conn[5] - conn[2] + playerY
else
mapidB = conn[0]
mapB = MapFactoryHelper.getMapDims(conn[0])
newx = conn[1] - conn[4] + playerX
newy = conn[2] - conn[5] + playerY
end
if newx>=0 && newx<mapB[0] && newy>=0 && newy<mapB[1]
return [getMap(mapidB),newx,newy]
end
end
return nil
end
def setCurrentMap
return if $game_player.moving?
return if $game_map.valid?($game_player.x,$game_player.y)
newmap = getNewMap($game_player.x,$game_player.y)
return if !newmap
oldmap=$game_map.map_id
if oldmap!=0 && oldmap!=newmap[0].map_id
setMapChanging(newmap[0].map_id,newmap[0])
end
$game_map = newmap[0]
@mapIndex = getMapIndex($game_map.map_id)
$game_player.moveto(newmap[1],newmap[2])
$game_map.update
pbAutoplayOnTransition
$game_map.refresh
setMapChanged(oldmap)
end
def setMapsInRange
return if @fixup
@fixup = true
id = $game_map.map_id
conns = MapFactoryHelper.getMapConnections
for conn in conns
if conn[0]==id
mapA = getMap(conn[0])
newdispx = (conn[4] - conn[1]) * Game_Map::REAL_RES_X + mapA.display_x
newdispy = (conn[5] - conn[2]) * Game_Map::REAL_RES_Y + mapA.display_y
if hasMap?(conn[3]) || MapFactoryHelper.mapInRangeById?(conn[3],newdispx,newdispy)
mapB = getMap(conn[3])
mapB.display_x = newdispx if mapB.display_x!=newdispx
mapB.display_y = newdispy if mapB.display_y!=newdispy
end
elsif conn[3]==id
mapA = getMap(conn[3])
newdispx = (conn[1] - conn[4]) * Game_Map::REAL_RES_X + mapA.display_x
newdispy = (conn[2] - conn[5]) * Game_Map::REAL_RES_Y + mapA.display_y
if hasMap?(conn[0]) || MapFactoryHelper.mapInRangeById?(conn[0],newdispx,newdispy)
mapB = getMap(conn[0])
mapB.display_x = newdispx if mapB.display_x!=newdispx
mapB.display_y = newdispy if mapB.display_y!=newdispy
end
end
end
@fixup = false
end
def setMapChanging(newID,newMap)
Events.onMapChanging.trigger(self,newID,newMap)
end
def setMapChanged(prevMap)
Events.onMapChange.trigger(self,prevMap)
@mapChanged = true
end
def setSceneStarted(scene)
Events.onMapSceneChange.trigger(self,scene,@mapChanged)
@mapChanged = false
end
# Similar to Game_Player#passable?, but supports map connections
def isPassableFromEdge?(x,y)
return true if $game_map.valid?(x,y)
newmap = getNewMap(x,y)
return false if !newmap
return isPassable?(newmap[0].map_id,newmap[1],newmap[2])
end
def isPassable?(mapID,x,y,thisEvent=nil)
thisEvent = $game_player if !thisEvent
map = getMapNoAdd(mapID)
return false if !map
return false if !map.valid?(x,y)
return true if thisEvent.through
if thisEvent==$game_player
return false unless ($DEBUG && Input.press?(Input::CTRL)) ||
map.passable?(x,y,0,thisEvent)
else
return false unless map.passable?(x,y,0,thisEvent)
end
for event in map.events.values
next if event.x != x || event.y != y
return false if !event.through && event.character_name!=""
end
if thisEvent.is_a?(Game_Player)
if thisEvent.x == x and thisEvent.y == y
return false if !thisEvent.through && thisEvent.character_name!=""
end
end
return true
end
# Only used by dependent events
def isPassableStrict?(mapID,x,y,thisEvent=nil)
thisEvent = $game_player if !thisEvent
map = getMapNoAdd(mapID)
return false if !map
return false if !map.valid?(x,y)
return true if thisEvent.through
if thisEvent==$game_player
if !($DEBUG && Input.press?(Input::CTRL))
return false if !map.passableStrict?(x,y,0,thisEvent)
end
else
return false if !map.passableStrict?(x,y,0,thisEvent)
end
for event in map.events.values
next if event == thisEvent || event.x != x || event.y != y
return false if !event.through && event.character_name!=""
end
return true
end
def getTerrainTag(mapid,x,y,countBridge=false)
map = getMapNoAdd(mapid)
return map.terrain_tag(x,y,countBridge)
end
def getFacingTerrainTag(dir=nil,event=nil)
tile = getFacingTile(dir,event)
return 0 if !tile
return getTerrainTag(tile[0],tile[1],tile[2])
end
def getTerrainTagFromCoords(mapid,x,y,countBridge=false)
tile = getRealTilePos(mapid,x,y)
return 0 if !tile
return getTerrainTag(tile[0],tile[1],tile[2])
end
def areConnected?(mapID1,mapID2)
return true if mapID1==mapID2
conns = MapFactoryHelper.getMapConnections
for conn in conns
if (conn[0]==mapID1 && conn[3]==mapID2) ||
(conn[0]==mapID2 && conn[3]==mapID1)
return true
end
end
return false
end
def getRelativePos(thisMapID,thisX,thisY,otherMapID,otherX,otherY)
if thisMapID==otherMapID
# Both events share the same map
return [otherX-thisX,otherY-thisY]
end
conns = MapFactoryHelper.getMapConnections
for conn in conns
if conn[0]==thisMapID && conn[1]==otherMapID
posX = thisX + conn[4] - conn[1] + otherX
posY = thisY + conn[5] - conn[2] + otherY
return [posX,posY]
elsif conn[1]==thisMapID && conn[0]==otherMapID
posX = thisX + conn[1] - conn[4] + otherX
posY = thisY + conn[2] - conn[5] + otherY
return [posX,posY]
end
end
return [0,0]
end
# Gets the distance from this event to another event. Example: If this event's
# coordinates are (2,5) and the other event's coordinates are (5,1), returns
# the array (3,-4), because (5-2=3) and (1-5=-4).
def getThisAndOtherEventRelativePos(thisEvent,otherEvent)
return [0,0] if !thisEvent || !otherEvent
return getRelativePos(
thisEvent.map.map_id,thisEvent.x,thisEvent.y,
otherEvent.map.map_id,otherEvent.x,otherEvent.y)
end
def getThisAndOtherPosRelativePos(thisEvent,otherMapID,otherX,otherY)
return [0,0] if !thisEvent
return getRelativePos(
thisEvent.map.map_id,thisEvent.x,thisEvent.y,otherMapID,otherX,otherY)
end
def getOffsetEventPos(event,xOffset,yOffset)
event = $game_player if !event
return nil if !event
return getRealTilePos(event.map.map_id,event.x+xOffset,event.y+yOffset)
end
def getFacingTile(direction=nil,event=nil,steps=1)
event = $game_player if event==nil
return [0,0,0] if !event
x = event.x
y = event.y
id = event.map.map_id
direction = event.direction if direction==nil
return getFacingTileFromPos(id,x,y,direction,steps)
end
def getFacingTileFromPos(mapID,x,y,direction=0,steps=1)
id = mapID
case direction
when 1; x -= steps; y += steps
when 2; y += steps
when 3; x += steps; y += steps
when 4; x -= steps
when 6; x += steps
when 7; x -= steps; y -= steps
when 8; y -= steps
when 9; x += steps; y -= steps
else; return [id,x,y]
end
return getRealTilePos(mapID,x,y)
end
def getRealTilePos(mapID,x,y)
id = mapID
return [id,x,y] if getMapNoAdd(id).valid?(x,y)
conns = MapFactoryHelper.getMapConnections
for conn in conns
if conn[0]==id
newX = x + conn[4] - conn[1]
newY = y + conn[5] - conn[2]
next if newX<0 || newY<0
dims = MapFactoryHelper.getMapDims(conn[3])
next if newX>=dims[0] || newY>=dims[1]
return [conn[3],newX,newY]
elsif conn[3]==id
newX = x + conn[1] - conn[4]
newY = y + conn[2] - conn[5]
next if newX<0 || newY<0
dims = MapFactoryHelper.getMapDims(conn[0])
next if newX>=dims[0] || newY>=dims[1]
return [conn[0],newX,newY]
end
end
return nil
end
def getFacingCoords(x,y,direction=0,steps=1)
case direction
when 1; x -= steps; y += steps
when 2; y += steps
when 3; x += steps; y += steps
when 4; x -= steps
when 6; x += steps
when 7; x -= steps; y -= steps
when 8; y -= steps
when 9; x += steps; y -= steps
end
return [x,y]
end
def updateMaps(scene)
updateMapsInternal
$MapFactory.setSceneStarted(scene) if @mapChanged
end
def updateMapsInternal
return if $game_player.moving?
if !MapFactoryHelper.hasConnections?($game_map.map_id)
return if @maps.length==1
for i in 0...@maps.length
@maps[i] = nil if $game_map.map_id!=@maps[i].map_id
end
@maps.compact!
@mapIndex = getMapIndex($game_map.map_id)
return
end
setMapsInRange
deleted = false
for i in 0...@maps.length
next if MapFactoryHelper.mapInRange?(@maps[i])
@maps[i] = nil
deleted = true
end
if deleted
@maps.compact!
@mapIndex = getMapIndex($game_map.map_id)
end
end
end
#===============================================================================
# Map Factory Helper (stores map connection and size data and calculations
# involving them)
#===============================================================================
module MapFactoryHelper
@@MapConnections = nil
@@MapDims = nil
def self.clear
@@MapConnections = nil
@@MapDims = nil
end
def self.getMapConnections
if !@@MapConnections
@@MapConnections = []
begin
conns = load_data("Data/map_connections.dat")
rescue
conns = []
end
for i in 0...conns.length
conn = conns[i]
v = getMapEdge(conn[0],conn[1])
dims = getMapDims(conn[0])
next if dims[0]==0 || dims[1]==0
if conn[1]=="N" || conn[1]=="S"
conn[1] = conn[2]
conn[2] = v
elsif conn[1]=="E" || conn[1]=="W"
conn[1] = v
end
v = getMapEdge(conn[3],conn[4])
dims = getMapDims(conn[3])
next if dims[0]==0 || dims[1]==0
if conn[4]=="N" || conn[4]=="S"
conn[4] = conn[5]
conn[5] = v
elsif conn[4]=="E" || conn[4]=="W"
conn[4] = v
end
@@MapConnections.push(conn)
end
end
return @@MapConnections
end
def self.hasConnections?(id)
conns = MapFactoryHelper.getMapConnections
for conn in conns
return true if conn[0]==id || conn[3]==id
end
return false
end
# Gets the height and width of the map with id
def self.getMapDims(id)
# Create cache if doesn't exist
@@MapDims = [] if !@@MapDims
# Add map to cache if can't be found
if !@@MapDims[id]
begin
map = pbLoadRxData(sprintf("Data/Map%03d", id))
@@MapDims[id] = [map.width,map.height]
rescue
@@MapDims[id] = [0,0]
end
end
# Return map in cache
return @@MapDims[id]
end
# Returns the X or Y coordinate of an edge on the map with id.
# Considers the special strings "N","W","E","S"
def self.getMapEdge(id,edge)
return 0 if edge=="N" || edge=="W"
dims = getMapDims(id) # Get dimensions
return dims[0] if edge=="E"
return dims[1] if edge=="S"
return dims[0] # real dimension (use width)
end
def self.mapInRange?(map)
range = 6 # Number of tiles
dispx = map.display_x
dispy = map.display_y
return false if dispx >= (map.width + range) * Game_Map::REAL_RES_X
return false if dispy >= (map.height + range) * Game_Map::REAL_RES_Y
return false if dispx <= -(Graphics.width + range * Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
return true
end
def self.mapInRangeById?(id,dispx,dispy)
range = 6 # Number of tiles
dims = MapFactoryHelper.getMapDims(id)
return false if dispx >= (dims[0] + range) * Game_Map::REAL_RES_X
return false if dispy >= (dims[1] + range) * Game_Map::REAL_RES_Y
return false if dispx <= -(Graphics.width + range * Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
return true
end
end
def updateTilesets
maps = $MapFactory.maps
for map in maps
map.updateTileset if map
end
end

View File

@@ -0,0 +1,58 @@
class Sprite_Picture
def initialize(viewport, picture)
@viewport = viewport
@picture = picture
@sprite = nil
update
end
def dispose
@sprite.dispose if @sprite
end
def update
@sprite.update if @sprite
# If picture file name is different from current one
if @picture_name != @picture.name
# Remember file name to instance variables
@picture_name = @picture.name
# If file name is not empty
if @picture_name != ""
# Get picture graphic
@sprite=IconSprite.new(0,0,@viewport) if !@sprite
@sprite.setBitmap("Graphics/Pictures/"+@picture_name)
end
end
# If file name is empty
if @picture_name == ""
# Set sprite to invisible
if @sprite
@sprite.dispose if @sprite
@sprite=nil
end
return
end
# Set sprite to visible
@sprite.visible = true
# Set transfer starting point
if @picture.origin == 0
@sprite.ox = 0
@sprite.oy = 0
else
@sprite.ox = @sprite.bitmap.width / 2
@sprite.oy = @sprite.bitmap.height / 2
end
# Set sprite coordinates
@sprite.x = @picture.x
@sprite.y = @picture.y
@sprite.z = @picture.number
# Set zoom rate, opacity level, and blend method
@sprite.zoom_x = @picture.zoom_x / 100.0
@sprite.zoom_y = @picture.zoom_y / 100.0
@sprite.opacity = @picture.opacity
@sprite.blend_type = @picture.blend_type
# Set rotation angle and color tone
@sprite.angle = @picture.angle
@sprite.tone = @picture.tone
end
end

View File

@@ -0,0 +1,44 @@
class Sprite_Timer
def initialize(viewport=nil)
@viewport=viewport
@timer=nil
@total_sec=nil
@disposed=false
end
def dispose
@timer.dispose if @timer
@timer=nil
@disposed=true
end
def disposed?
@disposed
end
def update
return if disposed?
if $game_system.timer_working
if !@timer
@timer=Window_AdvancedTextPokemon.newWithSize("",Graphics.width-120,0,120,64)
@timer.width=@timer.borderX+96
@timer.x=Graphics.width-@timer.width
@timer.viewport=@viewport
@timer.z=99998
end
curtime=$game_system.timer / Graphics.frame_rate
curtime=0 if curtime<0
if curtime != @total_sec
# Calculate total number of seconds
@total_sec = curtime
# Make a string for displaying the timer
min = @total_sec / 60
sec = @total_sec % 60
@timer.text = _ISPRINTF("<ac>{1:02d}:{2:02d}", min, sec)
end
@timer.update
else
@timer.visible=false if @timer
end
end
end

View File

@@ -0,0 +1,174 @@
class BushBitmap
def initialize(bitmap,isTile,depth)
@bitmaps = []
@bitmap = bitmap
@isTile = isTile
@isBitmap = @bitmap.is_a?(Bitmap)
@depth = depth
end
def dispose
for b in @bitmaps
b.dispose if b
end
end
def bitmap
thisBitmap = (@isBitmap) ? @bitmap : @bitmap.bitmap
current = (@isBitmap) ? 0 : @bitmap.currentIndex
if !@bitmaps[current]
if @isTile
@bitmaps[current] = pbBushDepthTile(thisBitmap,@depth)
else
@bitmaps[current] = pbBushDepthBitmap(thisBitmap,@depth)
end
end
return @bitmaps[current]
end
def pbBushDepthBitmap(bitmap,depth)
ret = Bitmap.new(bitmap.width,bitmap.height)
charheight = ret.height/4
cy = charheight-depth-2
for i in 0...4
y = i*charheight
if cy>=0
ret.blt(0,y,bitmap,Rect.new(0,y,ret.width,cy))
ret.blt(0,y+cy,bitmap,Rect.new(0,y+cy,ret.width,2),170)
end
ret.blt(0,y+cy+2,bitmap,Rect.new(0,y+cy+2,ret.width,2),85) if cy+2>=0
end
return ret
end
def pbBushDepthTile(bitmap,depth)
ret = Bitmap.new(bitmap.width,bitmap.height)
charheight = ret.height
cy = charheight-depth-2
y = charheight
if cy>=0
ret.blt(0,y,bitmap,Rect.new(0,y,ret.width,cy))
ret.blt(0,y+cy,bitmap,Rect.new(0,y+cy,ret.width,2),170)
end
ret.blt(0,y+cy+2,bitmap,Rect.new(0,y+cy+2,ret.width,2),85) if cy+2>=0
return ret
end
end
class Sprite_Character < RPG::Sprite
attr_accessor :character
def initialize(viewport, character = nil)
super(viewport)
@character = character
@oldbushdepth = 0
@spriteoffset = false
if !character || character==$game_player || (character.name[/reflection/i] rescue false)
@reflection = Sprite_Reflection.new(self,character,viewport)
end
@surfbase = Sprite_SurfBase.new(self,character,viewport) if character==$game_player
update
end
def groundY
return @character.screen_y_ground
end
def visible=(value)
super(value)
@reflection.visible = value if @reflection
end
def dispose
@bushbitmap.dispose if @bushbitmap
@bushbitmap = nil
@charbitmap.dispose if @charbitmap
@charbitmap = nil
@reflection.dispose if @reflection
@reflection = nil
@surfbase.dispose if @surfbase
@surfbase = nil
super
end
def update
return if @character.is_a?(Game_Event) && !@character.should_update?
super
if @tile_id!=@character.tile_id or
@character_name!=@character.character_name or
@character_hue!=@character.character_hue or
@oldbushdepth!=@character.bush_depth
@tile_id = @character.tile_id
@character_name = @character.character_name
@character_hue = @character.character_hue
@oldbushdepth = @character.bush_depth
if @tile_id>=384
@charbitmap.dispose if @charbitmap
@charbitmap = pbGetTileBitmap(@character.map.tileset_name,@tile_id,@character.character_hue)
@charbitmapAnimated = false
@bushbitmap.dispose if @bushbitmap
@bushbitmap = nil
@spriteoffset = false
@cw = Game_Map::TILE_WIDTH
@ch = Game_Map::TILE_HEIGHT
self.src_rect.set(0,0,@cw,@ch)
self.ox = @cw/2
self.oy = @ch
@character.sprite_size = [@cw,@ch]
else
@charbitmap.dispose if @charbitmap
@charbitmap = AnimatedBitmap.new(
"Graphics/Characters/"+@character.character_name,@character.character_hue)
@charbitmapAnimated = true
@bushbitmap.dispose if @bushbitmap
@bushbitmap = nil
@spriteoffset = @character_name[/offset/i]
@cw = @charbitmap.width/4
@ch = @charbitmap.height/4
self.ox = @cw/2
@character.sprite_size = [@cw,@ch]
end
end
@charbitmap.update if @charbitmapAnimated
bushdepth = @character.bush_depth
if bushdepth==0
self.bitmap = (@charbitmapAnimated) ? @charbitmap.bitmap : @charbitmap
else
@bushbitmap = BushBitmap.new(@charbitmap,(@tile_id>=384),bushdepth) if !@bushbitmap
self.bitmap = @bushbitmap.bitmap
end
self.visible = !@character.transparent
if @tile_id==0
sx = @character.pattern*@cw
sy = ((@character.direction-2)/2)*@ch
self.src_rect.set(sx,sy,@cw,@ch)
self.oy = (@spriteoffset rescue false) ? @ch-16 : @ch
self.oy -= @character.bob_height
end
if self.visible
if $PokemonSystem.tilemap==0 ||
(@character.is_a?(Game_Event) && @character.name[/regulartone/i])
self.tone.set(0,0,0,0)
else
pbDayNightTint(self)
end
end
self.x = @character.screen_x
self.y = @character.screen_y
self.z = @character.screen_z(@ch)
# self.zoom_x = Game_Map::TILE_WIDTH/32.0
# self.zoom_y = Game_Map::TILE_HEIGHT/32.0
self.opacity = @character.opacity
self.blend_type = @character.blend_type
# self.bush_depth = @character.bush_depth
if @character.animation_id!=0
animation = $data_animations[@character.animation_id]
animation(animation,true)
@character.animation_id = 0
end
@reflection.update if @reflection
@surfbase.update if @surfbase
end
end

View File

@@ -0,0 +1,90 @@
class Sprite_Reflection
attr_reader :visible
attr_accessor :event
def initialize(sprite,event,viewport=nil)
@rsprite = sprite
@sprite = nil
@event = event
@height = 0
@fixedheight = false
if @event && @event!=$game_player
if @event.name[/reflection\((\d+)\)/i]
@height = $~[1].to_i || 0
@fixedheight = true
end
end
@viewport = viewport
@disposed = false
update
end
def dispose
if !@disposed
@sprite.dispose if @sprite
@sprite = nil
@disposed = true
end
end
def disposed?
@disposed
end
def visible=(value)
@visible = value
@sprite.visible = value if @sprite && !@sprite.disposed?
end
def update
return if disposed?
shouldShow = @rsprite.visible
if !shouldShow
# Just-in-time disposal of sprite
if @sprite
@sprite.dispose
@sprite = nil
end
return
end
# Just-in-time creation of sprite
@sprite = Sprite.new(@viewport) if !@sprite
if @sprite
x = @rsprite.x-@rsprite.ox
y = @rsprite.y-@rsprite.oy
y -= 32 if @rsprite.character.character_name[/offset/i]
@height = $PokemonGlobal.bridge if !@fixedheight
y += @height*16
width = @rsprite.src_rect.width
height = @rsprite.src_rect.height
@sprite.x = x+width/2
@sprite.y = y+height+height/2
@sprite.ox = width/2
@sprite.oy = height/2-2 # Hard-coded 2 pixel shift up
@sprite.oy -= @rsprite.character.bob_height*2
@sprite.z = -50 # Still water is -100, map is 0 and above
@sprite.zoom_x = @rsprite.zoom_x
@sprite.zoom_y = @rsprite.zoom_y
frame = (Graphics.frame_count%40)/10
case frame
when 1; @sprite.zoom_x *= 0.95
when 3; @sprite.zoom_x *= 1.05
else; @sprite.zoom_x *= 1.0
end
@sprite.angle = 180.0
@sprite.mirror = true
@sprite.bitmap = @rsprite.bitmap
@sprite.tone = @rsprite.tone
if @height>0
@sprite.color = Color.new(48,96,160,255) # Dark still water
@sprite.opacity = @rsprite.opacity
@sprite.visible = !TIME_SHADING # Can't time-tone a colored sprite
else
@sprite.color = Color.new(224,224,224,96)
@sprite.opacity = @rsprite.opacity*3/4
@sprite.visible = true
end
@sprite.src_rect = @rsprite.src_rect
end
end
end

View File

@@ -0,0 +1,77 @@
class Sprite_SurfBase
attr_reader :visible
attr_accessor :event
def initialize(sprite,event,viewport=nil)
@rsprite = sprite
@sprite = nil
@event = event
@viewport = viewport
@disposed = false
@surfbitmap = AnimatedBitmap.new("Graphics/Characters/base_surf")
@divebitmap = AnimatedBitmap.new("Graphics/Characters/base_dive")
@cws = @surfbitmap.width/4
@chs = @surfbitmap.height/4
@cwd = @divebitmap.width/4
@chd = @divebitmap.height/4
update
end
def dispose
if !@disposed
@sprite.dispose if @sprite
@sprite = nil
@surfbitmap.dispose
@disposed = true
end
end
def disposed?
@disposed
end
def visible=(value)
@visible = value
@sprite.visible = value if @sprite && !@sprite.disposed?
end
def update
return if disposed?
if !$PokemonGlobal.surfing && !$PokemonGlobal.diving
# Just-in-time disposal of sprite
if @sprite
@sprite.dispose
@sprite = nil
end
return
end
# Just-in-time creation of sprite
@sprite = Sprite.new(@viewport) if !@sprite
if @sprite
if $PokemonGlobal.surfing
@sprite.bitmap = @surfbitmap.bitmap; cw = @cws; ch = @chs
elsif $PokemonGlobal.diving
@sprite.bitmap = @divebitmap.bitmap; cw = @cwd; ch = @chd
end
sx = @event.pattern_surf*cw
sy = ((@event.direction-2)/2)*ch
@sprite.src_rect.set(sx,sy,cw,ch)
if $PokemonTemp.surfJump
@sprite.x = ($PokemonTemp.surfJump[0]*Game_Map::REAL_RES_X-@event.map.display_x+3)/4+(Game_Map::TILE_WIDTH/2)
@sprite.y = ($PokemonTemp.surfJump[1]*Game_Map::REAL_RES_Y-@event.map.display_y+3)/4+(Game_Map::TILE_HEIGHT/2)+16
else
@sprite.x = @rsprite.x
@sprite.y = @rsprite.y
end
@sprite.ox = cw/2
@sprite.oy = ch-16 # Assume base needs offsetting
@sprite.oy -= @event.bob_height
@sprite.z = @event.screen_z(ch)-1
@sprite.zoom_x = @rsprite.zoom_x
@sprite.zoom_y = @rsprite.zoom_y
@sprite.tone = @rsprite.tone
@sprite.color = @rsprite.color
@sprite.opacity = @rsprite.opacity
end
end
end

View File

@@ -0,0 +1,167 @@
class ClippableSprite < Sprite_Character
def initialize(viewport,event,tilemap)
@tilemap = tilemap
@_src_rect = Rect.new(0,0,0,0)
super(viewport,event)
end
def update
super
@_src_rect = self.src_rect
tmright = @tilemap.map_data.xsize*Game_Map::TILE_WIDTH-@tilemap.ox
echoln("x=#{self.x},ox=#{self.ox},tmright=#{tmright},tmox=#{@tilemap.ox}")
if @tilemap.ox-self.ox<-self.x
# clipped on left
diff = -self.x-@tilemap.ox+self.ox
self.src_rect = Rect.new(@_src_rect.x+diff,@_src_rect.y,
@_src_rect.width-diff,@_src_rect.height)
echoln("clipped out left: #{diff} #{@tilemap.ox-self.ox} #{self.x}")
elsif tmright-self.ox<self.x
# clipped on right
diff = self.x-tmright+self.ox
self.src_rect = Rect.new(@_src_rect.x,@_src_rect.y,
@_src_rect.width-diff,@_src_rect.height)
echoln("clipped out right: #{diff} #{tmright+self.ox} #{self.x}")
else
echoln("-not- clipped out left: #{diff} #{@tilemap.ox-self.ox} #{self.x}")
end
end
end
class Spriteset_Map
attr_reader :map
attr_accessor :tilemap
@@viewport0 = Viewport.new(0,0,Graphics.width,Graphics.height) # Panorama
@@viewport0.z = -100
@@viewport1 = Viewport.new(0,0,Graphics.width,Graphics.height) # Map, events, player, fog
@@viewport1.z = 0
@@viewport3 = Viewport.new(0,0,Graphics.width,Graphics.height) # Flashing
@@viewport3.z = 500
def Spriteset_Map.viewport # For access by Spriteset_Global
return @@viewport1
end
def initialize(map=nil)
@map = (map) ? map : $game_map
@tilemap = TilemapLoader.new(@@viewport1)
@tilemap.tileset = pbGetTileset(@map.tileset_name)
for i in 0...7
autotile_name = @map.autotile_names[i]
@tilemap.autotiles[i] = pbGetAutotile(autotile_name)
end
@tilemap.map_data = @map.data
@tilemap.priorities = @map.priorities
@tilemap.terrain_tags = @map.terrain_tags
@panorama = AnimatedPlane.new(@@viewport0)
@fog = AnimatedPlane.new(@@viewport1)
@fog.z = 3000
@character_sprites = []
for i in @map.events.keys.sort
sprite = Sprite_Character.new(@@viewport1,@map.events[i])
@character_sprites.push(sprite)
end
@weather = RPG::Weather.new(@@viewport1)
pbOnSpritesetCreate(self,@@viewport1)
update
end
def dispose
@tilemap.tileset.dispose
for i in 0...7
@tilemap.autotiles[i].dispose
end
@tilemap.dispose
@panorama.dispose
@fog.dispose
for sprite in @character_sprites
sprite.dispose
end
@weather.dispose
@tilemap = nil
@panorama = nil
@fog = nil
@character_sprites.clear
@weather = nil
end
def getAnimations
return @usersprites
end
def restoreAnimations(anims)
@usersprites = anims
end
def update
if @panorama_name!=@map.panorama_name || @panorama_hue!=@map.panorama_hue
@panorama_name = @map.panorama_name
@panorama_hue = @map.panorama_hue
@panorama.setPanorama(nil) if @panorama.bitmap!=nil
@panorama.setPanorama(@panorama_name,@panorama_hue) if @panorama_name!=""
Graphics.frame_reset
end
if @fog_name!=@map.fog_name || @fog_hue!=@map.fog_hue
@fog_name = @map.fog_name
@fog_hue = @map.fog_hue
@fog.setFog(nil) if @fog.bitmap!=nil
@fog.setFog(@fog_name,@fog_hue) if @fog_name!=""
Graphics.frame_reset
end
tmox = (@map.display_x/Game_Map::X_SUBPIXELS).round
tmoy = (@map.display_y/Game_Map::Y_SUBPIXELS).round
@tilemap.ox = tmox
@tilemap.oy = tmoy
if $PokemonSystem.tilemap==0 # Original Map View only, to prevent wrapping
@@viewport1.rect.x = [-tmox,0].max
@@viewport1.rect.y = [-tmoy,0].max
@@viewport1.rect.width = [@tilemap.map_data.xsize*Game_Map::TILE_WIDTH-tmox,Graphics.width].min
@@viewport1.rect.height = [@tilemap.map_data.ysize*Game_Map::TILE_HEIGHT-tmoy,Graphics.height].min
@@viewport1.ox = [-tmox,0].max
@@viewport1.oy = [-tmoy,0].max
else
@@viewport1.rect.set(0,0,Graphics.width,Graphics.height)
@@viewport1.ox = 0
@@viewport1.oy = 0
end
@@viewport1.ox += $game_screen.shake
@tilemap.update
@panorama.ox = tmox/2
@panorama.oy = tmoy/2
@fog.ox = tmox+@map.fog_ox
@fog.oy = tmoy+@map.fog_oy
@fog.zoom_x = @map.fog_zoom/100.0
@fog.zoom_y = @map.fog_zoom/100.0
@fog.opacity = @map.fog_opacity
@fog.blend_type = @map.fog_blend_type
@fog.tone = @map.fog_tone
@panorama.update
@fog.update
for sprite in @character_sprites
sprite.update
end
if self.map!=$game_map
if @weather.max>0
@weather.max -= 2
if @weather.max<=0
@weather.max = 0
@weather.type = 0
@weather.ox = 0
@weather.oy = 0
end
end
else
@weather.type = $game_screen.weather_type
@weather.max = $game_screen.weather_max
@weather.ox = tmox
@weather.oy = tmoy
end
@weather.update
@@viewport1.tone = $game_screen.tone
@@viewport3.color = $game_screen.flash_color
@@viewport1.update
@@viewport3.update
end
end

View File

@@ -0,0 +1,34 @@
class Spriteset_Global
attr_reader :playersprite
@@viewport2 = Viewport.new(0,0,Graphics.width,Graphics.height)
@@viewport2.z = 200
def initialize
@playersprite = Sprite_Character.new(Spriteset_Map.viewport,$game_player)
@picture_sprites = []
for i in 1..100
@picture_sprites.push(Sprite_Picture.new(@@viewport2,$game_screen.pictures[i]))
end
@timer_sprite = Sprite_Timer.new
update
end
def dispose
@playersprite.dispose
for sprite in @picture_sprites
sprite.dispose
end
@timer_sprite.dispose
@playersprite = nil
@picture_sprites.clear
@timer_sprite = nil
end
def update
@playersprite.update
for sprite in @picture_sprites
sprite.update
end
@timer_sprite.update
end
end

View File

@@ -0,0 +1,94 @@
=begin
A sprite whose sole purpose is to display an animation. This sprite
can be displayed anywhere on the map and is disposed
automatically when its animation is finished.
Used for grass rustling and so forth.
=end
class AnimationSprite < RPG::Sprite
def initialize(animID,map,tileX,tileY,viewport=nil,tinting=false,height=3)
super(viewport)
@tileX = tileX
@tileY = tileY
self.bitmap = Bitmap.new(1, 1)
self.bitmap.clear
@map = map
setCoords
pbDayNightTint(self) if tinting
self.animation($data_animations[animID],true,height)
end
def setCoords
self.x = ((@tileX * Game_Map::REAL_RES_X - @map.display_x) / Game_Map::X_SUBPIXELS).ceil
self.x += Game_Map::TILE_WIDTH / 2
self.y = ((@tileY * Game_Map::REAL_RES_Y - @map.display_y) / Game_Map::Y_SUBPIXELS).ceil
self.y += Game_Map::TILE_HEIGHT
end
def dispose
self.bitmap.dispose
super
end
def update
if !self.disposed?
setCoords
super
self.dispose if !self.effect?
end
end
end
class Spriteset_Map
alias _animationSprite_initialize initialize
alias _animationSprite_update update
alias _animationSprite_dispose dispose
def initialize(map=nil)
@usersprites=[]
_animationSprite_initialize(map)
end
def addUserAnimation(animID,x,y,tinting=false,height=3)
sprite=AnimationSprite.new(animID,$game_map,x,y,@@viewport1,tinting,height)
addUserSprite(sprite)
return sprite
end
def addUserSprite(sprite)
for i in 0...@usersprites.length
if @usersprites[i]==nil || @usersprites[i].disposed?
@usersprites[i]=sprite
return
end
end
@usersprites.push(sprite)
end
def dispose
_animationSprite_dispose
for i in 0...@usersprites.length
@usersprites[i].dispose
end
@usersprites.clear
end
def update
return if @tilemap.disposed?
if $RPGVX || $PokemonSystem.tilemap==0
if self.map==$game_map
pbDayNightTint(@@viewport3)
else
@@viewport3.tone.set(0,0,0,0)
end
else
pbDayNightTint(@tilemap)
@@viewport3.tone.set(0,0,0,0)
end
_animationSprite_update
for i in 0...@usersprites.length
@usersprites[i].update if !@usersprites[i].disposed?
end
end
end

View File

@@ -0,0 +1,255 @@
#===============================================================================
# Sprite_Shadow (Sprite_Ombre )
# Based on Genzai Kawakami's shadows, dynamisme & features by Rataime, extra
# features Boushy
# Modified by Peter O. to be compatible with Pokémon Essentials
#===============================================================================
SHADOW_WARN = true
class Sprite_Shadow < RPG::Sprite
attr_accessor :character
def initialize(viewport, character = nil,params=[])
super(viewport)
@source = params[0]
@anglemin = (params.size>1) ? params[1] : 0
@anglemax = (params.size>2) ? params[2] : 0
@self_opacity = (params.size>4) ? params[4] : 100
@distancemax = (params.size>3) ? params[3] : 350
@character = character
update
end
def dispose
@chbitmap.dispose if @chbitmap
super
end
def update
if !in_range?(@character, @source, @distancemax)
self.opacity = 0
return
end
super
if @tile_id != @character.tile_id or
@character_name != @character.character_name or
@character_hue != @character.character_hue
@tile_id = @character.tile_id
@character_name = @character.character_name
@character_hue = @character.character_hue
if @tile_id >= 384
@chbitmap.dispose if @chbitmap
@chbitmap = pbGetTileBitmap(@character.map.tileset_name,
@tile_id, @character.character_hue)
self.src_rect.set(0, 0, 32, 32)
@ch = 32
@cw = 32
self.ox = 16
self.oy = 32
else
@chbitmap.dispose if @chbitmap
@chbitmap = AnimatedBitmap.new(
"Graphics/Characters/"+@character.character_name,@character.character_hue)
@cw = @chbitmap.width / 4
@ch = @chbitmap.height / 4
self.ox = @cw / 2
self.oy = @ch
end
end
if @chbitmap.is_a?(AnimatedBitmap)
@chbitmap.update
self.bitmap = @chbitmap.bitmap
else
self.bitmap = @chbitmap
end
self.visible = (not @character.transparent)
if @tile_id == 0
sx = @character.pattern * @cw
sy = (@character.direction - 2) / 2 * @ch
if self.angle > 90 or angle < -90
case @character.direction
when 2; sy = (8- 2) / 2 * @ch
when 4; sy = (6- 2) / 2 * @ch
when 6; sy = (4- 2) / 2 * @ch
when 8; sy = (2- 2) / 2 * @ch
end
end
self.src_rect.set(sx, sy, @cw, @ch)
end
self.x = ScreenPosHelper.pbScreenX(@character)
self.y = ScreenPosHelper.pbScreenY(@character)-5
self.z = ScreenPosHelper.pbScreenZ(@character,@ch)-1
self.zoom_x = ScreenPosHelper.pbScreenZoomX(@character)
self.zoom_y = ScreenPosHelper.pbScreenZoomY(@character)
self.blend_type = @character.blend_type
self.bush_depth = @character.bush_depth
if @character.animation_id != 0
animation = $data_animations[@character.animation_id]
animation(animation, true)
@character.animation_id = 0
end
@deltax = ScreenPosHelper.pbScreenX(@source) - self.x
@deltay = ScreenPosHelper.pbScreenY(@source) - self.y
self.color = Color.new(0, 0, 0)
@distance = ((@deltax ** 2) + (@deltay ** 2))
self.opacity = @self_opacity * 13000 / ((@distance * 370 / @distancemax) + 6000)
self.angle = 57.3 * Math.atan2(@deltax, @deltay)
@angle_trigo = self.angle+90
@angle_trigo += 360 if @angle_trigo < 0
if @anglemin != 0 or @anglemax != 0
if (@angle_trigo < @anglemin or @angle_trigo > @anglemax) and @anglemin < @anglemax
self.opacity = 0
return
end
if (@angle_trigo < @anglemin and @angle_trigo > @anglemax) and @anglemin > @anglemax
self.opacity = 0
return
end
end
end
def in_range?(element, object, range) # From Near's Anti Lag Script, edited
elemScreenX = ScreenPosHelper.pbScreenX(element)
elemScreenY = ScreenPosHelper.pbScreenY(element)
objScreenX = ScreenPosHelper.pbScreenX(object)
objScreenY = ScreenPosHelper.pbScreenY(object)
x = (elemScreenX - objScreenX) * (elemScreenX - objScreenX)
y = (elemScreenY - objScreenY) * (elemScreenY - objScreenY)
r = x + y
return r <= range * range
end
end
#===================================================
# ? CLASS Sprite_Character edit
#===================================================
class Sprite_Character < RPG::Sprite
alias :shadow_initialize :initialize
def initialize(viewport, character = nil)
@ombrelist = []
@character = character
shadow_initialize(viewport, @character)
end
def setShadows(map,shadows)
if character.is_a?(Game_Event) and shadows.length > 0
params = XPML_read(map,"Shadow",@character,4)
if params != nil
for i in 0...shadows.size
@ombrelist.push(Sprite_Shadow.new(viewport, @character, shadows[i]))
end
end
end
if character.is_a?(Game_Player) and shadows.length > 0
for i in 0...shadows.size
@ombrelist.push(Sprite_Shadow.new(viewport, $game_player, shadows[i]))
end
end
update
end
alias shadow_update update
def update
shadow_update
if @ombrelist.length>0
for i in 0...@ombrelist.size
@ombrelist[i].update
end
end
end
end
#===================================================
# ? CLASS Game_Event edit
#===================================================
class Game_Event
attr_accessor :id
end
#===================================================
# ? CLASS Spriteset_Map edit
#===================================================
class Spriteset_Map
attr_accessor :shadows
alias shadow_initialize initialize
def initialize(map=nil)
@shadows = []
warn = false
map = $game_map if !map
for k in map.events.keys.sort
ev = map.events[k]
warn = true if (ev.list != nil and ev.list.length > 0 and
ev.list[0].code == 108 and
(ev.list[0].parameters == ["s"] or ev.list[0].parameters == ["o"]))
params = XPML_read(map,"Shadow Source", ev, 4)
@shadows.push([ev] + params) if params != nil
end
if warn == true and SHADOW_WARN
p "Warning : At least one event on this map uses the obsolete way to add shadows"
end
shadow_initialize(map)
for sprite in @character_sprites
sprite.setShadows(map, @shadows)
end
$scene.spritesetGlobal.playersprite.setShadows(map, @shadows)
end
end
#===================================================
# ? XPML Definition, by Rataime, using ideas from Near Fantastica
#
# Returns nil if the markup wasn't present at all,
# returns [] if there wasn't any parameters, else
# returns a parameters list with "int" converted as int
# eg :
# begin first
# begin second
# param1 1
# param2 two
# begin third
# anything 3
#
# p XPML_read("first", event_id) -> []
# p XPML_read("second", event_id) -> [1, "two"]
# p XPML_read("third", event_id) -> [3]
# p XPML_read("forth", event_id) -> nil
#===================================================
def XPML_read(map,markup,event,max_param_number=0)
parameter_list = nil
return nil if !event || event.list == nil
for i in 0...event.list.size
if event.list[i].code == 108 and
event.list[i].parameters[0].downcase == "begin " + markup.downcase
parameter_list = [] if parameter_list == nil
for j in i+1...event.list.size
if event.list[j].code == 108
parts = event.list[j].parameters[0].split
if parts.size != 1 and parts[0].downcase != "begin"
if parts[1].to_i != 0 or parts[1] == "0"
parameter_list.push(parts[1].to_i)
else
parameter_list.push(parts[1])
end
else
return parameter_list
end
else
return parameter_list
end
return parameter_list if max_param_number != 0 and j == i + max_param_number
end
end
end
return parameter_list
end

View File

@@ -0,0 +1,586 @@
# Particle Engine, Peter O., 2007-11-03
# Based on version 2 by Near Fantastica, 04.01.06
# In turn based on the Particle Engine designed by PinkMan
class Particle_Engine
def initialize(viewport=nil,map=nil)
@map = (map) ? map : $game_map
@viewport = viewport
@effect = []
@disposed = false
@firsttime = true
@effects = {
# PinkMan's Effects
"fire" => Particle_Engine::Fire,
"smoke" => Particle_Engine::Smoke,
"teleport" => Particle_Engine::Teleport,
"spirit" => Particle_Engine::Spirit,
"explosion" => Particle_Engine::Explosion,
"aura" => Particle_Engine::Aura,
# BlueScope's Effects
"soot" => Particle_Engine::Soot,
"sootsmoke" => Particle_Engine::SootSmoke,
"rocket" => Particle_Engine::Rocket,
"fixteleport" => Particle_Engine::FixedTeleport,
"smokescreen" => Particle_Engine::Smokescreen,
"flare" => Particle_Engine::Flare,
"splash" => Particle_Engine::Splash,
# By Peter O.
"starteleport" => Particle_Engine::StarTeleport
}
end
def dispose
return if disposed?
for particle in @effect
next if particle.nil?
particle.dispose
end
@effect.clear
@map = nil
@disposed = true
end
def disposed?
return @disposed
end
def add_effect(event)
@effect[event.id] = pbParticleEffect(event)
end
def remove_effect(event)
return if @effect[event.id].nil?
@effect[event.id].dispose
@effect.delete_at(event.id)
end
def realloc_effect(event,particle)
type = pbEventCommentInput(event, 1, "Particle Engine Type")
if type.nil?
particle.dispose if particle
return nil
end
type = type[0].downcase
cls = @effects[type]
if cls.nil?
particle.dispose if particle
return nil
end
if !particle || !particle.is_a?(cls)
particle.dispose if particle
particle = cls.new(event,@viewport)
end
return particle
end
def pbParticleEffect(event)
return realloc_effect(event,nil)
end
def update
if @firsttime
@firsttime = false
for event in @map.events.values
remove_effect(event)
add_effect(event)
end
end
for i in 0...@effect.length
particle = @effect[i]
next if particle.nil?
if particle.event.pe_refresh
event = particle.event
event.pe_refresh = false
particle = realloc_effect(event,particle)
@effect[i] = particle
end
particle.update if particle
end
end
end
class ParticleEffect
attr_accessor :x, :y, :z
def initialize
@x = 0
@y = 0
@z = 0
end
def update; end
def dispose; end
end
class ParticleSprite
attr_accessor :x, :y, :z, :ox, :oy, :opacity, :bitmap, :blend_type
def initialize(viewport)
@viewport = viewport
@sprite = nil
@x = 0
@y = 0
@z = 0
@ox = 0
@oy = 0
@opacity = 255
@bitmap = nil
@blend_type = 0
@minleft = 0
@mintop = 0
end
def dispose
@sprite.dispose if @sprite
end
def bitmap=(value)
@bitmap = value
if value
@minleft = -value.width
@mintop = -value.height
else
@minleft = 0
@mintop = 0
end
end
def update
w = Graphics.width
h = Graphics.height
if !@sprite && @x>=@minleft && @y>=@mintop && @x<w && @y<h
@sprite = Sprite.new(@viewport)
elsif @sprite && (@x<@minleft || @y<@mintop || @x>=w || @y>=h)
@sprite.dispose
@sprite = nil
end
if @sprite
@sprite.x = @x if @sprite.x!=@x
@sprite.x -= @ox
@sprite.y = @y if @sprite.y!=@y
@sprite.y -= @oy
@sprite.z = @z if @sprite.z!=@z
@sprite.opacity = @opacity if @sprite.opacity!=@opacity
@sprite.blend_type = @blend_type if @sprite.blend_type!=@blend_type
@sprite.bitmap = @bitmap if @sprite.bitmap!=@bitmap
end
end
end
class ParticleEffect_Event < ParticleEffect
attr_accessor :event
def initialize(event,viewport=nil)
@event = event
@viewport = viewport
@particles = []
@bitmaps = {}
end
def setParameters(params)
@randomhue,@leftright,@fade,
@maxparticless,@hue,@slowdown,
@ytop,@ybottom,@xleft,@xright,
@xgravity,@ygravity,@xoffset,@yoffset,
@opacityvar,@originalopacity = params
end
def loadBitmap(filename,hue)
key = [filename,hue]
bitmap = @bitmaps[key]
if !bitmap || bitmap.disposed?
bitmap = AnimatedBitmap.new("Graphics/Fogs/"+filename,hue).deanimate
@bitmaps[key] = bitmap
end
return bitmap
end
def initParticles(filename,opacity,zOffset=0,blendtype=1)
@particles = []
@particlex = []
@particley = []
@opacity = []
@startingx = self.x + @xoffset
@startingy = self.y + @yoffset
@screen_x = self.x
@screen_y = self.y
@real_x = @event.real_x
@real_y = @event.real_y
@filename = filename
@zoffset = zOffset
@bmwidth = 32
@bmheight = 32
for i in 0...@maxparticless
@particlex[i] = -@xoffset
@particley[i] = -@yoffset
@particles[i] = ParticleSprite.new(@viewport)
@particles[i].bitmap = loadBitmap(filename, @hue) if filename
if i==0 && @particles[i].bitmap
@bmwidth = @particles[i].bitmap.width
@bmheight = @particles[i].bitmap.height
end
@particles[i].blend_type = blendtype
@particles[i].y = @startingy
@particles[i].x = @startingx
@particles[i].z = self.z+zOffset
@opacity[i] = rand(opacity/4)
@particles[i].opacity = @opacity[i]
@particles[i].update
end
end
def x; return ScreenPosHelper.pbScreenX(@event); end
def y; return ScreenPosHelper.pbScreenY(@event); end
def z; return ScreenPosHelper.pbScreenZ(@event); end
def update
if @viewport &&
(@viewport.rect.x >= Graphics.width ||
@viewport.rect.y >= Graphics.height)
return
end
selfX = self.x
selfY = self.y
selfZ = self.z
newRealX = @event.real_x
newRealY = @event.real_y
@startingx = selfX + @xoffset
@startingy = selfY + @yoffset
@__offsetx = (@real_x==newRealX) ? 0 : selfX-@screen_x
@__offsety = (@real_y==newRealY) ? 0 : selfY-@screen_y
@screen_x = selfX
@screen_y = selfY
@real_x = newRealX
@real_y = newRealY
if @opacityvar>0 && @viewport
opac = 255.0/@opacityvar
minX = opac*(-@xgravity*1.0 / @slowdown).floor + @startingx
maxX = opac*(@xgravity*1.0 / @slowdown).floor + @startingx
minY = opac*(-@ygravity*1.0 / @slowdown).floor + @startingy
maxY = @startingy
minX -= @bmwidth
minY -= @bmheight
maxX += @bmwidth
maxY += @bmheight
if maxX<0 || maxY<0 || minX>=Graphics.width || minY>=Graphics.height
# echo "skipped"
return
end
end
particleZ = selfZ+@zoffset
for i in 0...@maxparticless
@particles[i].z = particleZ
if @particles[i].y <= @ytop
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].x <= @xleft
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].y >= @ybottom
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].x >= @xright
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @fade == 0
if @opacity[i] <= 0
@opacity[i] = @originalopacity
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
else
if @opacity[i] <= 0
@opacity[i] = 250
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
end
calcParticlePos(i)
if @randomhue == 1
@hue += 0.5
@hue = 0 if @hue >= 360
@particles[i].bitmap = loadBitmap(@filename, @hue) if @filename
end
@opacity[i] = @opacity[i] - rand(@opacityvar)
@particles[i].opacity = @opacity[i]
@particles[i].update
end
end
def calcParticlePos(i)
@leftright = rand(2)
if @leftright == 1
xo = -@xgravity*1.0 / @slowdown
else
xo = @xgravity*1.0 / @slowdown
end
yo = -@ygravity*1.0 / @slowdown
@particlex[i] += xo
@particley[i] += yo
@particlex[i] -= @__offsetx
@particley[i] -= @__offsety
@particlex[i] = @particlex[i].floor
@particley[i] = @particley[i].floor
@particles[i].x = @particlex[i]+@startingx+@xoffset
@particles[i].y = @particley[i]+@startingy+@yoffset
end
def dispose
for particle in @particles
particle.dispose
end
for bitmap in @bitmaps.values
bitmap.dispose
end
@particles.clear
@bitmaps.clear
end
end
class Particle_Engine::Fire < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,40,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Smoke < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,80,20,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",250)
end
end
class Particle_Engine::Teleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,1,1,10,rand(360),1,-64,
Graphics.height,-64,Graphics.width,0,3,-8,-15,20,0])
initParticles("wideportal",250)
for i in 0...@maxparticless
@particles[i].ox = 16
@particles[i].oy = 16
end
end
end
class Particle_Engine::Spirit < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,0,1,20,rand(360),0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Explosion < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("explosion",250)
end
end
class Particle_Engine::Aura < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,0,1,-64,
Graphics.height,-64,Graphics.width,2,2,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Soot < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,20,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",100,0,2)
end
end
class Particle_Engine::SootSmoke < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,30,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",100,0)
for i in 0...@maxparticless
@particles[i].blend_type = rand(6) < 3 ? 1 : 2
end
end
end
class Particle_Engine::Rocket < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,60,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0,-5,-15,5,80])
initParticles("smoke",100,-1)
end
end
class Particle_Engine::FixedTeleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,0,1,10,rand(360),1,
-Graphics.height,Graphics.height,0,Graphics.width,0,3,-8,-15,20,0])
initParticles("wideportal",250)
for i in 0...@maxparticless
@particles[i].ox = 16
@particles[i].oy = 16
end
end
end
# By Peter O.
class Particle_Engine::StarTeleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,10,0,1,
-Graphics.height,Graphics.height,0,Graphics.width,0,3,-8,-15,10,0])
initParticles("star",250)
for i in 0...@maxparticless
@particles[i].ox = 48
@particles[i].oy = 48
end
end
end
class Particle_Engine::Smokescreen < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,250,0,0.2,-64,
Graphics.height,-64,Graphics.width,0.8,0.8,-5,-15,5,80])
initParticles(nil,100)
for i in 0...@maxparticless
rnd = rand(3)
@opacity[i] = (rnd==0) ? 1 : 100
filename = (rnd==0) ? "explosionsmoke" : "smoke"
@particles[i].bitmap = loadBitmap(filename, @hue)
end
end
def calcParticlePos(i)
if @randomhue==1
filename = (rand(3)==0) ? "explosionsmoke" : "smoke"
@particles[i].bitmap = loadBitmap(filename, @hue)
end
multiple = 1.7
xgrav = @xgravity*multiple/@slowdown
xgrav = -xgrav if (rand(2)==1)
ygrav = @ygravity*multiple/@slowdown
ygrav = -ygrav if (rand(2)==1)
@particlex[i] += xgrav
@particley[i] += ygrav
@particlex[i] -= @__offsetx
@particley[i] -= @__offsety
@particlex[i] = @particlex[i].floor
@particley[i] = @particley[i].floor
@particles[i].x = @particlex[i]+@startingx+@xoffset
@particles[i].y = @particley[i]+@startingy+@yoffset
end
end
class Particle_Engine::Flare < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,30,10,1,-64,
Graphics.height,-64,Graphics.width,2,2,-5,-12,30,0])
initParticles("particle",255)
end
end
class Particle_Engine::Splash < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,30,255,1,-64,
Graphics.height,-64,Graphics.width,4,2,-5,-12,30,0])
initParticles("smoke",50)
end
def update
super
for i in 0...@maxparticless
@particles[i].opacity = 50
@particles[i].update
end
end
end
class Game_Event < Game_Character
attr_accessor :pe_refresh
alias nf_particles_game_map_initialize initialize
def initialize(map_id,event,map=nil)
@pe_refresh = false
begin
nf_particles_game_map_initialize(map_id, event, map)
rescue ArgumentError
nf_particles_game_map_initialize(map_id, event)
end
end
alias nf_particles_game_map_refresh refresh
def refresh
nf_particles_game_map_refresh
@pe_refresh = true
end
end

View File

@@ -0,0 +1,947 @@
#===============================================================================
#
#===============================================================================
class CustomTilemapAutotiles
attr_accessor :changed
def initialize
@changed = true
@tiles = [nil,nil,nil,nil,nil,nil,nil]
end
def [](i)
return @tiles[i]
end
def []=(i,value)
@tiles[i] = value
@changed = true
end
end
#Console::setup_console
class CustomTilemapSprite < Sprite
end
#===============================================================================
#
#===============================================================================
class CustomTilemap
attr_reader :tileset
attr_reader :autotiles
attr_reader :map_data
attr_reader :flash_data
attr_reader :priorities
attr_reader :terrain_tags
attr_reader :visible
attr_reader :viewport
attr_reader :graphicsWidth
attr_reader :graphicsHeight
attr_accessor :ox
attr_accessor :oy
attr_accessor :tone
attr_accessor :color
Autotiles = [
[ [27, 28, 33, 34], [ 5, 28, 33, 34], [27, 6, 33, 34], [ 5, 6, 33, 34],
[27, 28, 33, 12], [ 5, 28, 33, 12], [27, 6, 33, 12], [ 5, 6, 33, 12] ],
[ [27, 28, 11, 34], [ 5, 28, 11, 34], [27, 6, 11, 34], [ 5, 6, 11, 34],
[27, 28, 11, 12], [ 5, 28, 11, 12], [27, 6, 11, 12], [ 5, 6, 11, 12] ],
[ [25, 26, 31, 32], [25, 6, 31, 32], [25, 26, 31, 12], [25, 6, 31, 12],
[15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
[ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
[39, 40, 45, 46], [ 5, 40, 45, 46], [39, 6, 45, 46], [ 5, 6, 45, 46] ],
[ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
[17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
[ [37, 38, 43, 44], [37, 6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
[37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1, 2, 7, 8] ]
]
Animated_Autotiles_Frames = 5*Graphics.frame_rate/20 # Frequency of updating animated autotiles
FlashOpacity = [100,90,80,70,80,90]
def initialize(viewport)
@tileset = nil # Refers to Map Tileset Name
@autotiles = CustomTilemapAutotiles.new
@map_data = nil # Refers to 3D Array Of Tile Settings
@flash_data = nil # Refers to 3D Array of Tile Flashdata
@priorities = nil # Refers to Tileset Priorities
@terrain_tags = nil # Refers to Tileset Terrain Tags
@visible = true # Refers to Tileset Visibleness
@ox = 0 # Bitmap Offsets
@oy = 0 # Bitmap Offsets
@plane = false
@haveGraphicsWH = (Graphics.width!=nil rescue false)
if @haveGraphicsWH
@graphicsWidth = Graphics.width
@graphicsHeight = Graphics.height
else
@graphicsWidth = 640
@graphicsHeight = 480
end
@tileWidth = Game_Map::TILE_WIDTH rescue 32
@tileHeight = Game_Map::TILE_HEIGHT rescue 32
@tileSrcWidth = 32
@tileSrcHeight = 32
@diffsizes = (@tileWidth!=@tileSrcWidth) || (@tileHeight!=@tileSrcHeight)
@tone = Tone.new(0,0,0,0)
@oldtone = Tone.new(0,0,0,0)
@color = Color.new(0,0,0,0)
@oldcolor = Color.new(0,0,0,0)
@selfviewport = Viewport.new(0,0,graphicsWidth,graphicsHeight)
@viewport = (viewport) ? viewport : @selfviewport
@tiles = []
@autotileInfo = []
@regularTileInfo = []
@oldOx = 0
@oldOy = 0
@oldViewportOx = 0
@oldViewportOy = 0
@layer0 = CustomTilemapSprite.new(viewport)
@layer0.visible = true
@nowshown = false
@layer0.bitmap = Bitmap.new([graphicsWidth+320,1].max,[graphicsHeight+320,1].max)
@layer0.z = 0
@layer0.ox = 0
@layer0.oy = 0
@oxLayer0 = 0
@oyLayer0 = 0
@flash = nil
@oxFlash = 0
@oyFlash = 0
@priotiles = []
@priotilesfast = []
@prioautotiles = []
@autosprites = []
@framecount = [0,0,0,0,0,0,0,0] # For autotiles
@tilesetChanged = true
@flashChanged = false
@firsttime = true
@disposed = false
@usedsprites = false
@layer0clip = true
@firsttimeflash = true
@fullyrefreshed = false
@fullyrefreshedautos = false
end
def dispose
return if disposed?
@help.dispose if @help
@help = nil
i = 0; len = @autotileInfo.length; while i<len
if @autotileInfo[i]
@autotileInfo[i].dispose
@autotileInfo[i] = nil
end
i += 1
end
i = 0; len = @regularTileInfo.length; while i<len
if @regularTileInfo[i]
@regularTileInfo[i].dispose
@regularTileInfo[i] = nil
end
i += 1
end
i = 0; len = @tiles.length; while i<len
@tiles[i].dispose
@tiles[i] = nil
i += 2
end
i = 0; len = @autosprites.length; while i<len
@autosprites[i].dispose
@autosprites[i] = nil
i += 2
end
if @layer0
@layer0.bitmap.dispose if !@layer0.disposed?
@layer0.bitmap = nil if !@layer0.disposed?
@layer0.dispose
@layer0 = nil
end
if @flash
@flash.bitmap.dispose if !@flash.disposed?
@flash.bitmap = nil if !@flash.disposed?
@flash.dispose
@flash = nil
end
for i in 0...7
self.autotiles[i] = nil
end
@tiles.clear
@autosprites.clear
@autotileInfo.clear
@regularTileInfo.clear
@tilemap = nil
@tileset = nil
@priorities = nil
@selfviewport.dispose
@selfviewport = nil
@disposed = true
end
def disposed?
return @disposed
end
def flash_data=(value)
@flash_data = value
@flashChanged = true
end
def map_data=(value)
@map_data = value
@tilesetChanged = true
end
def priorities=(value)
@priorities = value
@tilesetChanged = true
end
def terrain_tags=(value)
@terrain_tags = value
@tilesetChanged = true
end
def tileset=(value)
@tileset = value
@tilesetChanged = true
end
def getResizeFactor
return $ResizeFactor || 1.0
end
def ox=(val)
rf = getResizeFactor
if rf!=1.0
val = (val*rf).to_i
val = (val/rf).to_i
end
wasshown = self.shown?
@ox = val.floor
@nowshown = (!wasshown && self.shown?)
end
def oy=(val)
rf = getResizeFactor
if rf!=1.0
val = (val*rf).to_i
val = (val/rf).to_i
end
wasshown = self.shown?
@oy = val.floor
@nowshown = (!wasshown && self.shown?)
end
def visible=(val)
wasshown = @visible
@visible = val
@nowshown = (!wasshown && val)
end
def shown?
return false if !@visible
xsize = @map_data.xsize
xStart = @ox/@tileWidth - 1
xStart = 0 if xStart<0
xStart = xsize-1 if xStart>=xsize
xEnd = (@ox+@viewport.rect.width)/@tileWidth + 1
xEnd = 0 if xEnd<0
xEnd = xsize-1 if xEnd>=xsize
return false if xStart>=xEnd
ysize = @map_data.ysize
yStart = @oy/@tileHeight - 1
yStart = 0 if yStart<0
yStart = ysize-1 if yStart>=ysize
yEnd = (@oy+@viewport.rect.height)/@tileHeight + 1
yEnd = 0 if yEnd<0
yEnd = ysize-1 if yEnd>=ysize
return false if yStart>=yEnd
return true
end
def autotileNumFrames(id)
autotile = @autotiles[id/48-1]
return 0 if !autotile || autotile.disposed?
frames = 1
if autotile.height==@tileHeight
frames = autotile.width/@tileWidth
else
frames = autotile.width/(3*@tileWidth)
end
return frames
end
def autotileFrame(id)
autotile = @autotiles[id/48-1]
return -1 if !autotile || autotile.disposed?
frames = 1
if autotile.height==@tileHeight
frames = autotile.width/@tileWidth
else
frames = autotile.width/(3*@tileWidth)
end
return (Graphics.frame_count/Animated_Autotiles_Frames)%frames
end
def repaintAutotiles
for i in 0...@autotileInfo.length
next if !@autotileInfo[i]
frame = autotileFrame(i)
@autotileInfo[i].clear
bltAutotile(@autotileInfo[i],0,0,i,frame)
end
end
def bltAutotile(bitmap,x,y,id,frame)
return if frame<0
autotile = @autotiles[id/48-1]
return if !autotile || autotile.disposed?
if autotile.height==@tileSrcHeight
anim = frame*@tileSrcWidth
src_rect = Rect.new(anim,0,@tileSrcWidth,@tileSrcHeight)
if @diffsizes
bitmap.stretch_blt(Rect.new(x,y,@tileWidth,@tileHeight),autotile,src_rect)
else
bitmap.blt(x,y,autotile,src_rect)
end
else
anim = frame*3*@tileSrcWidth
id %= 48
tiles = Autotiles[id>>3][id&7]
src = Rect.new(0,0,0,0)
halfTileWidth = @tileWidth>>1
halfTileHeight = @tileHeight>>1
halfTileSrcWidth = @tileSrcWidth>>1
halfTileSrcHeight = @tileSrcHeight>>1
for i in 0...4
tile_position = tiles[i] - 1
src.set( (tile_position % 6)*halfTileSrcWidth + anim,
(tile_position / 6)*halfTileSrcHeight, halfTileSrcWidth, halfTileSrcHeight)
if @diffsizes
bitmap.stretch_blt(
Rect.new(i%2*halfTileWidth+x,i/2*halfTileHeight+y,halfTileWidth,halfTileHeight),
autotile,src)
else
bitmap.blt(i%2*halfTileWidth+x,i/2*halfTileHeight+y, autotile, src)
end
end
end
end
def getAutotile(sprite,id)
frames = @framecount[id/48-1]
if frames<=1
anim = 0
else
anim = (Graphics.frame_count/Animated_Autotiles_Frames)%frames
end
return if anim<0
bitmap = @autotileInfo[id]
if !bitmap
bitmap = Bitmap.new(@tileWidth,@tileHeight)
bltAutotile(bitmap,0,0,id,anim)
@autotileInfo[id] = bitmap
end
sprite.bitmap = bitmap if sprite.bitmap!=bitmap
end
def getRegularTile(sprite,id)
if @diffsizes
bitmap = @regularTileInfo[id]
if !bitmap
bitmap = Bitmap.new(@tileWidth,@tileHeight)
rect = Rect.new(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
@tileSrcWidth,@tileSrcHeight)
bitmap.stretch_blt(Rect.new(0,0,@tileWidth,@tileHeight),@tileset,rect)
@regularTileInfo[id] = bitmap
end
sprite.bitmap = bitmap if sprite.bitmap!=bitmap
else
sprite.bitmap = @tileset if sprite.bitmap!=@tileset
sprite.src_rect.set(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
@tileSrcWidth,@tileSrcHeight)
end
end
def addTile(tiles,count,xpos,ypos,id)
terrain = @terrain_tags[id]
priority = @priorities[id]
if id>=384
if count>=tiles.length
sprite = CustomTilemapSprite.new(@viewport)
tiles.push(sprite,0)
else
sprite = tiles[count]
tiles[count+1] = 0
end
sprite.visible = @visible
sprite.x = xpos
sprite.y = ypos
sprite.tone = @tone
sprite.color = @color
getRegularTile(sprite,id)
else
if count>=tiles.length
sprite = CustomTilemapSprite.new(@viewport)
tiles.push(sprite,1)
else
sprite = tiles[count]
tiles[count+1] = 1
end
sprite.visible = @visible
sprite.x = xpos
sprite.y = ypos
sprite.tone = @tone
sprite.color = @color
getAutotile(sprite,id)
end
if PBTerrain.hasReflections?(terrain)
spriteZ = -100
elsif $PokemonGlobal.bridge>0 && PBTerrain.isBridge?(terrain)
spriteZ = 1
else
spriteZ = (priority==0) ? 0 : ypos+priority*32+32
end
sprite.z = spriteZ
count += 2
return count
end
def refresh_flash
if @flash_data && !@flash
@flash = CustomTilemapSprite.new(viewport)
@flash.visible = true
@flash.z = 1
@flash.tone = tone
@flash.color = color
@flash.blend_type = 1
@flash.bitmap = Bitmap.new([graphicsWidth*2,1].max,[graphicsHeight*2,1].max)
@firsttimeflash = true
elsif !@flash_data && @flash
@flash.bitmap.dispose if @flash.bitmap
@flash.dispose
@flash = nil
@firsttimeflash = false
end
end
def refreshFlashSprite
return if !@flash || @flash_data.nil?
ptX = @ox-@oxFlash
ptY = @oy-@oyFlash
if !@firsttimeflash && !@usedsprites &&
ptX>=0 && ptX+@viewport.rect.width<=@flash.bitmap.width &&
ptY>=0 && ptY+@viewport.rect.height<=@flash.bitmap.height
@flash.ox = 0
@flash.oy = 0
@flash.src_rect.set(ptX.round,ptY.round,
@viewport.rect.width,@viewport.rect.height)
return
end
width = @flash.bitmap.width
height = @flash.bitmap.height
bitmap = @flash.bitmap
ysize = @map_data.ysize
xsize = @map_data.xsize
zsize = @map_data.zsize
@firsttimeflash = false
@oxFlash = @ox-(width>>2)
@oyFlash = @oy-(height>>2)
@flash.ox = 0
@flash.oy = 0
@flash.src_rect.set(width>>2,height>>2,
@viewport.rect.width,@viewport.rect.height)
@flash.bitmap.clear
@oxFlash = @oxFlash.floor
@oyFlash = @oyFlash.floor
xStart = @oxFlash/@tileWidth
xStart = 0 if xStart<0
yStart = @oyFlash/@tileHeight
yStart = 0 if yStart<0
xEnd = xStart+(width/@tileWidth)+1
yEnd = yStart+(height/@tileHeight)+1
xEnd = xsize if xEnd>=xsize
yEnd = ysize if yEnd>=ysize
if xStart<xEnd && yStart<yEnd
yrange = yStart...yEnd
xrange = xStart...xEnd
tmpcolor = Color.new(0,0,0,0)
for y in yrange
ypos = (y*@tileHeight)-@oyFlash
for x in xrange
xpos = (x*@tileWidth)-@oxFlash
id = @flash_data[x, y, 0]
r = (id>>8)&15
g = (id>>4)&15
b = (id)&15
tmpcolor.set(r<<4,g<<4,b<<4)
bitmap.fill_rect(xpos,ypos,@tileWidth,@tileHeight,tmpcolor)
end
end
end
end
def refresh_tileset
i = 0; len = @regularTileInfo.length; while i<len
if @regularTileInfo[i]
@regularTileInfo[i].dispose
@regularTileInfo[i] = nil
end
i += 1
end
@regularTileInfo.clear
@priotiles.clear
ysize = @map_data.ysize
xsize = @map_data.xsize
zsize = @map_data.zsize
if xsize>100 || ysize>100
@fullyrefreshed = false
else
for z in 0...zsize
for y in 0...ysize
for x in 0...xsize
id = @map_data[x, y, z]
next if id==0
next if @priorities[id]==0 && !PBTerrain.hasReflections?(@terrain_tags[id])
@priotiles.push([x,y,z,id])
end
end
end
@fullyrefreshed = true
end
end
def refresh_autotiles
i = 0; len = @autotileInfo.length; while i<len
if @autotileInfo[i]
@autotileInfo[i].dispose
@autotileInfo[i] = nil
end
i += 1
end
i = 0; len = @autosprites.length; while i<len
if @autosprites[i]
@autosprites[i].dispose
@autosprites[i] = nil
end
i += 2
end
@autosprites.clear
@autotileInfo.clear
@prioautotiles.clear
@priorect = nil
@priorectautos = nil
hasanimated = false
for i in 0...7
numframes = autotileNumFrames(48*(i+1))
hasanimated = true if numframes>=2
@framecount[i] = numframes
end
if hasanimated
ysize = @map_data.ysize
xsize = @map_data.xsize
zsize = @map_data.zsize
if xsize>100 || ysize>100
@fullyrefreshedautos = false
else
for y in 0...ysize
for x in 0...xsize
haveautotile = false
for z in 0...zsize
id = @map_data[x, y, z]
next if id==0 || id>=384
next if @priorities[id]!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
next if @framecount[id/48-1]<2
haveautotile = true
break
end
@prioautotiles.push([x,y]) if haveautotile
end
end
@fullyrefreshedautos = true
end
else
@fullyrefreshedautos = true
end
end
def refreshLayer0(autotiles=false)
return true if autotiles && !shown?
ptX = @ox-@oxLayer0
ptY = @oy-@oyLayer0
if !autotiles && !@firsttime && !@usedsprites &&
ptX>=0 && ptX+@viewport.rect.width<=@layer0.bitmap.width &&
ptY>=0 && ptY+@viewport.rect.height<=@layer0.bitmap.height
if @layer0clip && @viewport.ox==0 && @viewport.oy==0
@layer0.ox = 0
@layer0.oy = 0
@layer0.src_rect.set(ptX.round,ptY.round,
@viewport.rect.width,@viewport.rect.height)
else
@layer0.ox = ptX.round
@layer0.oy = ptY.round
@layer0.src_rect.set(0,0,@layer0.bitmap.width,@layer0.bitmap.height)
end
return true
end
width = @layer0.bitmap.width
height = @layer0.bitmap.height
bitmap = @layer0.bitmap
ysize = @map_data.ysize
xsize = @map_data.xsize
zsize = @map_data.zsize
twidth = @tileWidth
theight = @tileHeight
mapdata = @map_data
if autotiles
return true if @fullyrefreshedautos && @prioautotiles.length==0
xStart = @oxLayer0/twidth
xStart = 0 if xStart<0
yStart = @oyLayer0/theight
yStart = 0 if yStart<0
xEnd = xStart+(width/twidth)+1
yEnd = yStart+(height/theight)+1
xEnd = xsize if xEnd>xsize
yEnd = ysize if yEnd>ysize
return true if xStart>=xEnd || yStart>=yEnd
trans = Color.new(0,0,0,0)
temprect = Rect.new(0,0,0,0)
tilerect = Rect.new(0,0,twidth,theight)
zrange = 0...zsize
overallcount = 0
count = 0
if !@fullyrefreshedautos
for y in yStart..yEnd
for x in xStart..xEnd
haveautotile = false
for z in zrange
id = mapdata[x, y, z]
next if !id || id<48 || id>=384
prioid = @priorities[id]
next if prioid!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
fcount = @framecount[id/48-1]
next if !fcount || fcount<2
if !haveautotile
haveautotile = true
overallcount += 1
xpos = (x*twidth)-@oxLayer0
ypos = (y*theight)-@oyLayer0
bitmap.fill_rect(xpos,ypos,twidth,theight,trans) if overallcount<=2000
break
end
end
for z in zrange
id = mapdata[x,y,z]
next if !id || id<48
prioid = @priorities[id]
next if prioid!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
if overallcount>2000
xpos = (x*twidth)-@oxLayer0
ypos = (y*theight)-@oyLayer0
count = addTile(@autosprites,count,xpos,ypos,id)
next
elsif id>=384
temprect.set(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
@tileSrcWidth,@tileSrcHeight)
xpos = (x*twidth)-@oxLayer0
ypos = (y*theight)-@oyLayer0
if @diffsizes
bitmap.stretch_blt(Rect.new(xpos,ypos,twidth,theight),@tileset,temprect)
else
bitmap.blt(xpos,ypos,@tileset,temprect)
end
else
tilebitmap = @autotileInfo[id]
if !tilebitmap
anim = autotileFrame(id)
next if anim<0
tilebitmap = Bitmap.new(twidth,theight)
bltAutotile(tilebitmap,0,0,id,anim)
@autotileInfo[id] = tilebitmap
end
xpos = (x*twidth)-@oxLayer0
ypos = (y*theight)-@oyLayer0
bitmap.blt(xpos,ypos,tilebitmap,tilerect)
end
end
end
end
Graphics.frame_reset
else
if !@priorect || !@priorectautos ||
@priorect[0]!=xStart || @priorect[1]!=yStart ||
@priorect[2]!=xEnd || @priorect[3]!=yEnd
@priorectautos = @prioautotiles.find_all { |tile|
x = tile[0]
y = tile[1]
# "next" means "return" here
next !(x<xStart || x>xEnd || y<yStart || y>yEnd)
}
@priorect = [xStart,yStart,xEnd,yEnd]
end
# echoln ["autos",@priorect,@priorectautos.length,@prioautotiles.length]
for tile in @priorectautos
x = tile[0]
y = tile[1]
overallcount+=1
xpos = (x*twidth)-@oxLayer0
ypos = (y*theight)-@oyLayer0
bitmap.fill_rect(xpos,ypos,twidth,theight,trans)
z = 0
while z<zsize
id = mapdata[x,y,z]
z += 1
next if !id || id<48
prioid = @priorities[id]
next if prioid!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
if id>=384
temprect.set(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
@tileSrcWidth,@tileSrcHeight)
if @diffsizes
bitmap.stretch_blt(Rect.new(xpos,ypos,twidth,theight),@tileset,temprect)
else
bitmap.blt(xpos,ypos,@tileset,temprect)
end
else
tilebitmap = @autotileInfo[id]
if !tilebitmap
anim = autotileFrame(id)
next if anim<0
tilebitmap = Bitmap.new(twidth,theight)
bltAutotile(tilebitmap,0,0,id,anim)
@autotileInfo[id] = tilebitmap
end
bitmap.blt(xpos,ypos,tilebitmap,tilerect)
end
end
end
Graphics.frame_reset if overallcount>500
end
@usedsprites = false
return true
end
return false if @usedsprites
@firsttime = false
@oxLayer0 = @ox-(width>>2)
@oyLayer0 = @oy-(height>>2)
if @layer0clip
@layer0.ox = 0
@layer0.oy = 0
@layer0.src_rect.set(width>>2,height>>2,
@viewport.rect.width,@viewport.rect.height)
else
@layer0.ox = (width>>2)
@layer0.oy = (height>>2)
end
@layer0.bitmap.clear
@oxLayer0 = @oxLayer0.round
@oyLayer0 = @oyLayer0.round
xStart = @oxLayer0/twidth
xStart = 0 if xStart<0
yStart = @oyLayer0/theight
yStart = 0 if yStart<0
xEnd = xStart+(width/twidth)+1
yEnd = yStart+(height/theight)+1
xEnd = xsize if xEnd>=xsize
yEnd = ysize if yEnd>=ysize
if xStart<xEnd && yStart<yEnd
tmprect = Rect.new(0,0,0,0)
yrange = yStart...yEnd
xrange = xStart...xEnd
for z in 0...zsize
for y in yrange
ypos = (y*theight)-@oyLayer0
for x in xrange
xpos = (x*twidth)-@oxLayer0
id = mapdata[x, y, z]
next if id==0 || @priorities[id]!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
if id>=384
tmprect.set( ((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
@tileSrcWidth,@tileSrcHeight)
if @diffsizes
bitmap.stretch_blt(Rect.new(xpos,ypos,twidth,theight),@tileset,tmprect)
else
bitmap.blt(xpos,ypos,@tileset,tmprect)
end
else
frames = @framecount[id/48-1]
if frames<=1
frame = 0
else
frame = (Graphics.frame_count/Animated_Autotiles_Frames)%frames
end
bltAutotile(bitmap,xpos,ypos,id,frame)
end
end
end
end
Graphics.frame_reset
end
return true
end
def refresh(autotiles=false)
@oldOx = @ox
@oldOy = @oy
usesprites = false
if @layer0
@layer0.visible = @visible
usesprites = !refreshLayer0(autotiles)
return if autotiles && !usesprites
else
usesprites = true
end
refreshFlashSprite
vpx = @viewport.rect.x
vpy = @viewport.rect.y
vpr = @viewport.rect.width+vpx
vpb = @viewport.rect.height+vpy
xsize = @map_data.xsize
ysize = @map_data.ysize
minX = (@ox/@tileWidth)-1
minX = 0 if minX<0
minX = xsize-1 if minX>=xsize
maxX = ((@ox+@viewport.rect.width)/@tileWidth)+1
maxX = 0 if maxX<0
maxX = xsize-1 if maxX>=xsize
minY = (@oy/@tileHeight)-1
minY = 0 if minY<0
minY = ysize-1 if minY>=ysize
maxY = ((@oy+@viewport.rect.height)/@tileHeight)+1
maxY = 0 if maxY<0
maxY = ysize-1 if maxY>=ysize
count = 0
if minX<maxX && minY<maxY
@usedsprites = usesprites || @usedsprites
if @layer0
@layer0.visible = false if usesprites
end
if @fullyrefreshed
if !@priotilesrect || !@priotilesfast ||
@priotilesrect[0]!=minX ||
@priotilesrect[1]!=minY ||
@priotilesrect[2]!=maxX ||
@priotilesrect[3]!=maxY
@priotilesfast = @priotiles.find_all { |tile|
x = tile[0]
y = tile[1]
# "next" means "return" here
next !(x<minX || x>maxX || y<minY || y>maxY)
}
@priotilesrect = [minX,minY,maxX,maxY]
end
# echoln [minX,minY,maxX,maxY,@priotilesfast.length,@priotiles.length]
for prio in @priotilesfast
xpos = (prio[0]*@tileWidth)-@ox
ypos = (prio[1]*@tileHeight)-@oy
count = addTile(@tiles,count,xpos,ypos,prio[3])
end
else
if !@priotilesrect || !@priotilesfast ||
@priotilesrect[0]!=minX ||
@priotilesrect[1]!=minY ||
@priotilesrect[2]!=maxX ||
@priotilesrect[3]!=maxY
@priotilesfast=[]
for z in 0...@map_data.zsize
for y in minY..maxY
for x in minX..maxX
id = @map_data[x, y, z]
next if id==0
next if @priorities[id]==0 && !PBTerrain.hasReflections?(@terrain_tags[id])
@priotilesfast.push([x,y,z,id])
end
end
end
@priotilesrect = [minX,minY,maxX,maxY]
end
for prio in @priotilesfast
xpos = (prio[0]*@tileWidth)-@ox
ypos = (prio[1]*@tileHeight)-@oy
count = addTile(@tiles,count,xpos,ypos,prio[3])
end
end
end
if count<@tiles.length
bigchange = (count<=(@tiles.length*2/3)) && (@tiles.length*2/3)>25
j = count; len = @tiles.length; while j<len
sprite = @tiles[j]
@tiles[j+1] = -1
if bigchange
sprite.dispose
@tiles[j] = nil
@tiles[j+1] = nil
elsif !@tiles[j].disposed?
sprite.visible = false if sprite.visible
end
j += 2
end
@tiles.compact! if bigchange
end
end
def update
if @haveGraphicsWH
@graphicsWidth = Graphics.width
@graphicsHeight = Graphics.height
end
# Update tone
if @oldtone!=@tone
@layer0.tone = @tone
@flash.tone = @tone if @flash
for sprite in @autosprites
sprite.tone = @tone if sprite.is_a?(Sprite)
end
for sprite in @tiles
sprite.tone = @tone if sprite.is_a?(Sprite)
end
@oldtone = @tone.clone
end
# Update color
if @oldcolor!=@color
@layer0.color = @color
@flash.color = @color if @flash
for sprite in @autosprites
sprite.color = @color if sprite.is_a?(Sprite)
end
for sprite in @tiles
sprite.color = @color if sprite.is_a?(Sprite)
end
@oldcolor = @color.clone
end
# Refresh anything that has changed
if @autotiles.changed
refresh_autotiles
repaintAutotiles
end
if @flashChanged
refresh_flash
end
if @tilesetChanged
refresh_tileset
end
if @flash
@flash.opacity = FlashOpacity[(Graphics.frame_count/2) % 6]
end
mustrefresh = (@oldOx!=@ox || @oldOy!=@oy || @tilesetChanged || @autotiles.changed)
if @viewport.ox!=@oldViewportOx || @viewport.oy!=@oldViewportOy
mustrefresh = true
@oldViewportOx = @viewport.ox
@oldViewportOy = @viewport.oy
end
refresh if mustrefresh
if (Graphics.frame_count % Animated_Autotiles_Frames == 0) || @nowshown
repaintAutotiles
refresh(true)
end
@nowshown = false
@autotiles.changed = false
@tilesetChanged = false
end
end

View File

@@ -0,0 +1,447 @@
def bltMinimapAutotile(dstBitmap,x,y,srcBitmap,id)
return if id>=48 || !srcBitmap || srcBitmap.disposed?
anim=0
cxTile=3
cyTile=3
tiles = TileDrawingHelper::Autotiles[id>>3][id&7]
src=Rect.new(0,0,0,0)
for i in 0...4
tile_position = tiles[i] - 1
src.set(
tile_position % 6 * cxTile + anim,
tile_position / 6 * cyTile, cxTile, cyTile)
dstBitmap.blt(i%2*cxTile+x,i/2*cyTile+y, srcBitmap, src)
end
end
def passable?(passages,tile_id)
return false if tile_id == nil
passage = passages[tile_id]
return (passage && passage<15)
end
def getPassabilityMinimap(mapid)
map = load_data(sprintf("Data/Map%03d.rxdata",mapid))
tileset = $data_tilesets[map.tileset_id]
minimap = AnimatedBitmap.new("Graphics/Pictures/minimap_tiles")
ret = Bitmap.new(map.width*6,map.height*6)
passtable = Table.new(map.width,map.height)
passages = tileset.passages
for i in 0...map.width
for j in 0...map.height
pass=true
for z in [2,1,0]
if !passable?(passages,map.data[i,j,z])
pass=false
break
end
end
passtable[i,j]=pass ? 1 : 0
end
end
neighbors=TileDrawingHelper::NeighborsToTiles
for i in 0...map.width
for j in 0...map.height
if passtable[i,j]==0
nb=TileDrawingHelper.tableNeighbors(passtable,i,j)
tile=neighbors[nb]
bltMinimapAutotile(ret,i*6,j*6,minimap.bitmap,tile)
end
end
end
minimap.disposes
return ret
end
module ScreenPosHelper
def self.pbScreenZoomX(ch)
zoom=1.0
if $PokemonSystem.tilemap==2
zoom=((ch.screen_y - 16) - (Graphics.height / 2)) *
(Draw_Tilemap::Pitch*1.0 / (Graphics.height * 25)) + 1
end
return zoom*Game_Map::TILE_WIDTH/32.0
end
def self.pbScreenZoomY(ch)
zoom=1.0
if $PokemonSystem.tilemap==2
zoom=((ch.screen_y - 16) - (Graphics.height / 2)) *
(Draw_Tilemap::Pitch*1.0 / (Graphics.height * 25)) + 1
end
return zoom*Game_Map::TILE_HEIGHT/32.0
end
def self.pbScreenX(ch)
ret=ch.screen_x
if $PokemonSystem.tilemap==2
widthdiv2=(Graphics.width / 2)
ret=widthdiv2+(ret-widthdiv2)*pbScreenZoomX(ch)
end
return ret
end
def self.pbScreenY(ch)
ret=ch.screen_y
if $PokemonSystem.tilemap==2 && Draw_Tilemap::Curve && Draw_Tilemap::Pitch != 0
zoomy=pbScreenZoomY(ch)
oneMinusZoomY=1-zoomy
ret += (8 * oneMinusZoomY * (oneMinusZoomY /
(2 * ((Draw_Tilemap::Pitch*1.0 / 100) / (Graphics.height*1.0 / 16.0))) + 0.5))
end
return ret
end
@heightcache={}
def self.bmHeight(bm)
h=@heightcache[bm]
if !h
bmap=AnimatedBitmap.new("Graphics/Characters/"+bm,0)
h=bmap.height
@heightcache[bm]=h
bmap.dispose
end
return h
end
def self.pbScreenZ(ch,height=nil)
if height==nil
height=0
if ch.tile_id > 0
height=32
elsif ch.character_name!=""
height=bmHeight(ch.character_name)/4
end
end
ret=ch.screen_z(height)
if $PokemonSystem.tilemap==2
ret-=(pbScreenZoomY(ch) < 0.5 ? 1000 : 0)
end
return ret
end
end
###############################################
class Draw_Tilemap # This class controls a set of sprites, with
attr_reader :tileset # different Z values, arranged into horizontal bars
attr_reader :map_data
attr_reader :flash_data
attr_reader :priorities
attr_reader :terrain_tags
attr_reader :autotiles
attr_accessor :bitmaps
attr_accessor :pitch
attr_accessor :ox
attr_accessor :oy
attr_accessor :visible
attr_reader :viewport
attr_accessor :color
attr_accessor :tone
StripSize = 16
Curve = true
Pitch = 3
FlashOpacity = [100,90,80,70,80,90]
def initialize(viewport=nil)
@tileset=nil
@map_data=nil
@priorities=nil
@terrain_tags=nil
@autotiles=[nil,nil,nil,nil,nil,nil,nil]
@viewport=viewport
@visible=true
@helper=TileDrawingHelper.new(nil,@autotiles)
@drawnstrips=[]
@contentstrips=[]
@disposed=false
@bitmaps=[]
@sprites=[]
@ox=0
@oy=0
@tone=Tone.new(0,0,0,0)
@color=Color.new(0,0,0,0)
@flash_data=nil
@numsprites=0
end
def tileset=(value)
@tileset=value
@helper.tileset=value
@doredraw=true
end
def map_data=(value)
@map_data=value
@doredraw=true
end
def flash_data=(value)
@flash_data=value
@doredraw=true
end
def priorities=(value)
@priorities=value
@doredraw=true
end
def terrain_tags=(value)
@terrain_tags=value
@doredraw=true
end
def redrawmap
# Provide blank data in proper object form
self.clear
xsize=@map_data.xsize
ysize=@map_data.ysize
# Bitmaps used for each priority's drawing. Priorities 2-5 are combined.
@bitmaps = [Bitmap.new(xsize*32, ysize*32+StripSize),
Bitmap.new(xsize*32, ysize*32+StripSize),
Bitmap.new(xsize*32, ysize*32+StripSize)]
for i in @bitmaps
i.clear
end
if @flash_data
@bitmaps.push(Bitmap.new(xsize*32, ysize*32+StripSize))
end
@drawnstrips.clear
@contentstrips.clear
# Generate blank sprites
@sprites.clear
@numsprites=ysize * (32 / StripSize)
for i in 0...@map_data.zsize # For each layer
@sprites.push([])
@contentstrips.push([])
end
if @flash_data
@sprites.push([])
@contentstrips.push([])
end
end
def update
oyunchanged=false
if !@flash_data.nil? && @sprites.length>0
flashindex=@sprites.length-1
for j in 0...@numsprites
sprite=@sprites[flashindex][j]
next if !sprite.is_a?(Sprite)
sprite.opacity=FlashOpacity[(Graphics.frame_count/2) % 6]
end
end
for s in @sprites
for sprite in s
next if !sprite.is_a?(Sprite)
# sprite.tone=@tone
# sprite.color=@color
end
end
if @doredraw
@drawnstrips=[]
redrawmap
@doredraw=false
elsif @oldOx==@ox && @oldOy==@oy
return
elsif @oldOy==@oy
oyunchanged=true
end
@oldOx=@ox
@oldOy=@oy
@pitch = Pitch
minvalue=[0, ((Graphics.height / 2) -
((Graphics.height * 60) / @pitch) + @oy) / StripSize].max.to_i
maxvalue=[@numsprites - 1,(@oy + Graphics.height) / StripSize].min.to_i
return if minvalue>maxvalue
for j in 0...@numsprites
if j<minvalue || j>maxvalue
for i in 0...@sprites.length
sprite=@sprites[i][j]
if sprite
sprite.dispose if sprite.is_a?(Sprite)
@sprites[i][j]=nil
end
end
else
drawStrip(j)
end
end
vpy=@viewport.rect.y
vpr=@viewport.rect.x+@viewport.rect.width
vpb=@viewport.rect.y+@viewport.rect.height
numsprites=0
for i in @sprites
numsprites+=i.compact.length
end
for j in minvalue..maxvalue
# For each strip within the visible screen, update OX/Y
x=Graphics.width/2
sox=@ox+x
y = (j * StripSize - @oy)
zoom_x=1.0
zoom_y=1.0
unless @pitch == 0 # Apply X Zoom
zoom_x = (y - Graphics.height*1.0 / 2) * (@pitch*1.0 / (Graphics.height * 25)) + 1
if Curve # Zoom Y values same as X, and compensate
zoom_y = zoom_x
yadd = StripSize*1.0 * (1 - zoom_y) * ((1 - zoom_y) /
(2 * ((@pitch*1.0 / 100) / (Graphics.height*1.0 / (StripSize * 2)))) + 0.5)
y+=yadd
end
end
xstart=(x-sox*zoom_x)
yend=(y+(StripSize*2)*zoom_y)
if xstart>vpr || yend<=vpy
for i in 0...@sprites.length
sprite=@sprites[i][j]
if sprite.is_a?(Sprite)
sprite.dispose
@sprites[i][j]=nil
end
end
else
for i in 0...@sprites.length
sprite=@sprites[i][j]
next if !sprite
if sprite==true
sprite=newSprite(i,j)
@sprites[i][j]=sprite
end
sprite.visible=@visible
sprite.x = x
sprite.ox = sox
sprite.y = y
sprite.zoom_x = zoom_x
sprite.zoom_y = zoom_y
end
end
end
end
def clear
for i in @bitmaps
i.dispose
end
@bitmaps.clear
for i in 0...@sprites.length
for j in 0...@sprites[i].length
@sprites[i][j].dispose if @sprites[i][j].is_a?(Sprite)
end
@sprites[i].clear
end
@sprites.clear
end
def dispose
return if @disposed
self.clear
for i in 0...7
self.autotiles[i]=nil
end
@helper=nil
@sprites=nil
@bitmaps=nil
@disposed = true
end
def disposed?
return @disposed
end
def newSprite(i,j)
sprite=Sprite.new(@viewport)
sprite.bitmap=@bitmaps[i]
sprite.src_rect.set(0, j * StripSize, @map_data.xsize * 32, StripSize * 2)
sprite.x = Graphics.width / 2
sprite.y = -64
sprite.z = (i * 32)
sprite.tone=@tone
sprite.color=@color
if i==@bitmaps.length-1 && !@flash_data.nil?
sprite.blend_type=1
sprite.z=1
sprite.opacity=FlashOpacity[(Graphics.frame_count/2) % 6]
end
return sprite
end
def drawStrip(j)
minY=(j*StripSize)/32
maxY=(j*StripSize+StripSize*2)/32
minY=0 if minY<0
minY=@map_data.ysize-1 if minY>@map_data.ysize-1
maxY=0 if maxY<0
maxY=@map_data.ysize-1 if maxY>@map_data.ysize-1
for y in minY..maxY
if !@drawnstrips[y]
for x in 0...@map_data.xsize
draw_position(x, y)
end
@drawnstrips[y]=true
end
end
for i in 0...@sprites.length # For each priority
sprite=@sprites[i][j]
if !sprite || (sprite!=true && sprite.disposed?)
havecontent=false
for y in minY..maxY
havecontent=havecontent||@contentstrips[i][y]
end
sprite=(havecontent) ? true : nil
@sprites[i][j]=sprite
end
end
end
def draw_position(x, y)
for layer in 0...@map_data.zsize
pos = @map_data[x, y, layer]
priopos=@priorities[pos]
priopos=0 if !priopos
prio=(2<priopos) ? 2 : priopos
@contentstrips[prio][y]=true if pos>0
@helper.bltTile(@bitmaps[prio],x*32,y*32,pos,0)
end
if !@flash_data.nil?
lastlayer=@bitmaps.length-1
id=@flash_data[x,y,0]
r=(id>>8)&15
g=(id>>4)&15
b=(id)&15
@contentstrips[lastlayer][y]=true
color=Color.new(r*16,g*16,b*16)
@bitmaps[lastlayer].fill_rect(x*32,y*32,32,32,color)
end
end
end
class Sprite_Character
alias perspectivetilemap_initialize initialize
attr_accessor :character
def initialize(viewport, character = nil)
@character = character
perspectivetilemap_initialize(viewport,character)
end
alias update_or :update
def update
update_or
if $PokemonSystem.tilemap==2
self.zoom_y=ScreenPosHelper.pbScreenZoomY(@character)
self.zoom_x=ScreenPosHelper.pbScreenZoomX(@character)
self.x=ScreenPosHelper.pbScreenX(@character)
self.y=ScreenPosHelper.pbScreenY(@character)
self.z=ScreenPosHelper.pbScreenZ(@character,@ch)
end
end
end

View File

@@ -0,0 +1,118 @@
#===============================================================================
#
#===============================================================================
class SynchronizedTilemapAutotilesInternal
def initialize(oldat)
@atdisposables = [[],[],[],[],[],[],[]]
@atframes = [[],[],[],[],[],[],[]]
@atframe = [-1,-1,-1,-1,-1,-1,-1]
@autotiles = []
@oldat = oldat
end
def dispose
for i in 0...7
for bitmap in @atdisposables[i]
bitmap.dispose
end
@atdisposables[i].clear
@atframes[i].clear
end
end
def [](i)
return @autotiles[i]
end
def []=(i,value)
for frame in @atdisposables[i]
frame.dispose
end
@atframe[i] = -1
@atframes[i].clear
@atdisposables[i].clear
if value && !value.disposed?
if value.height==32
frames = value.width/32
for j in 0...frames
@atdisposables[i][j] = Bitmap.new(32,32)
@atdisposables[i][j].blt(0,0,value,Rect.new(j*32,0,32,32))
@atframes[i][j] = @atdisposables[i][j]
end
elsif value.height==128
frames = value.width/96
for j in 0...frames
@atdisposables[i][j] = Bitmap.new(96,128)
@atdisposables[i][j].blt(0,0,value,Rect.new(j*96,0,96,128))
@atframes[i][j] = @atdisposables[i][j]
end
else
@atframes[i][0] = value
end
else
@atframes[i][0] = value
end
@autotiles[i] = value
sync
end
def sync
frameused = []
for i in 0...7
frames = [1,@atframes[i].length].max
frame = (Graphics.frame_count/15)%frames
if frames>1 && @atframe[i]!=frame
@oldat[i] = @atframes[i][frame]
@atframe[i] = frame
end
end
end
end
class SynchronizedTilemapAutotiles
def initialize(autotiles)
@autotiles = autotiles
end
def [](i)
return @autotiles[i]
end
def []=(i,value)
@autotiles[i] = value
end
end
class SynchronizedTilemap < Tilemap
# This class derives from Tilemap just to synchronize
# the tilemap animation.
attr_accessor :numupdates
def initialize(viewport=nil)
super(viewport)
@updating = true
@autotiles = SynchronizedTilemapAutotilesInternal.new(self.autotiles)
@autos = SynchronizedTilemapAutotiles.new(@autotiles)
@updating = false
end
def dispose
@autotiles.dispose
super
end
def autotiles
return @autos if !@updating
super
end
def update
return if disposed?
@autotiles.sync
super
end
end

View File

@@ -0,0 +1,70 @@
class TilemapLoader
def initialize(viewport)
@viewport = viewport
@tilemap = nil
@color = Color.new(0,0,0,0)
@tone = Tone.new(0,0,0,0)
updateClass
end
def updateClass
case $PokemonSystem.tilemap
when 1 # Custom (recommended)
setClass(CustomTilemap)
when 2 # Perspective
setClass(Draw_Tilemap)
else # Original (SynchronizedTilemap) or custom (CustomTilemap)
if Tilemap.method_defined?(:passages)
setClass(CustomTilemap)
else
setClass(($ResizeFactor==1.0) ? SynchronizedTilemap : CustomTilemap)
end
end
end
def setClass(cls)
newtilemap = cls.new(@viewport)
if @tilemap
newtilemap.tileset = @tilemap.tileset
newtilemap.map_data = @tilemap.map_data
newtilemap.flash_data = @tilemap.flash_data
newtilemap.priorities = @tilemap.priorities
newtilemap.terrain_tags = @tilemap.terrain_tags
newtilemap.visible = @tilemap.visible
newtilemap.ox = @tilemap.ox
newtilemap.oy = @tilemap.oy
for i in 0...7
newtilemap.autotiles[i] = @tilemap.autotiles[i]
end
@tilemap.dispose
@tilemap = newtilemap
newtilemap.update if cls!=SynchronizedTilemap
else
@tilemap = newtilemap
end
end
def dispose; @tilemap.dispose; end
def disposed?; @tilemap && @tilemap.disposed?; end
def update; @tilemap.update; end
def viewport; @tilemap.viewport; end
def autotiles; @tilemap.autotiles; end
def tileset; @tilemap.tileset; end
def tileset=(v); @tilemap.tileset = v; end
def map_data; @tilemap.map_data; end
def map_data=(v); @tilemap.map_data = v; end
def flash_data; @tilemap.flash_data; end
def flash_data=(v); @tilemap.flash_data = v; end
def priorities; @tilemap.priorities; end
def priorities=(v); @tilemap.priorities = v; end
def terrain_tags; (@tilemap.terrain_tags rescue nil); end
def terrain_tags=(v); (@tilemap.terrain_tags = v rescue nil); end
def visible; @tilemap.visible; end
def visible=(v); @tilemap.visible = v; end
def tone; (@tilemap.tone rescue @tone); end
def tone=(value); (@tilemap.tone = value rescue nil); end
def ox; @tilemap.ox; end
def ox=(v); @tilemap.ox = v; end
def oy; @tilemap.oy; end
def oy=(v); @tilemap.oy = v; end
end

View File

@@ -0,0 +1,136 @@
class TileDrawingHelper
attr_accessor :tileset
attr_accessor :autotiles
Autotiles = [
[ [27, 28, 33, 34], [ 5, 28, 33, 34], [27, 6, 33, 34], [ 5, 6, 33, 34],
[27, 28, 33, 12], [ 5, 28, 33, 12], [27, 6, 33, 12], [ 5, 6, 33, 12] ],
[ [27, 28, 11, 34], [ 5, 28, 11, 34], [27, 6, 11, 34], [ 5, 6, 11, 34],
[27, 28, 11, 12], [ 5, 28, 11, 12], [27, 6, 11, 12], [ 5, 6, 11, 12] ],
[ [25, 26, 31, 32], [25, 6, 31, 32], [25, 26, 31, 12], [25, 6, 31, 12],
[15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
[ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
[39, 40, 45, 46], [ 5, 40, 45, 46], [39, 6, 45, 46], [ 5, 6, 45, 46] ],
[ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
[17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
[ [37, 38, 43, 44], [37, 6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
[37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1, 2, 7, 8] ]
]
# converts neighbors returned from tableNeighbors to tile indexes
NeighborsToTiles = [
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
37, 27, 37, 27, 23, 15, 23, 13, 37, 27, 37, 27, 22, 11, 22, 9,
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
36, 26, 36, 26, 21, 7, 21, 5, 36, 26, 36, 26, 20, 3, 20, 1,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
37, 25, 37, 25, 23, 14, 23, 12, 37, 25, 37, 25, 22, 10, 22, 8,
45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
36, 24, 36, 24, 21, 6, 21, 4, 36, 24, 36, 24, 20, 2, 20, 0
]
def self.tableNeighbors(data,x,y)
return 0 if x < 0 || x >= data.xsize
return 0 if y < 0 || y >= data.ysize
t = data[x,y]
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
return i
end
def self.fromTileset(tileset)
bmtileset=pbGetTileset(tileset.tileset_name)
bmautotiles=[]
for i in 0...7
bmautotiles.push(pbGetAutotile(tileset.autotile_names[i]))
end
return self.new(bmtileset,bmautotiles)
end
def initialize(tileset,autotiles)
@tileset = tileset
@autotiles = autotiles
end
def dispose
@tileset.dispose if @tileset
@tileset = nil
for i in 0...@autotiles.length
@autotiles[i].dispose
@autotiles[i] = nil
end
end
def bltSmallAutotile(bitmap,x,y,cxTile,cyTile,id,frame)
return if id >= 384 || frame < 0 || !@autotiles
autotile = @autotiles[id / 48 - 1]
return if !autotile || autotile.disposed?
cxTile = [cxTile / 2, 1].max
cyTile = [cyTile / 2, 1].max
if autotile.height == 32
anim = frame * 32
src_rect = Rect.new(anim, 0, 32, 32)
bitmap.stretch_blt(Rect.new(x, y, cxTile * 2, cyTile * 2), autotile, src_rect)
else
anim = frame * 96
id %= 48
tiles = TileDrawingHelper::Autotiles[id >> 3][id & 7]
src = Rect.new(0, 0, 0, 0)
for i in 0...4
tile_position = tiles[i] - 1
src.set(tile_position % 6 * 16 + anim, tile_position / 6 * 16, 16, 16)
bitmap.stretch_blt(Rect.new(i % 2 * cxTile + x, i / 2 * cyTile + y, cxTile, cyTile),
autotile, src)
end
end
end
def bltSmallRegularTile(bitmap,x,y,cxTile,cyTile,id)
return if id < 384 || !@tileset || @tileset.disposed?
rect = Rect.new((id - 384) % 8 * 32, (id - 384) / 8 * 32, 32, 32)
bitmap.stretch_blt(Rect.new(x, y, cxTile, cyTile), @tileset, rect)
end
def bltSmallTile(bitmap,x,y,cxTile,cyTile,id,frame=0)
if id >= 384
bltSmallRegularTile(bitmap, x, y, cxTile, cyTile, id)
elsif id > 0
bltSmallAutotile(bitmap, x, y, cxTile, cyTile, id, frame)
end
end
def bltAutotile(bitmap,x,y,id,frame)
bltSmallAutotile(bitmap, x, y, 32, 32, id, frame)
end
def bltRegularTile(bitmap,x,y,id)
bltSmallRegularTile(bitmap, x, y, 32, 32, id)
end
def bltTile(bitmap,x,y,id,frame=0)
if id >= 384
bltRegularTile(bitmap, x, y, id)
elsif id > 0
bltAutotile(bitmap, x, y, id, frame)
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
# Defines an event that procedures can subscribe to.
class Event
def initialize
@callbacks = []
end
# Sets an event handler for this event and removes all other event handlers.
def set(method)
@callbacks.clear
@callbacks.push(method)
end
# Removes an event handler procedure from the event.
def -(method)
for i in 0...@callbacks.length
next if @callbacks[i]!=method
@callbacks.delete_at(i)
break
end
return self
end
# Adds an event handler procedure from the event.
def +(method)
for i in 0...@callbacks.length
return self if @callbacks[i]==method
end
@callbacks.push(method)
return self
end
# Clears the event of event handlers.
def clear
@callbacks.clear
end
# Triggers the event and calls all its event handlers. Normally called only
# by the code where the event occurred.
# The first argument is the sender of the event, the second argument contains
# the event's parameters. If three or more arguments are given, this method
# supports the following callbacks:
# proc{ |sender,params| } where params is an array of the other parameters, and
# proc{ |sender,arg0,arg1,...| }
def trigger(*arg)
arglist = arg[1,arg.length]
for callback in @callbacks
if callback.arity>2 && arg.length==callback.arity
# Retrofitted for callbacks that take three or more arguments
callback.call(*arg)
else
callback.call(arg[0],arglist)
end
end
end
# Triggers the event and calls all its event handlers. Normally called only
# by the code where the event occurred. The first argument is the sender of
# the event, the other arguments are the event's parameters.
def trigger2(*arg)
for callback in @callbacks
callback.call(*arg)
end
end
end
class HandlerHash
def initialize(mod)
@mod = mod
@hash = {}
@addIfs = []
@symbolCache = {}
end
def fromSymbol(sym)
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
mod = Object.const_get(@mod) rescue nil
return nil if !mod
return mod.const_get(sym.to_sym) rescue nil
end
def toSymbol(sym)
return sym.to_sym if sym.is_a?(Symbol) || sym.is_a?(String)
ret = @symbolCache[sym]
return ret if ret
mod = Object.const_get(@mod) rescue nil
return nil if !mod
for key in mod.constants
next if mod.const_get(key)!=sym
ret = key.to_sym
@symbolCache[sym] = ret
break
end
return ret
end
def addIf(condProc,handler)
@addIfs.push([condProc,handler])
end
def add(sym,handler) # 'sym' can be an ID or symbol
id = fromSymbol(sym)
@hash[id] = handler if id
symbol = toSymbol(sym)
@hash[symbol] = handler if symbol
end
def copy(src,*dests)
handler = self[src]
if handler
for dest in dests
self.add(dest,handler)
end
end
end
def [](sym) # 'sym' can be an ID or symbol
id = fromSymbol(sym)
ret = nil
ret = @hash[id] if id && @hash[id] # Real ID from the item
symbol = toSymbol(sym)
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
unless ret
for addif in @addIfs
return addif[1] if addif[0].call(id)
end
end
return ret
end
def trigger(sym,*args)
handler = self[sym]
return (handler) ? handler.call(fromSymbol(sym),*args) : nil
end
def clear
@hash.clear
end
end
class SpeciesHandlerHash < HandlerHash
def initialize
super(:PBSpecies)
end
end
class AbilityHandlerHash < HandlerHash
def initialize
super(:PBAbilities)
end
end
class ItemHandlerHash < HandlerHash
def initialize
super(:PBItems)
end
end
class MoveHandlerHash < HandlerHash
def initialize
super(:PBMoves)
end
end

View File

@@ -0,0 +1,163 @@
module FileInputMixin
def fgetb
x=0
ret=0
each_byte do |i|
ret=i || 0
break
end
return ret
end
def fgetw
x=0
ret=0
each_byte do |i|
break if !i
ret|=(i<<x)
x+=8
break if x==16
end
return ret
end
def fgetdw
x=0
ret=0
each_byte do |i|
break if !i
ret|=(i<<x)
x+=8
break if x==32
end
return ret
end
def fgetsb
ret=fgetb
if (ret&0x80)!=0
return ret-256
else
return ret
end
end
def xfgetb(offset)
self.pos=offset
return fgetb
end
def xfgetw(offset)
self.pos=offset
return fgetw
end
def xfgetdw(offset)
self.pos=offset
return fgetdw
end
def getOffset(index)
self.binmode
self.pos=0
offset=fgetdw>>3
return 0 if index>=offset
self.pos=index*8
return fgetdw
end
def getLength(index)
self.binmode
self.pos=0
offset=fgetdw>>3
return 0 if index>=offset
self.pos=index*8+4
return fgetdw
end
def readName(index)
self.binmode
self.pos=0
offset=fgetdw>>3
return "" if index>=offset
self.pos=index<<3
offset=fgetdw
length=fgetdw
return "" if length==0
self.pos=offset
return read(length)
end
end
module FileOutputMixin
def fputb(b)
b=b&0xFF
write(b.chr)
end
def fputw(w)
2.times do
b=w&0xFF
write(b.chr)
w>>=8
end
end
def fputdw(w)
4.times do
b=w&0xFF
write(b.chr)
w>>=8
end
end
end
class File < IO
=begin
unless defined?(debugopen)
class << self
alias debugopen open
end
end
def open(f,m="r")
debugopen("debug.txt","ab") { |file| file.write([f,m,Time.now.to_f].inspect+"\r\n") }
if block_given?
debugopen(f,m) { |file| yield file }
else
return debugopen(f,m)
end
end
=end
include FileInputMixin
include FileOutputMixin
end
class StringInput
include FileInputMixin
def pos=(value)
seek(value)
end
def each_byte
while !eof?
yield getc
end
end
def binmode
end
end
class StringOutput
include FileOutputMixin
end

View File

@@ -0,0 +1,776 @@
def pbAddScriptTexts(items,script)
script.scan(/(?:_I)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s|
string=s[0]
string.gsub!(/\\\"/,"\"")
string.gsub!(/\\\\/,"\\")
items.push(string)
}
end
def pbAddRgssScriptTexts(items,script)
script.scan(/(?:_INTL|_ISPRINTF)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s|
string=s[0]
string.gsub!(/\\r/,"\r")
string.gsub!(/\\n/,"\n")
string.gsub!(/\\1/,"\1")
string.gsub!(/\\\"/,"\"")
string.gsub!(/\\\\/,"\\")
items.push(string)
}
end
def pbSetTextMessages
Graphics.update
begin
t = Time.now.to_i
texts=[]
for script in $RGSS_SCRIPTS
if Time.now.to_i - t >= 5
t = Time.now.to_i
Graphics.update
end
scr=Zlib::Inflate.inflate(script[2])
pbAddRgssScriptTexts(texts,scr)
end
# Must add messages because this code is used by both game system and Editor
MessageTypes.addMessagesAsHash(MessageTypes::ScriptTexts,texts)
commonevents=pbLoadRxData("Data/CommonEvents")
items=[]
choices=[]
for event in commonevents.compact
if Time.now.to_i - t >= 5
t = Time.now.to_i
Graphics.update
end
begin
neednewline=false
lastitem=""
for j in 0...event.list.size
list = event.list[j]
if neednewline && list.code!=401
if lastitem!=""
lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1+" " }
items.push(lastitem)
lastitem=""
end
neednewline=false
end
if list.code == 101
lastitem+="#{list.parameters[0]}" if !$RPGVX
neednewline=true
elsif list.code == 102
for k in 0...list.parameters[0].length
choices.push(list.parameters[0][k])
end
neednewline=false
elsif list.code == 401
lastitem+=" " if lastitem!=""
lastitem+="#{list.parameters[0]}"
neednewline=true
elsif list.code == 355 || list.code == 655
pbAddScriptTexts(items,list.parameters[0])
elsif list.code == 111 && list.parameters[0]==12
pbAddScriptTexts(items,list.parameters[1])
elsif list.code == 209
route=list.parameters[1]
for k in 0...route.list.size
if route.list[k].code == 45
pbAddScriptTexts(items,route.list[k].parameters[0])
end
end
end
end
if neednewline
if lastitem!=""
items.push(lastitem)
lastitem=""
end
end
end
end
if Time.now.to_i - t >= 5
t = Time.now.to_i
Graphics.update
end
items|=[]
choices|=[]
items.concat(choices)
MessageTypes.setMapMessagesAsHash(0,items)
mapinfos = pbLoadRxData("Data/MapInfos")
mapnames=[]
for id in mapinfos.keys
mapnames[id]=mapinfos[id].name
end
MessageTypes.setMessages(MessageTypes::MapNames,mapnames)
for id in mapinfos.keys
if Time.now.to_i - t >= 5
t = Time.now.to_i
Graphics.update
end
filename=sprintf("Data/Map%03d.%s",id,$RPGVX ? "rvdata" : "rxdata")
next if !pbRgssExists?(filename)
map = load_data(filename)
items=[]
choices=[]
for event in map.events.values
if Time.now.to_i - t >= 5
t = Time.now.to_i
Graphics.update
end
begin
for i in 0...event.pages.size
neednewline=false
lastitem=""
for j in 0...event.pages[i].list.size
list = event.pages[i].list[j]
if neednewline && list.code!=401
if lastitem!=""
lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1+" " }
items.push(lastitem)
lastitem=""
end
neednewline=false
end
if list.code == 101
lastitem+="#{list.parameters[0]}" if !$RPGVX
neednewline=true
elsif list.code == 102
for k in 0...list.parameters[0].length
choices.push(list.parameters[0][k])
end
neednewline=false
elsif list.code == 401
lastitem+=" " if lastitem!=""
lastitem+="#{list.parameters[0]}"
neednewline=true
elsif list.code == 355 || list.code==655
pbAddScriptTexts(items,list.parameters[0])
elsif list.code == 111 && list.parameters[0]==12
pbAddScriptTexts(items,list.parameters[1])
elsif list.code==209
route=list.parameters[1]
for k in 0...route.list.size
if route.list[k].code==45
pbAddScriptTexts(items,route.list[k].parameters[0])
end
end
end
end
if neednewline
if lastitem!=""
items.push(lastitem)
lastitem=""
end
end
end
end
end
if Time.now.to_i - t >= 5
t = Time.now.to_i
Graphics.update
end
items|=[]
choices|=[]
items.concat(choices)
MessageTypes.setMapMessagesAsHash(id,items)
if Time.now.to_i - t >= 5
t = Time.now.to_i
Graphics.update
end
end
rescue Hangup
end
Graphics.update
end
def pbEachIntlSection(file)
lineno=1
re=/^\s*\[\s*([^\]]+)\s*\]\s*$/
havesection=false
sectionname=nil
lastsection=[]
file.each_line { |line|
if lineno==1 && line[0]==0xEF && line[1]==0xBB && line[2]==0xBF
line=line[3,line.length-3]
end
if !line[/^\#/] && !line[/^\s*$/]
if line[re]
if havesection
yield lastsection,sectionname
end
lastsection.clear
sectionname=$~[1]
havesection=true
else
if sectionname==nil
raise _INTL("Expected a section at the beginning of the file (line {1})",lineno)
end
lastsection.push(line.gsub(/\s+$/,""))
end
end
lineno+=1
if lineno%500==0
Graphics.update
end
}
if havesection
yield lastsection,sectionname
end
end
def pbGetText(infile)
begin
file=File.open(infile,"rb")
rescue
raise _INTL("Can't find {1}",infile)
end
intldat=[]
begin
pbEachIntlSection(file) { |section,name|
next if section.length==0
index=name
if !name[/^([Mm][Aa][Pp])?(\d+)$/]
raise _INTL("Invalid section name {1}",name)
end
ismap=$~[1] && $~[1]!=""
id=$~[2].to_i
itemlength=0
if section[0][/^\d+$/]
intlhash=[]
itemlength=3
if ismap
raise _INTL("Section {1} can't be an ordered list (section was recognized as an ordered list because its first line is a number)",name)
end
if section.length%3!=0
raise _INTL("Section {1}'s line count is not divisible by 3 (section was recognized as an ordered list because its first line is a number)",name)
end
else
intlhash=OrderedHash.new
itemlength=2
if section.length%2!=0
raise _INTL("Section {1} has an odd number of entries (section was recognized as a hash because its first line is not a number)",name)
end
end
i=0;loop do break unless i<section.length
if itemlength==3
if !section[i][/^\d+$/]
raise _INTL("Expected a number in section {1}, got {2} instead",name,section[i])
end
key=section[i].to_i
i+=1
else
key=MessageTypes.denormalizeValue(section[i])
end
intlhash[key]=MessageTypes.denormalizeValue(section[i+1])
i+=2
end
if ismap
intldat[0]=[] if !intldat[0]
intldat[0][id]=intlhash
else
intldat[id]=intlhash
end
}
ensure
file.close
end
return intldat
end
def pbCompileText
outfile=File.open("intl.dat","wb")
begin
intldat=pbGetText("intl.txt")
Marshal.dump(intldat,outfile)
rescue
raise
ensure
outfile.close
end
end
class OrderedHash < Hash
def initialize
@keys=[]
super
end
def keys
return @keys.clone
end
def inspect
str="{"
for i in 0...@keys.length
str+=", " if i>0
str+=@keys[i].inspect+"=>"+self[@keys[i]].inspect
end
str+="}"
return str
end
alias :to_s :inspect
def []=(key,value)
oldvalue=self[key]
if !oldvalue && value
@keys.push(key)
elsif !value
@keys|=[]
@keys-=[key]
end
return super(key,value)
end
def self._load(string)
ret=self.new
keysvalues=Marshal.load(string)
keys=keysvalues[0]
values=keysvalues[1]
for i in 0...keys.length
ret[keys[i]]=values[i]
end
return ret
end
def _dump(depth=100)
values=[]
for key in @keys
values.push(self[key])
end
return Marshal.dump([@keys,values])
end
end
class Messages
def initialize(filename=nil,delayLoad=false)
@messages=nil
@filename=filename
if @filename && !delayLoad
loadMessageFile(@filename)
end
end
def delayedLoad
if @filename && !@messages
loadMessageFile(@filename)
@filename=nil
end
end
def self.stringToKey(str)
if str && str[/[\r\n\t\1]|^\s+|\s+$|\s{2,}/]
key=str.clone
key.gsub!(/^\s+/,"")
key.gsub!(/\s+$/,"")
key.gsub!(/\s{2,}/," ")
return key
end
return str
end
def self.normalizeValue(value)
if value[/[\r\n\t\x01]|^[\[\]]/]
ret=value.clone
ret.gsub!(/\r/,"<<r>>")
ret.gsub!(/\n/,"<<n>>")
ret.gsub!(/\t/,"<<t>>")
ret.gsub!(/\[/,"<<[>>")
ret.gsub!(/\]/,"<<]>>")
ret.gsub!(/\x01/,"<<1>>")
return ret
end
return value
end
def self.denormalizeValue(value)
if value[/<<[rnt1\[\]]>>/]
ret=value.clone
ret.gsub!(/<<1>>/,"\1")
ret.gsub!(/<<r>>/,"\r")
ret.gsub!(/<<n>>/,"\n")
ret.gsub!(/<<\[>>/,"[")
ret.gsub!(/<<\]>>/,"]")
ret.gsub!(/<<t>>/,"\t")
return ret
end
return value
end
def self.writeObject(f,msgs,secname,origMessages=nil)
return if !msgs
if msgs.is_a?(Array)
f.write("[#{secname}]\r\n")
for j in 0...msgs.length
next if msgs[j]==nil || msgs[j]==""
value=Messages.normalizeValue(msgs[j])
origValue=""
if origMessages
origValue=Messages.normalizeValue(origMessages.get(secname,j))
else
origValue=Messages.normalizeValue(MessageTypes.get(secname,j))
end
f.write("#{j}\r\n")
f.write(origValue+"\r\n")
f.write(value+"\r\n")
end
elsif msgs.is_a?(OrderedHash)
f.write("[#{secname}]\r\n")
keys=msgs.keys
for key in keys
next if msgs[key]==nil || msgs[key]==""
value=Messages.normalizeValue(msgs[key])
valkey=Messages.normalizeValue(key)
# key is already serialized
f.write(valkey+"\r\n")
f.write(value+"\r\n")
end
end
end
def messages
return @messages || []
end
def extract(outfile)
# return if !@messages
origMessages=Messages.new("Data/messages.dat")
File.open(outfile,"wb") { |f|
f.write(0xef.chr)
f.write(0xbb.chr)
f.write(0xbf.chr)
f.write("# To localize this text for a particular language, please\r\n")
f.write("# translate every second line of this file.\r\n")
if origMessages.messages[0]
for i in 0...origMessages.messages[0].length
msgs=origMessages.messages[0][i]
Messages.writeObject(f,msgs,"Map#{i}",origMessages)
end
end
for i in 1...origMessages.messages.length
msgs=origMessages.messages[i]
Messages.writeObject(f,msgs,i,origMessages)
end
}
end
def setMessages(type,array)
@messages=[] if !@messages
arr=[]
for i in 0...array.length
arr[i]=(array[i]) ? array[i] : ""
end
@messages[type]=arr
end
def addMessages(type,array)
@messages=[] if !@messages
arr=(@messages[type]) ? @messages[type] : []
for i in 0...array.length
arr[i]=(array[i]) ? array[i] : (arr[i]) ? arr[i] : ""
end
@messages[type]=arr
end
def self.createHash(type,array)
arr=OrderedHash.new
for i in 0...array.length
if array[i]
key=Messages.stringToKey(array[i])
arr[key]=array[i]
end
end
return arr
end
def self.addToHash(type,array,hash)
if !hash
hash=OrderedHash.new
end
for i in 0...array.length
if array[i]
key=Messages.stringToKey(array[i])
hash[key]=array[i]
end
end
return hash
end
def setMapMessagesAsHash(type,array)
@messages=[] if !@messages
@messages[0]=[] if !@messages[0]
@messages[0][type]=Messages.createHash(type,array)
end
def addMapMessagesAsHash(type,array)
@messages=[] if !@messages
@messages[0]=[] if !@messages[0]
@messages[0][type]=Messages.addToHash(type,array,@messages[0][type])
end
def setMessagesAsHash(type,array)
@messages=[] if !@messages
@messages[type]=Messages.createHash(type,array)
end
def addMessagesAsHash(type,array)
@messages=[] if !@messages
@messages[type]=Messages.addToHash(type,array,@messages[type])
end
def saveMessages(filename=nil)
filename="Data/messages.dat" if !filename
File.open(filename,"wb") { |f| Marshal.dump(@messages,f) }
end
def loadMessageFile(filename)
begin
pbRgssOpen(filename,"rb") { |f| @messages=Marshal.load(f) }
if !@messages.is_a?(Array)
@messages=nil
raise "Corrupted data"
end
return @messages
rescue
@messages=nil
return nil
end
end
def set(type,id,value)
delayedLoad
return if !@messages
return if !@messages[type]
@messages[type][id]=value
end
def getCount(type)
delayedLoad
return 0 if !@messages
return 0 if !@messages[type]
return @messages[type].length
end
def get(type,id)
delayedLoad
return "" if !@messages
return "" if !@messages[type]
return "" if !@messages[type][id]
return @messages[type][id]
end
def getFromHash(type,key)
delayedLoad
return key if !@messages || !@messages[type] || !key
id=Messages.stringToKey(key)
return key if !@messages[type][id]
return @messages[type][id]
end
def getFromMapHash(type,key)
delayedLoad
return key if !@messages
return key if !@messages[0]
return key if !@messages[0][type] && !@messages[0][0]
id=Messages.stringToKey(key)
if @messages[0][type] && @messages[0][type][id]
return @messages[0][type][id]
elsif @messages[0][0] && @messages[0][0][id]
return @messages[0][0][id]
end
return key
end
end
module MessageTypes
# Value 0 is used for common event and map event text
Species = 1
Kinds = 2
Entries = 3
FormNames = 4
Moves = 5
MoveDescriptions = 6
Items = 7
ItemPlurals = 8
ItemDescriptions = 9
Abilities = 10
AbilityDescs = 11
Types = 12
TrainerTypes = 13
TrainerNames = 14
BeginSpeech = 15
EndSpeechWin = 16
EndSpeechLose = 17
RegionNames = 18
PlaceNames = 19
PlaceDescriptions = 20
MapNames = 21
PhoneMessages = 22
TrainerLoseText = 23
ScriptTexts = 24
@@messages = Messages.new
@@messagesFallback = Messages.new("Data/messages.dat",true)
def self.stringToKey(str)
return Messages.stringToKey(str)
end
def self.normalizeValue(value)
return Messages.normalizeValue(value)
end
def self.denormalizeValue(value)
Messages.denormalizeValue(value)
end
def self.writeObject(f,msgs,secname)
Messages.denormalizeValue(str)
end
def self.extract(outfile)
@@messages.extract(outfile)
end
def self.setMessages(type,array)
@@messages.setMessages(type,array)
end
def self.addMessages(type,array)
@@messages.addMessages(type,array)
end
def self.createHash(type,array)
Messages.createHash(type,array)
end
def self.addMapMessagesAsHash(type,array)
@@messages.addMapMessagesAsHash(type,array)
end
def self.setMapMessagesAsHash(type,array)
@@messages.setMapMessagesAsHash(type,array)
end
def self.addMessagesAsHash(type,array)
@@messages.addMessagesAsHash(type,array)
end
def self.setMessagesAsHash(type,array)
@@messages.setMessagesAsHash(type,array)
end
def self.saveMessages(filename=nil)
@@messages.saveMessages(filename)
end
def self.loadMessageFile(filename)
@@messages.loadMessageFile(filename)
end
def self.get(type,id)
ret=@@messages.get(type,id)
if ret==""
ret=@@messagesFallback.get(type,id)
end
return ret
end
def self.getCount(type)
c1=@@messages.getCount(type)
c2=@@messagesFallback.getCount(type)
return c1>c2 ? c1 : c2
end
def self.getOriginal(type,id)
return @@messagesFallback.get(type,id)
end
def self.getFromHash(type,key)
@@messages.getFromHash(type,key)
end
def self.getFromMapHash(type,key)
@@messages.getFromMapHash(type,key)
end
end
def pbLoadMessages(file)
return MessageTypes.loadMessageFile(file)
end
def pbGetMessageCount(type)
return MessageTypes.getCount(type)
end
def pbGetMessage(type,id)
return MessageTypes.get(type,id)
end
def pbGetMessageFromHash(type,id)
return MessageTypes.getFromHash(type,id)
end
# Replaces first argument with a localized version and formats the other
# parameters by replacing {1}, {2}, etc. with those placeholders.
def _INTL(*arg)
begin
string=MessageTypes.getFromHash(MessageTypes::ScriptTexts,arg[0])
rescue
string=arg[0]
end
string=string.clone
for i in 1...arg.length
string.gsub!(/\{#{i}\}/,"#{arg[i]}")
end
return string
end
# Replaces first argument with a localized version and formats the other
# parameters by replacing {1}, {2}, etc. with those placeholders.
# This version acts more like sprintf, supports e.g. {1:d} or {2:s}
def _ISPRINTF(*arg)
begin
string=MessageTypes.getFromHash(MessageTypes::ScriptTexts,arg[0])
rescue
string=arg[0]
end
string=string.clone
for i in 1...arg.length
string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m|
next sprintf("%"+$1,arg[i])
}
end
return string
end
def _I(str)
return _MAPINTL($game_map.map_id,str)
end
def _MAPINTL(mapid,*arg)
string=MessageTypes.getFromMapHash(mapid,arg[0])
string=string.clone
for i in 1...arg.length
string.gsub!(/\{#{i}\}/,"#{arg[i]}")
end
return string
end
def _MAPISPRINTF(mapid,*arg)
string=MessageTypes.getFromMapHash(mapid,arg[0])
string=string.clone
for i in 1...arg.length
string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m|
next sprintf("%"+$1,arg[i])
}
end
return string
end

View File

@@ -0,0 +1,40 @@
module PBDebug
@@log = []
def self.logonerr
begin
yield
rescue
PBDebug.log("")
PBDebug.log("**Exception: #{$!.message}")
PBDebug.log("#{$!.backtrace.inspect}")
PBDebug.log("")
# if $INTERNAL
pbPrintException($!)
# end
PBDebug.flush
end
end
def self.flush
if $DEBUG && $INTERNAL && @@log.length>0
File.open("Data/debuglog.txt", "a+b") { |f| f.write("#{@@log}") }
end
@@log.clear
end
def self.log(msg)
if $DEBUG && $INTERNAL
@@log.push("#{msg}\r\n")
# if @@log.length>1024
PBDebug.flush
# end
end
end
def self.dump(msg)
if $DEBUG && $INTERNAL
File.open("Data/dumplog.txt", "a+b") { |f| f.write("#{msg}\r\n") }
end
end
end

View File

@@ -0,0 +1,376 @@
class Thread
def Thread.exclusive
_old = Thread.critical
begin
Thread.critical = true
return yield
ensure
Thread.critical = _old
end
end
end
def getPlayMusic
return MiniRegistry.get(MiniRegistry::HKEY_CURRENT_USER,
"SOFTWARE\\Enterbrain\\RGSS","PlayMusic",true)
end
def getPlaySound
return MiniRegistry.get(MiniRegistry::HKEY_CURRENT_USER,
"SOFTWARE\\Enterbrain\\RGSS","PlaySound",true)
end
class AudioContext
attr_reader :context
def initialize
init = Win32API.new("audio.dll", "AudioContextInitialize", '', 'l')
@context=init.call()
end
def dispose
if @context!=0
init = Win32API.new("audio.dll", "AudioContextFree", 'l', '')
init.call(context)
@context=0
end
end
end
#####################################
# Needed because RGSS doesn't call at_exit procs on exit
# Exit is not called when game is reset (using F12)
$AtExitProcs=[] if !$AtExitProcs
def exit(code=0)
for p in $AtExitProcs
p.call
end
raise SystemExit.new(code)
end
def at_exit(&block)
$AtExitProcs.push(Proc.new(&block))
end
#####################################
# Works around a problem with FileTest.exist
# if directory contains accent marks
def safeExists?(f)
ret=false
File.open(f,"rb") { ret=true } rescue nil
return ret
end
module AudioState
w32_LL = Win32API.new("kernel32.dll", "LoadLibrary", 'p', 'l') # :nodoc:
w32_FL = Win32API.new("kernel32.dll", "FreeLibrary", 'p', 'l')# :nodoc:
if safeExists?("audio.dll")
@handle = w32_LL.call("audio.dll")
at_exit { w32_FL.call(@handle) }
AudioContextIsActive = Win32API.new("audio.dll","AudioContextIsActive","l","l")# :nodoc:
AudioContextPlay = Win32API.new("audio.dll","AudioContextPlay","lpllll","")# :nodoc:
AudioContextStop = Win32API.new("audio.dll","AudioContextStop","l","")# :nodoc:
AudioContextFadeOut = Win32API.new("audio.dll","AudioContextFadeOut","ll","")# :nodoc:
AudioContextGetPosition = Win32API.new("audio.dll","AudioContextGetPosition","l","l")# :nodoc:
AudioContextFadeIn = Win32API.new("audio.dll","AudioContextFadeIn","ll","")# :nodoc:
AudioContextSetVolume = Win32API.new("audio.dll","AudioContextSetVolume","ll","")# :nodoc:
AudioContextSEPlay = Win32API.new("audio.dll","AudioContextSEPlay","lplll","")# :nodoc:
if !@MEContext
@MEContext=AudioContext.new
at_exit { @MEContext.dispose }
end
if !@BGMContext
@BGMContext=AudioContext.new
at_exit { @BGMContext.dispose }
end
if !@BGSContext
@BGSContext=AudioContext.new
at_exit { @BGSContext.dispose }
end
if !@SEContext
@SEContext=AudioContext.new
at_exit { @SEContext.dispose }
end
else
AudioContextIsActive = nil # :nodoc:
AudioContextPlay = nil # :nodoc:
AudioContextStop = nil # :nodoc:
AudioContextFadeOut = nil # :nodoc:
AudioContextGetPosition = nil # :nodoc:
AudioContextFadeIn = nil # :nodoc:
AudioContextSetVolume = nil # :nodoc:
AudioContextSEPlay = nil # :nodoc:
end
@channel = nil
@bgm = nil
@name = ""
@pitch = 100
@bgmVolume = 100.0
@meVolume = 100.0
@bgsVolume = 100.0
@seVolume = 100.0
def self.setWaitingBGM(bgm,volume,pitch,position)
@waitingBGM=[bgm,volume,pitch,position]
end
def self.bgmActive?
return !@BGMContext ? false : (AudioContextIsActive.call(@BGMContext.context)!=0)
end
def self.meActive?
return !@MEContext ? false : (AudioContextIsActive.call(@MEContext.context)!=0)
end
def self.waitingBGM; @waitingBGM; end
def self.context; @BGMContext ? @BGMContext.context : nil; end
def self.meContext; @MEContext ? @MEContext.context : nil; end
def self.bgsContext; @BGSContext ? @BGSContext.context : nil; end
def self.seContext; @SEContext ? @SEContext.context : nil; end
def self.system; @system; end
def self.bgm; @bgm; end
def self.name; @name; end
def self.pitch; @pitch; end
def self.volume; @volume; end
def self.waitingBGM=(value);
Thread.exclusive { @waitingBGM=value; }
end
def self.volume=(value); @volume=value; end
def self.bgm=(value); @bgm=value; end
def self.name=(value); @name=value; end
def self.pitch=(value); @pitch=value; end
end
def Audio_bgm_playing?
AudioState.channel!=nil
end
def Audio_bgm_name
AudioState.name
end
def Audio_bgm_pitch
AudioState.pitch
end
def Audio_bgm_play(name, volume, pitch, position = 0)
volume=0 if !getPlayMusic()
begin
filename = canonicalize(RTP.getAudioPath(name))
if AudioState.meActive?
AudioState.setWaitingBGM(filename,volume,pitch,position)
return
end
AudioState::AudioContextPlay.call(AudioState.context,filename,volume,pitch,position,1)
AudioState.name=filename
AudioState.volume=volume
AudioState.pitch=pitch
rescue Hangup
rescue
p $!.message,$!.backtrace
end
end
def Audio_bgm_fadein(ms)
AudioState::AudioContextFadeIn.call(AudioState.context,ms.to_i)
end
def Audio_bgm_fade(ms)
AudioState::AudioContextFadeOut.call(AudioState.context,ms.to_i)
end
def Audio_bgm_stop()
begin
AudioState::AudioContextStop.call(AudioState.context)
AudioState.waitingBGM=nil
AudioState.name = ""
rescue
p $!.message,$!.backtrace
end
end
def Audio_bgm_get_position
return AudioState::AudioContextGetPosition.call(AudioState.context)
end
def Audio_bgm_get_volume
return 0 if !AudioState.bgmActive?
return AudioState.volume
end
def Audio_bgm_set_volume(volume)
return if !AudioState.bgmActive?
AudioState.volume = volume * 1.0
AudioState::AudioContextSetVolume.call(AudioState.context,volume.to_i)
end
def Audio_me_play(name, volume, pitch, position = 0)
volume=0 if !getPlayMusic()
begin
filename = canonicalize(RTP.getAudioPath(name))
if AudioState.bgmActive?
bgmPosition=Audio_bgm_get_position
AudioState.setWaitingBGM(
AudioState.name,
AudioState.volume,
AudioState.pitch,
bgmPosition
)
AudioState::AudioContextStop.call(AudioState.context)
end
AudioState::AudioContextPlay.call(AudioState.meContext,filename,
volume,pitch,position,0)
rescue
p $!.message,$!.backtrace
end
end
def Audio_me_fade(ms)
AudioState::AudioContextFadeOut.call(AudioState.meContext,ms)
end
def Audio_me_stop()
AudioState::AudioContextStop.call(AudioState.meContext)
end
def Audio_bgs_play(name, volume, pitch, position = 0)
volume=0 if !getPlaySound()
begin
filename = canonicalize(RTP.getAudioPath(name))
AudioState::AudioContextPlay.call(AudioState.bgsContext,filename,
volume,pitch,position,0)
rescue
p $!.message,$!.backtrace
end
end
def Audio_bgs_fade(ms)
AudioState::AudioContextFadeOut.call(AudioState.bgsContext,ms)
end
def Audio_bgs_stop()
AudioState::AudioContextStop.call(AudioState.bgsContext)
end
def Audio_se_play(name, volume, pitch, position = 0)
volume=0 if !getPlaySound()
begin
filename = canonicalize(RTP.getAudioPath(name))
AudioState::AudioContextSEPlay.call(AudioState.seContext,filename,
volume,pitch,position)
rescue
p $!.message,$!.backtrace
end
end
def Audio_se_stop()
AudioState::AudioContextStop.call(AudioState.seContext)
end
####################################################
if safeExists?("audio.dll")
module Graphics
if !defined?(audiomodule_update)
class << self
alias audiomodule_update update
end
end
def self.update
Audio.update
audiomodule_update
end
end
module Audio
@@musicstate = nil
@@soundstate = nil
def self.update
return if Graphics.frame_count%10!=0
if AudioState.waitingBGM && !AudioState.meActive?
waitbgm=AudioState.waitingBGM
AudioState.waitingBGM=nil
bgm_play(waitbgm[0],waitbgm[1],waitbgm[2],waitbgm[3])
end
end
def self.bgm_play(name,volume=80,pitch=100,position=nil)
begin
if position==nil || position==0
Audio_bgm_play(name,volume,pitch,0)
else
Audio_bgm_play(name,volume,pitch,position)
Audio_bgm_fadein(500)
end
rescue Hangup
bgm_play(name,volume,pitch,position)
end
end
def self.bgm_fade(ms)
Audio_bgm_fade(ms)
end
def self.bgm_stop
Audio_bgm_stop()
end
def self.bgm_position
return Audio_bgm_get_position
end
def self.me_play(name,volume=80,pitch=100)
Audio_me_play(name,volume,pitch,0)
end
def self.me_fade(ms)
Audio_me_fade(ms)
end
def self.me_stop
Audio_me_stop()
end
def self.bgs_play(name,volume=80,pitch=100)
Audio_bgs_play(name,volume,pitch,0)
end
def self.bgs_fade(ms)
Audio_bgs_fade(ms)
end
def self.bgs_stop
Audio_bgs_stop()
end
=begin
def self.se_play(name,volume=80,pitch=100)
Audio_se_play(name,volume,pitch,0)
end
def self.se_stop
Audio_se_stop()
end
=end
end
end # safeExists?("audio.dll")

View File

@@ -0,0 +1,292 @@
def pbStringToAudioFile(str)
if str[/^(.*)\:\s*(\d+)\s*\:\s*(\d+)\s*$/] # Of the format "XXX: ###: ###"
file = $1
volume = $2.to_i
pitch = $3.to_i
return RPG::AudioFile.new(file,volume,pitch)
elsif str[/^(.*)\:\s*(\d+)\s*$/] # Of the format "XXX: ###"
file = $1
volume = $2.to_i
return RPG::AudioFile.new(file,volume,100)
else
return RPG::AudioFile.new(str,100,100)
end
end
# Converts an object to an audio file.
# str -- Either a string showing the filename or an RPG::AudioFile object.
# Possible formats for _str_:
# filename volume and pitch 100
# filename:volume pitch 100
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbResolveAudioFile(str,volume=nil,pitch=nil)
if str.is_a?(String)
str = pbStringToAudioFile(str)
str.volume = volume || 100
str.pitch = pitch || 100
end
if str.is_a?(RPG::AudioFile)
if volume || pitch
return RPG::AudioFile.new(str.name,volume || str.volume || 100 ,
pitch || str.pitch || 100)
else
return str
end
end
return str
end
################################################################################
# Plays a BGM file.
# param -- Either a string showing the filename
# (relative to Audio/BGM/) or an RPG::AudioFile object.
# Possible formats for _param_:
# filename volume and pitch 100
# filename:volume pitch 100
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbBGMPlay(param,volume=nil,pitch=nil)
return if !param
param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""
if $game_system && $game_system.respond_to?("bgm_play")
$game_system.bgm_play(param)
return
elsif (RPG.const_defined?(:BGM) rescue false)
b=RPG::BGM.new(param.name,param.volume,param.pitch)
if b && b.respond_to?("play")
b.play
return
end
end
Audio.bgm_play(canonicalize("Audio/BGM/"+param.name),param.volume,param.pitch)
end
end
# Fades out or stops BGM playback. 'x' is the time in seconds to fade out.
def pbBGMFade(x=0.0); pbBGMStop(x);end
# Fades out or stops BGM playback. 'x' is the time in seconds to fade out.
def pbBGMStop(timeInSeconds=0.0)
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("bgm_fade")
$game_system.bgm_fade(timeInSeconds)
return
elsif $game_system && $game_system.respond_to?("bgm_stop")
$game_system.bgm_stop
return
elsif (RPG.const_defined?(:BGM) rescue false)
begin
(timeInSeconds>0.0) ? RPG::BGM.fade((timeInSeconds*1000).floor) : RPG::BGM.stop
return
rescue
end
end
(timeInSeconds>0.0) ? Audio.bgm_fade((timeInSeconds*1000).floor) : Audio.bgm_stop
end
################################################################################
# Plays an ME file.
# param -- Either a string showing the filename
# (relative to Audio/ME/) or an RPG::AudioFile object.
# Possible formats for _param_:
# filename volume and pitch 100
# filename:volume pitch 100
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbMEPlay(param,volume=nil,pitch=nil)
return if !param
param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""
if $game_system && $game_system.respond_to?("me_play")
$game_system.me_play(param)
return
elsif (RPG.const_defined?(:ME) rescue false)
b=RPG::ME.new(param.name,param.volume,param.pitch)
if b && b.respond_to?("play")
b.play; return
end
end
Audio.me_play(canonicalize("Audio/ME/"+param.name),param.volume,param.pitch)
end
end
# Fades out or stops ME playback. 'x' is the time in seconds to fade out.
def pbMEFade(x=0.0); pbMEStop(x);end
# Fades out or stops ME playback. 'x' is the time in seconds to fade out.
def pbMEStop(timeInSeconds=0.0)
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("me_fade")
$game_system.me_fade(timeInSeconds)
return
elsif $game_system && $game_system.respond_to?("me_stop")
$game_system.me_stop(nil)
return
elsif (RPG.const_defined?(:ME) rescue false)
begin
(timeInSeconds>0.0) ? RPG::ME.fade((timeInSeconds*1000).floor) : RPG::ME.stop
return
rescue
end
end
(timeInSeconds>0.0) ? Audio.me_fade((timeInSeconds*1000).floor) : Audio.me_stop
end
################################################################################
# Plays a BGS file.
# param -- Either a string showing the filename
# (relative to Audio/BGS/) or an RPG::AudioFile object.
# Possible formats for _param_:
# filename volume and pitch 100
# filename:volume pitch 100
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbBGSPlay(param,volume=nil,pitch=nil)
return if !param
param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""
if $game_system && $game_system.respond_to?("bgs_play")
$game_system.bgs_play(param)
return
elsif (RPG.const_defined?(:BGS) rescue false)
b=RPG::BGS.new(param.name,param.volume,param.pitch)
if b && b.respond_to?("play")
b.play; return
end
end
Audio.bgs_play(canonicalize("Audio/BGS/"+param.name),param.volume,param.pitch)
end
end
# Fades out or stops BGS playback. 'x' is the time in seconds to fade out.
def pbBGSFade(x=0.0); pbBGSStop(x);end
# Fades out or stops BGS playback. 'x' is the time in seconds to fade out.
def pbBGSStop(timeInSeconds=0.0)
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("bgs_fade")
$game_system.bgs_fade(timeInSeconds)
return
elsif $game_system && $game_system.respond_to?("bgs_play")
$game_system.bgs_play(nil)
return
elsif (RPG.const_defined?(:BGS) rescue false)
begin
(timeInSeconds>0.0) ? RPG::BGS.fade((timeInSeconds*1000).floor) : RPG::BGS.stop
return
rescue
end
end
(timeInSeconds>0.0) ? Audio.bgs_fade((timeInSeconds*1000).floor) : Audio.bgs_stop
end
################################################################################
# Plays an SE file.
# param -- Either a string showing the filename
# (relative to Audio/SE/) or an RPG::AudioFile object.
# Possible formats for _param_:
# filename volume and pitch 100
# filename:volume pitch 100
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbSEPlay(param,volume=nil,pitch=nil)
return if !param
param = pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""
if $game_system && $game_system.respond_to?("se_play")
$game_system.se_play(param)
return
end
if (RPG.const_defined?(:SE) rescue false)
b = RPG::SE.new(param.name,param.volume,param.pitch)
if b && b.respond_to?("play")
b.play
return
end
end
Audio.se_play(canonicalize("Audio/SE/"+param.name),param.volume,param.pitch)
end
end
# Stops SE playback.
def pbSEFade(x=0.0); pbSEStop(x);end
# Stops SE playback.
def pbSEStop(timeInSeconds=0.0)
if $game_system
$game_system.se_stop
elsif (RPG.const_defined?(:SE) rescue false)
RPG::SE.stop rescue nil
else
Audio.se_stop
end
end
################################################################################
# Plays a sound effect that plays when the player moves the cursor.
def pbPlayCursorSE
if $data_system && $data_system.respond_to?("cursor_se") &&
$data_system.cursor_se && $data_system.cursor_se.name!=""
pbSEPlay($data_system.cursor_se)
elsif $data_system && $data_system.respond_to?("sounds") &&
$data_system.sounds && $data_system.sounds[0] && $data_system.sounds[0].name!=""
pbSEPlay($data_system.sounds[0])
elsif FileTest.audio_exist?("Audio/SE/GUI sel cursor")
pbSEPlay("GUI sel cursor",80)
end
end
# Plays a sound effect that plays when a decision is confirmed or a choice is made.
def pbPlayDecisionSE
if $data_system && $data_system.respond_to?("decision_se") &&
$data_system.decision_se && $data_system.decision_se.name!=""
pbSEPlay($data_system.decision_se)
elsif $data_system && $data_system.respond_to?("sounds") &&
$data_system.sounds && $data_system.sounds[1] && $data_system.sounds[1].name!=""
pbSEPlay($data_system.sounds[1])
elsif FileTest.audio_exist?("Audio/SE/GUI sel decision")
pbSEPlay("GUI sel decision",80)
end
end
# Plays a sound effect that plays when a choice is canceled.
def pbPlayCancelSE
if $data_system && $data_system.respond_to?("cancel_se") &&
$data_system.cancel_se && $data_system.cancel_se.name!=""
pbSEPlay($data_system.cancel_se)
elsif $data_system && $data_system.respond_to?("sounds") &&
$data_system.sounds && $data_system.sounds[2] && $data_system.sounds[2].name!=""
pbSEPlay($data_system.sounds[2])
elsif FileTest.audio_exist?("Audio/SE/GUI sel cancel")
pbSEPlay("GUI sel cancel",80)
end
end
# Plays a buzzer sound effect.
def pbPlayBuzzerSE
if $data_system && $data_system.respond_to?("buzzer_se") &&
$data_system.buzzer_se && $data_system.buzzer_se.name!=""
pbSEPlay($data_system.buzzer_se)
elsif $data_system && $data_system.respond_to?("sounds") &&
$data_system.sounds && $data_system.sounds[3] && $data_system.sounds[3].name!=""
pbSEPlay($data_system.sounds[3])
elsif FileTest.audio_exist?("Audio/SE/GUI sel buzzer")
pbSEPlay("GUI sel buzzer",80)
end
end
# Plays a sound effect that plays when the player moves the cursor.
def pbPlayCloseMenuSE
if FileTest.audio_exist?("Audio/SE/GUI menu close")
pbSEPlay("GUI menu close",80)
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,657 @@
#===============================================================================
# Checking for files and directories
#===============================================================================
# Works around a problem with FileTest.directory if directory contains accent marks
def safeIsDirectory?(f)
ret = false
Dir.chdir(f) { ret = true } rescue nil
return ret
end
# Works around a problem with FileTest.exist if path contains accent marks
def safeExists?(f)
return FileTest.exist?(f) if f[/\A[\x20-\x7E]*\z/]
ret = false
begin
File.open(f,"rb") { ret = true }
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
ret = false
end
return ret
end
# Similar to "Dir.glob", but designed to work around a problem with accessing
# files if a path contains accent marks.
# "dir" is the directory path, "wildcard" is the filename pattern to match.
def safeGlob(dir,wildcard)
ret = []
afterChdir = false
begin
Dir.chdir(dir) {
afterChdir = true
Dir.glob(wildcard) { |f| ret.push(dir+"/"+f) }
}
rescue Errno::ENOENT
raise if afterChdir
end
if block_given?
ret.each { |f| yield(f) }
end
return (block_given?) ? nil : ret
end
# Finds the real path for an image file. This includes paths in encrypted
# archives. Returns nil if the path can't be found.
def pbResolveBitmap(x)
return nil if !x
noext = x.gsub(/\.(bmp|png|gif|jpg|jpeg)$/,"")
filename = nil
# RTP.eachPathFor(x) { |path|
# filename = pbTryString(path) if !filename
# filename = pbTryString(path+".gif") if !filename
# }
RTP.eachPathFor(noext) { |path|
filename = pbTryString(path+".png") if !filename
filename = pbTryString(path+".gif") if !filename
# filename = pbTryString(path+".jpg") if !filename
# filename = pbTryString(path+".jpeg") if !filename
# filename = pbTryString(path+".bmp") if !filename
}
return filename
end
# Finds the real path for an image file. This includes paths in encrypted
# archives. Returns _x_ if the path can't be found.
def pbBitmapName(x)
ret = pbResolveBitmap(x)
return (ret) ? ret : x
end
def getUnicodeString(addr)
return "" if addr==0
rtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
ret = ""
data = "xx"
index = (addr.is_a?(String)) ? 0 : addr
loop do
if addr.is_a?(String)
data = addr[index,2]
else
rtlMoveMemory_pi.call(data, index, 2)
end
codepoint = data.unpack("v")[0]
break if codepoint==0
index += 2
if codepoint<=0x7F
ret += codepoint.chr
elsif codepoint<=0x7FF
ret += (0xC0|((codepoint>>6)&0x1F)).chr
ret += (0x80|(codepoint &0x3F)).chr
elsif codepoint<=0xFFFF
ret += (0xE0|((codepoint>>12)&0x0F)).chr
ret += (0x80|((codepoint>>6)&0x3F)).chr
ret += (0x80|(codepoint &0x3F)).chr
elsif codepoint<=0x10FFFF
ret += (0xF0|((codepoint>>18)&0x07)).chr
ret += (0x80|((codepoint>>12)&0x3F)).chr
ret += (0x80|((codepoint>>6)&0x3F)).chr
ret += (0x80|(codepoint &0x3F)).chr
end
end
return ret
end
def getUnicodeStringFromAnsi(addr)
return "" if addr==0
rtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
ret = ""
data = "x"
index = (addr.is_a?(String)) ? 0 : addr
loop do
if addr.is_a?(String)
data = addr[index,1]
else
rtlMoveMemory_pi.call(data, index, 1)
end
index += 1
codepoint = data.unpack("C")[0]
break if codepoint==0 || !codepoint
break if codepoint==0
if codepoint<=0x7F
ret += codepoint.chr
else
ret += (0xC0|((codepoint>>6)&0x1F)).chr
ret += (0x80|(codepoint &0x3F)).chr
end
end
return ret
end
def getKnownFolder(guid)
packedGuid = guid.pack("VvvC*")
shGetKnownFolderPath = Win32API.new("shell32.dll","SHGetKnownFolderPath","pllp","i") rescue nil
coTaskMemFree = Win32API.new("ole32.dll","CoTaskMemFree","i","") rescue nil
return "" if !(shGetKnownFolderPath && coTaskMemFree)
path = "\0"*4
ret = shGetKnownFolderPath.call(packedGuid,0,0,path)
path = path.unpack("V")[0]
ret = getUnicodeString(path)
coTaskMemFree.call(path)
return ret
end
module RTP
@rtpPaths = nil
def self.exists?(filename,extensions=[])
return false if !filename || filename==""
eachPathFor(filename) { |path|
return true if safeExists?(path)
for ext in extensions
return true if safeExists?(path+ext)
end
}
return false
end
def self.getImagePath(filename)
return self.getPath(filename,["",".png",".gif"]) # ".jpg",".bmp",".jpeg"
end
def self.getAudioPath(filename)
return self.getPath(filename,["",".mp3",".wav",".wma",".mid",".ogg",".midi"])
end
def self.getPath(filename,extensions=[])
return filename if !filename || filename==""
eachPathFor(filename) { |path|
return path if safeExists?(path)
for ext in extensions
file = path+ext
return file if safeExists?(file)
end
}
return filename
end
# Gets the absolute RGSS paths for the given file name
def self.eachPathFor(filename)
return if !filename
if filename[/^[A-Za-z]\:[\/\\]/] || filename[/^[\/\\]/]
# filename is already absolute
yield filename
else
# relative path
RTP.eachPath { |path|
if path=="./"
yield filename
else
yield path+filename
end
}
end
end
# Gets all RGSS search paths
def self.eachPath
# XXX: Use "." instead of Dir.pwd because of problems retrieving files if
# the current directory contains an accent mark
yield ".".gsub(/[\/\\]/,"/").gsub(/[\/\\]$/,"")+"/"
if !@rtpPaths
tmp = Sprite.new
isRgss2 = tmp.respond_to?("wave_amp")
tmp.dispose
@rtpPaths = []
if isRgss2
rtp = getGameIniValue("Game","RTP")
if rtp!=""
rtp = MiniRegistry.get(MiniRegistry::HKEY_LOCAL_MACHINE,
"SOFTWARE\\Enterbrain\\RGSS2\\RTP",rtp,nil)
if rtp && safeIsDirectory?(rtp)
@rtpPaths.push(rtp.sub(/[\/\\]$/,"")+"/")
end
end
else
%w( RTP1 RTP2 RTP3 ).each { |v|
rtp = getGameIniValue("Game",v)
if rtp!=""
rtp = MiniRegistry.get(MiniRegistry::HKEY_LOCAL_MACHINE,
"SOFTWARE\\Enterbrain\\RGSS\\RTP",rtp,nil)
if rtp && safeIsDirectory?(rtp)
@rtpPaths.push(rtp.sub(/[\/\\]$/,"")+"/")
end
end
}
end
end
@rtpPaths.each { |x| yield x }
end
private
@@folder = nil
def self.getGameIniValue(section,key)
val = "\0"*256
gps = Win32API.new('kernel32', 'GetPrivateProfileString',%w(p p p p l p), 'l')
gps.call(section, key, "", val, 256, ".\\Game.ini")
val.delete!("\0")
return val
end
def self.isDirWritable(dir)
return false if !dir || dir==""
loop do
name = dir.gsub(/[\/\\]$/,"")+"/writetest"
for i in 0...12
name += sprintf("%02X",rand(256))
end
name += ".tmp"
if !safeExists?(name)
retval = false
begin
File.open(name,"wb") { retval = true }
rescue Errno::EINVAL, Errno::EACCES, Errno::ENOENT
ensure
File.delete(name) rescue nil
end
return retval
end
end
end
def self.ensureGameDir(dir)
title = RTP.getGameIniValue("Game","Title")
title = "RGSS Game" if title==""
title = title.gsub(/[^\w ]/,"_")
newdir = dir.gsub(/[\/\\]$/,"")+"/"
# Convert to UTF-8 because of ANSI function
newdir += getUnicodeStringFromAnsi(title)
Dir.mkdir(newdir) rescue nil
ret = safeIsDirectory?(newdir) ? newdir : dir
return ret
end
def self.getSaveFileName(fileName)
return getSaveFolder().gsub(/[\/\\]$/,"")+"/"+fileName
end
def self.getSaveFolder
if !@@folder
# XXX: Use "." instead of Dir.pwd because of problems retrieving files if
# the current directory contains an accent mark
pwd = "."
# Get the known folder path for saved games
savedGames = getKnownFolder([
0x4c5c32ff,0xbb9d,0x43b0,0xb5,0xb4,0x2d,0x72,0xe5,0x4e,0xaa,0xa4])
if savedGames && savedGames!="" && isDirWritable(savedGames)
pwd = ensureGameDir(savedGames)
end
if isDirWritable(pwd)
@@folder = pwd
else
appdata = ENV["LOCALAPPDATA"]
if isDirWritable(appdata)
appdata = ensureGameDir(appdata)
else
appdata = ENV["APPDATA"]
if isDirWritable(appdata)
appdata = ensureGameDir(appdata)
elsif isDirWritable(pwd)
appdata = pwd
else
appdata = "."
end
end
@@folder = appdata
end
end
return @@folder
end
end
module FileTest
Image_ext = ['.bmp', '.png', '.jpg', '.jpeg', '.gif']
Audio_ext = ['.mp3', '.mid', '.midi', '.ogg', '.wav', '.wma']
def self.audio_exist?(filename)
return RTP.exists?(filename,Audio_ext)
end
def self.image_exist?(filename)
return RTP.exists?(filename,Image_ext)
end
end
# Used to determine whether a data file exists (rather than a graphics or
# audio file). Doesn't check RTP, but does check encrypted archives.
def pbRgssExists?(filename)
filename = canonicalize(filename)
if safeExists?("./Game.rgssad") || safeExists?("./Game.rgss2a")
return pbGetFileChar(filename)!=nil
else
return safeExists?(filename)
end
end
# Opens an IO, even if the file is in an encrypted archive.
# Doesn't check RTP for the file.
def pbRgssOpen(file,mode=nil)
#File.open("debug.txt","ab") { |fw| fw.write([file,mode,Time.now.to_f].inspect+"\r\n") }
if !safeExists?("./Game.rgssad") && !safeExists?("./Game.rgss2a")
if block_given?
File.open(file,mode) { |f| yield f }
return nil
else
return File.open(file,mode)
end
end
file = canonicalize(file)
Marshal.neverload = true
begin
str = load_data(file)
ensure
Marshal.neverload = false
end
if block_given?
StringInput.open(str) { |f| yield f }
return nil
else
return StringInput.open(str)
end
end
# Gets at least the first byte of a file. Doesn't check RTP, but does check
# encrypted archives.
def pbGetFileChar(file)
file = canonicalize(file)
if !safeExists?("./Game.rgssad") && !safeExists?("./Game.rgss2a")
return nil if !safeExists?(file)
begin
File.open(file,"rb") { |f| return f.read(1) } # read one byte
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
return nil
end
end
Marshal.neverload = true
str = nil
begin
str = load_data(file)
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, RGSSError
str = nil
ensure
Marshal.neverload = false
end
return str
end
def pbTryString(x)
ret = pbGetFileChar(x)
return (ret!=nil && ret!="") ? x : nil
end
# Gets the contents of a file. Doesn't check RTP, but does check
# encrypted archives.
def pbGetFileString(file)
file = canonicalize(file)
if !(safeExists?("./Game.rgssad") || safeExists?("./Game.rgss2a"))
return nil if !safeExists?(file)
begin
File.open(file,"rb") { |f| return f.read } # read all data
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
return nil
end
end
Marshal.neverload = true
str = nil
begin
str = load_data(file)
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, RGSSError
str = nil
ensure
Marshal.neverload = false
end
return str
end
#===============================================================================
#
#===============================================================================
module MiniRegistry
HKEY_CLASSES_ROOT = 0x80000000
HKEY_CURRENT_USER = 0x80000001
HKEY_LOCAL_MACHINE = 0x80000002
HKEY_USERS = 0x80000003
FormatMessageA = Win32API.new("kernel32","FormatMessageA","LPLLPLP","L")
RegOpenKeyExA = Win32API.new("advapi32","RegOpenKeyExA","LPLLP","L")
RegCloseKey = Win32API.new("advapi32","RegCloseKey","L","L")
RegQueryValueExA = Win32API.new("advapi32","RegQueryValueExA","LPLPPP","L")
def self.open(hkey,subkey,bit64=false)
key = 0.chr*4
flag = bit64 ? 0x20119 : 0x20019
rg = RegOpenKeyExA.call(hkey, subkey, 0, flag, key)
return nil if rg!=0
key = key.unpack("V")[0]
if block_given?
begin
yield(key)
ensure
check(RegCloseKey.call(key))
end
else
return key
end
end
def self.close(hkey); check(RegCloseKey.call(hkey)) if hkey; end
def self.get(hkey,subkey,name,defaultValue=nil,bit64=false)
self.open(hkey,subkey,bit64) { |key|
return self.read(key,name) rescue defaultValue
}
return defaultValue
end
def self.read(hkey,name)
hkey = 0 if !hkey
type = 0.chr*4
size = 0.chr*4
check(RegQueryValueExA.call(hkey,name,0,type,0,size))
data = " "*size.unpack("V")[0]
check(RegQueryValueExA.call(hkey,name,0,type,data,size))
type = type.unpack("V")[0]
data = data[0,size.unpack("V")[0]]
case type
when 1; return data.chop # REG_SZ
when 2; return data.gsub(/%([^%]+)%/) { ENV[$1] || $& } # REG_EXPAND_SZ
when 3; return data # REG_BINARY
when 4; return data.unpack("V")[0] # REG_DWORD
when 5; return data.unpack("V")[0] # REG_DWORD_BIG_ENDIAN
when 11; qw = data.unpack("VV"); return (data[1]<<32|data[0]) # REG_QWORD
else; raise "Type #{type} not supported."
end
end
private
def self.check(code)
if code!=0
msg = "\0"*1024
len = FormatMessageA.call(0x1200, 0, code, 0, msg, 1024, 0)
raise msg[0, len].tr("\r", '').chomp
end
end
end
class StringInput
include Enumerable
class << self
def new( str )
if block_given?
begin
f = super
yield f
ensure
f.close if f
end
else
super
end
end
alias open new
end
def initialize( str )
@string = str
@pos = 0
@closed = false
@lineno = 0
end
attr_reader :lineno,:string
def inspect
return "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@string[0,30].inspect}>"
end
def close
raise IOError, 'closed stream' if @closed
@pos = nil
@closed = true
end
def closed?; @closed; end
def pos
raise IOError, 'closed stream' if @closed
[@pos, @string.size].min
end
alias tell pos
def rewind; seek(0); end
def pos=(value); seek(value); end
def seek(offset, whence=IO::SEEK_SET)
raise IOError, 'closed stream' if @closed
case whence
when IO::SEEK_SET; @pos = offset
when IO::SEEK_CUR; @pos += offset
when IO::SEEK_END; @pos = @string.size - offset
else
raise ArgumentError, "unknown seek flag: #{whence}"
end
@pos = 0 if @pos < 0
@pos = [@pos, @string.size + 1].min
offset
end
def eof?
raise IOError, 'closed stream' if @closed
@pos > @string.size
end
def each( &block )
raise IOError, 'closed stream' if @closed
begin
@string.each(&block)
ensure
@pos = 0
end
end
def gets
raise IOError, 'closed stream' if @closed
if idx = @string.index(?\n, @pos)
idx += 1 # "\n".size
line = @string[ @pos ... idx ]
@pos = idx
@pos += 1 if @pos == @string.size
else
line = @string[ @pos .. -1 ]
@pos = @string.size + 1
end
@lineno += 1
line
end
def getc
raise IOError, 'closed stream' if @closed
ch = @string[@pos]
@pos += 1
@pos += 1 if @pos == @string.size
ch
end
def read( len = nil )
raise IOError, 'closed stream' if @closed
if !len
return nil if eof?
rest = @string[@pos ... @string.size]
@pos = @string.size + 1
return rest
end
str = @string[@pos, len]
@pos += len
@pos += 1 if @pos == @string.size
str
end
def read_all; read(); end
alias sysread read
end
module ::Marshal
class << self
if !@oldloadAliased
alias oldload load
@oldloadAliased = true
end
@@neverload = false
def neverload
return @@neverload
end
def neverload=(value)
@@neverload = value
end
def load(port,*arg)
if @@neverload
if port.is_a?(IO)
return port.read
end
return port
end
oldpos = port.pos if port.is_a?(IO)
begin
oldload(port,*arg)
rescue
p [$!.class,$!.message,$!.backtrace]
if port.is_a?(IO)
port.pos = oldpos
return port.read
end
return port
end
end
end
end

View File

@@ -0,0 +1,497 @@
class Hangup < Exception; end
def strsplit(str,re)
ret=[]
tstr=str
while re=~tstr
ret[ret.length]=$~.pre_match
tstr=$~.post_match
end
ret[ret.length]=tstr if ret.length
return ret
end
def canonicalize(c)
csplit = strsplit(c,/[\/\\]/)
pos = -1
ret = []
retstr = ""
for x in csplit
if x=="."
elsif x==".."
if pos>=0
ret.delete_at(pos)
pos -= 1
end
else
ret.push(x)
pos += 1
end
end
for i in 0...ret.length
retstr += "/" if i>0
retstr += ret[i]
end
return retstr
end
#####################################################################
class WeakRef
@@id_map = {}
@@id_rev_map = {}
@@final = lambda { |id|
__old_status = Thread.critical
Thread.critical = true
begin
rids = @@id_map[id]
if rids
for rid in rids
@@id_rev_map.delete(rid)
end
@@id_map.delete(id)
end
rid = @@id_rev_map[id]
if rid
@@id_rev_map.delete(id)
@@id_map[rid].delete(id)
@@id_map.delete(rid) if @@id_map[rid].empty?
end
ensure
Thread.critical = __old_status
end
}
# Create a new WeakRef from +orig+.
def initialize(orig)
__setobj__(orig)
end
def __getobj__
unless @@id_rev_map[self.__id__] == @__id
return nil
end
begin
ObjectSpace._id2ref(@__id)
rescue RangeError
return nil
end
end
def __setobj__(obj)
@__id = obj.__id__
__old_status = Thread.critical
begin
Thread.critical = true
unless @@id_rev_map.key?(self)
ObjectSpace.define_finalizer obj, @@final
ObjectSpace.define_finalizer self, @@final
end
@@id_map[@__id] = [] unless @@id_map[@__id]
ensure
Thread.critical = __old_status
end
@@id_map[@__id].push self.__id__
@@id_rev_map[self.__id__] = @__id
end
# Returns true if the referenced object still exists, and false if it has
# been garbage collected.
def weakref_alive?
@@id_rev_map[self.__id__] == @__id
end
end
class WeakHashtable
include Enumerable
def initialize
@hash={}
end
def clear
@hash.clear
end
def delete(value)
@hash.delete(value)
end
def include?(value)
@hash.include?(value)
end
def each
@hash.each { |i| yield i }
end
def keys
@hash.keys
end
def values
@hash.values
end
def [](key)
o=@hash[key]
return o if !o
if o.weakref_alive?
o=o.__getobj__
else
@hash.delete(key)
o=nil
end
return o
end
def []=(key,o)
if o!=nil
o=WeakRef.new(o)
end
@hash[key]=o
end
end
# Cache from RPG Maker VX library
module Cache
def self.system(x,hue=0)
BitmapCache.load_bitmap("Graphics/System/"+x,hue, true)
end
def self.character(x,hue=0)
BitmapCache.load_bitmap("Graphics/Characters/"+x,hue, true)
end
def self.picture(x,hue=0)
BitmapCache.load_bitmap("Graphics/Pictures/"+x,hue, true)
end
def self.animation(x,hue=0)
BitmapCache.load_bitmap("Graphics/Animations/"+x,hue, true)
end
def self.battler(x,hue=0)
BitmapCache.load_bitmap("Graphics/Battlers/"+x,hue, true)
end
def self.face(x,hue=0)
BitmapCache.load_bitmap("Graphics/Faces/"+x,hue, true)
end
def self.parallax(x,hue=0)
BitmapCache.load_bitmap("Graphics/Parallaxes/"+x,hue, true)
end
def self.clear
BitmapCache.clear()
end
def self.load_bitmap(dir,name,hue=0)
BitmapCache.load_bitmap(dir+name,hue, true)
end
end
# RPG::Cache from RPG Maker XP library
module RPG
module Cache
def self.load_bitmap(folder_name, filename, hue = 0)
BitmapCache.load_bitmap(folder_name+filename.to_s,hue, true)
end
def self.animation(filename, hue)
self.load_bitmap("Graphics/Animations/", filename, hue)
end
def self.autotile(filename)
self.load_bitmap("Graphics/Autotiles/", filename)
end
def self.battleback(filename)
self.load_bitmap("Graphics/Battlebacks/", filename)
end
def self.battler(filename, hue)
self.load_bitmap("Graphics/Battlers/", filename, hue)
end
def self.character(filename, hue)
self.load_bitmap("Graphics/Characters/", filename, hue)
end
def self.fog(filename, hue)
self.load_bitmap("Graphics/Fogs/", filename, hue)
end
def self.gameover(filename)
self.load_bitmap("Graphics/Gameovers/", filename)
end
def self.icon(filename)
self.load_bitmap("Graphics/Icons/", filename)
end
def self.panorama(filename, hue)
self.load_bitmap("Graphics/Panoramas/", filename, hue)
end
def self.picture(filename)
self.load_bitmap("Graphics/Pictures/", filename)
end
def self.tileset(filename)
self.load_bitmap("Graphics/Tilesets/", filename)
end
def self.title(filename)
self.load_bitmap("Graphics/Titles/", filename)
end
def self.windowskin(filename)
self.load_bitmap("Graphics/Windowskins/", filename)
end
def self.tile(filename, tile_id, hue)
BitmapCache.tile(filename,tile_id,hue)
end
def self.clear
BitmapCache.clear()
end
end
end
# A safer version of RPG::Cache, this module loads bitmaps that keep an internal
# reference count. Each call to dispose decrements the reference count and the
# bitmap is freed when the reference count reaches 0.
class Thread
def Thread.exclusive
_old = Thread.critical
begin
Thread.critical = true
return yield
ensure
Thread.critical = _old
end
end
end
class BitmapWrapper < Bitmap
@@disposedBitmaps={}
@@keys={}
=begin
@@final = lambda { |id|
Thread.exclusive {
if @@disposedBitmaps[id]!=true
File.open("debug.txt","ab") { |f|
f.write("Bitmap finalized without being disposed: #{@@keys[id]}\r\n")
}
end
@@disposedBitmaps[id]=nil
}
}
=end
attr_reader :refcount
def dispose
return if self.disposed?
@refcount-=1
if @refcount==0
super
#Thread.exclusive { @@disposedBitmaps[__id__]=true }
end
end
def initialize(*arg)
super
@refcount=1
#Thread.exclusive { @@keys[__id__]=arg.inspect+caller(1).inspect }
#ObjectSpace.define_finalizer(self,@@final)
end
def resetRef # internal
@refcount=1
end
def copy
bm=self.clone
bm.resetRef
return bm
end
def addRef
@refcount+=1
end
end
module BitmapCache
@cache = WeakHashtable.new
def self.fromCache(i)
return nil if !@cache.include?(i)
obj=@cache[i]
return nil if obj && obj.disposed?
return obj
end
def self.setKey(key,obj)
@cache[key]=obj
end
def self.debug
File.open("bitmapcache2.txt","wb") { |f|
for i in @cache.keys
k = fromCache(i)
if !k
f.write("#{i} (nil)\r\n")
elsif k.disposed?
f.write("#{i} (disposed)\r\n")
else
f.write("#{i} (#{k.refcount}, #{k.width}x#{k.height})\r\n")
end
end
}
end
def self.load_bitmap(path, hue = 0, failsafe = false)
cached = true
path = canonicalize(path)
objPath = fromCache(path)
if !objPath
@cleancounter = ((@cleancounter || 0) + 1)%10
if @cleancounter == 0
for i in @cache.keys
@cache.delete(i) if !fromCache(i)
end
end
begin
bm = BitmapWrapper.new(path)
rescue Hangup
begin
bm = BitmapWrapper.new(path)
rescue
raise _INTL("Failed to load the bitmap located at: {1}",path) if !failsafe
bm = BitmapWrapper.new(32,32)
end
rescue
raise _INTL("Failed to load the bitmap located at: {1}",path) if !failsafe
bm = BitmapWrapper.new(32,32)
end
objPath = bm
@cache[path] = objPath
cached=false
end
if hue == 0
objPath.addRef if cached
return objPath
else
key = [path, hue]
objKey = fromCache(key)
if !objKey
bitmap = objPath.copy
bitmap.hue_change(hue) if hue!=0
objKey = bitmap
@cache[key] = objKey
else
objKey.addRef
end
return objKey
end
end
def self.animation(filename, hue)
self.load_bitmap("Graphics/Animations/"+filename, hue)
end
def self.autotile(filename)
self.load_bitmap("Graphics/Autotiles/"+ filename)
end
def self.battleback(filename)
self.load_bitmap("Graphics/Battlebacks/"+ filename)
end
def self.battler(filename, hue)
self.load_bitmap("Graphics/Battlers/"+ filename, hue)
end
def self.character(filename, hue)
self.load_bitmap("Graphics/Characters/"+ filename, hue)
end
def self.fog(filename, hue)
self.load_bitmap("Graphics/Fogs/"+ filename, hue)
end
def self.gameover(filename)
self.load_bitmap("Graphics/Gameovers/"+ filename)
end
def self.icon(filename)
self.load_bitmap("Graphics/Icons/"+ filename)
end
def self.panorama(filename, hue)
self.load_bitmap("Graphics/Panoramas/"+ filename, hue)
end
def self.picture(filename)
self.load_bitmap("Graphics/Pictures/"+ filename)
end
def self.tileset(filename)
self.load_bitmap("Graphics/Tilesets/"+ filename)
end
def self.title(filename)
self.load_bitmap("Graphics/Titles/"+ filename)
end
def self.windowskin(filename)
self.load_bitmap("Graphics/Windowskins/"+ filename)
end
def self.tileEx(filename, tile_id, hue)
key = [filename, tile_id, hue]
objKey=fromCache(key)
if !objKey
bitmap=BitmapWrapper.new(32, 32)
x = (tile_id - 384) % 8 * 32
y = (tile_id - 384) / 8 * 32
rect = Rect.new(x, y, 32, 32)
tileset = yield(filename)
bitmap.blt(0, 0, tileset, rect)
tileset.dispose
bitmap.hue_change(hue) if hue!=0
objKey=bitmap
@cache[key]=objKey
else
objKey.addRef
end
objKey
end
def self.tile(filename, tile_id, hue)
return self.tileEx(filename, tile_id,hue) { |f| self.tileset(f) }
end
def self.clear
@cache = {}
GC.start
end
end

View File

@@ -0,0 +1,608 @@
class WindowCursorRect < Rect
def initialize(window)
@window=window
@x=0
@y=0
@width=0
@height=0
end
attr_reader :x,:y,:width,:height
def empty
needupdate=@x!=0 || @y!=0 || @width!=0 || @height!=0
if needupdate
@x=0
@y=0
@width=0
@height=0
@window.width=@window.width
end
end
def isEmpty?
return @x==0 && @y==0 && @width==0 && @height==0
end
def set(x,y,width,height)
needupdate=@x!=x || @y!=y || @width!=width || @height!=height
if needupdate
@x=x
@y=y
@width=width
@height=height
@window.width=@window.width
end
end
def height=(value)
@height=value; @window.width=@window.width
end
def width=(value)
@width=value; @window.width=@window.width
end
def x=(value)
@x=value; @window.width=@window.width
end
def y=(value)
@y=value; @window.width=@window.width
end
end
class Window
attr_reader :tone
attr_reader :color
attr_reader :blend_type
attr_reader :contents_blend_type
attr_reader :viewport
attr_reader :contents
attr_reader :ox
attr_reader :oy
attr_reader :x
attr_reader :y
attr_reader :z
attr_reader :width
attr_reader :active
attr_reader :pause
attr_reader :height
attr_reader :opacity
attr_reader :back_opacity
attr_reader :contents_opacity
attr_reader :visible
attr_reader :cursor_rect
attr_reader :openness
attr_reader :stretch
def windowskin
@_windowskin
end
def initialize(viewport=nil)
@sprites={}
@spritekeys=[
"back",
"corner0","side0","scroll0",
"corner1","side1","scroll1",
"corner2","side2","scroll2",
"corner3","side3","scroll3",
"cursor","contents","pause"
]
@sidebitmaps=[nil,nil,nil,nil]
@cursorbitmap=nil
@bgbitmap=nil
@viewport=viewport
for i in @spritekeys
@sprites[i]=Sprite.new(@viewport)
end
@disposed=false
@tone=Tone.new(0,0,0)
@color=Color.new(0,0,0,0)
@blankcontents=Bitmap.new(1,1) # RGSS2 requires this
@contents=@blankcontents
@_windowskin=nil
@rpgvx=false # Set to true to emulate RPGVX windows
@x=0
@y=0
@width=0
@openness=255
@height=0
@ox=0
@oy=0
@z=0
@stretch=true
@visible=true
@active=true
@blend_type=0
@contents_blend_type=0
@opacity=255
@back_opacity=255
@contents_opacity=255
@cursor_rect=WindowCursorRect.new(self)
@cursorblink=0
@cursoropacity=255
@pause=false
@pauseopacity=255
@pauseframe=0
privRefresh(true)
end
def dispose
if !self.disposed?
for i in @sprites
i[1].dispose if i[1]
@sprites[i[0]]=nil
end
for i in 0...@sidebitmaps.length
@sidebitmaps[i].dispose if @sidebitmaps[i]
@sidebitmaps[i]=nil
end
@blankcontents.dispose
@cursorbitmap.dispose if @cursorbitmap
@backbitmap.dispose if @backbitmap
@sprites.clear
@sidebitmaps.clear
@_windowskin=nil
@_contents=nil
@disposed=true
end
end
def openness=(value)
@openness=value
@openness=0 if @openness<0
@openness=255 if @openness>255
privRefresh
end
def stretch=(value)
@stretch=value
privRefresh(true)
end
def visible=(value)
@visible=value
privRefresh
end
def viewport=(value)
@viewport=value
for i in @spritekeys
@sprites[i].dispose
if @sprites[i].is_a?(Sprite)
@sprites[i]=Sprite.new(@viewport)
else
@sprites[i]=nil
end
end
privRefresh(true)
end
def z=(value)
@z=value
privRefresh
end
def disposed?
return @disposed
end
def contents=(value)
@contents=value
privRefresh
end
def windowskin=(value)
@_windowskin=value
if value && value.is_a?(Bitmap) && !value.disposed? && value.width==128
@rpgvx=true
else
@rpgvx=false
end
privRefresh(true)
end
def ox=(value)
@ox=value
privRefresh
end
def active=(value)
@active=value
privRefresh(true)
end
def cursor_rect=(value)
if !value
@cursor_rect.empty
else
@cursor_rect.set(value.x,value.y,value.width,value.height)
end
end
def oy=(value)
@oy=value
privRefresh
end
def width=(value)
@width=value
privRefresh(true)
end
def height=(value)
@height=value
privRefresh(true)
end
def pause=(value)
@pause=value
@pauseopacity=0 if !value
privRefresh
end
def x=(value)
@x=value
privRefresh
end
def y=(value)
@y=value
privRefresh
end
def opacity=(value)
@opacity=value
@opacity=0 if @opacity<0
@opacity=255 if @opacity>255
privRefresh
end
def back_opacity=(value)
@back_opacity=value
@back_opacity=0 if @back_opacity<0
@back_opacity=255 if @back_opacity>255
privRefresh
end
def contents_opacity=(value)
@contents_opacity=value
@contents_opacity=0 if @contents_opacity<0
@contents_opacity=255 if @contents_opacity>255
privRefresh
end
def tone=(value)
@tone=value
privRefresh
end
def color=(value)
@color=value
privRefresh
end
def blend_type=(value)
@blend_type=value
privRefresh
end
def flash(color,duration)
return if disposed?
for i in @sprites
i[1].flash(color,duration)
end
end
def update
return if disposed?
mustchange=false
if @active
if @cursorblink==0
@cursoropacity-=8
@cursorblink=1 if @cursoropacity<=128
else
@cursoropacity+=8
@cursorblink=0 if @cursoropacity>=255
end
mustchange=true if !@cursor_rect.isEmpty?
else
mustchange=true if @cursoropacity!=128
@cursoropacity=128
end
if @pause
@pauseframe=(Graphics.frame_count / 8) % 4
@pauseopacity=[@pauseopacity+64,255].min
mustchange=true
end
privRefresh if mustchange
for i in @sprites
i[1].update
end
end
private
def ensureBitmap(bitmap,dwidth,dheight)
if !bitmap||bitmap.disposed?||bitmap.width<dwidth||bitmap.height<dheight
bitmap.dispose if bitmap
bitmap=Bitmap.new([1,dwidth].max,[1,dheight].max)
end
return bitmap
end
def tileBitmap(dstbitmap,dstrect,srcbitmap,srcrect)
return if !srcbitmap || srcbitmap.disposed?
left=dstrect.x
top=dstrect.y
y=0;loop do break unless y<dstrect.height
x=0;loop do break unless x<dstrect.width
dstbitmap.blt(x+left,y+top,srcbitmap,srcrect)
x+=srcrect.width
end
y+=srcrect.height
end
end
def privRefresh(changeBitmap=false)
return if self.disposed?
backopac=self.back_opacity*self.opacity/255
contopac=self.contents_opacity
cursoropac=@cursoropacity*contopac/255
for i in 0...4
@sprites["corner#{i}"].bitmap=@_windowskin
@sprites["scroll#{i}"].bitmap=@_windowskin
end
@sprites["pause"].bitmap=@_windowskin
@sprites["contents"].bitmap=@contents
if @_windowskin && !@_windowskin.disposed?
for i in 0...4
@sprites["corner#{i}"].opacity=@opacity
@sprites["corner#{i}"].tone=@tone
@sprites["corner#{i}"].color=@color
@sprites["corner#{i}"].blend_type=@blend_type
@sprites["corner#{i}"].visible=@visible
@sprites["side#{i}"].opacity=@opacity
@sprites["side#{i}"].tone=@tone
@sprites["side#{i}"].color=@color
@sprites["side#{i}"].blend_type=@blend_type
@sprites["side#{i}"].visible=@visible
@sprites["scroll#{i}"].opacity=@opacity
@sprites["scroll#{i}"].tone=@tone
@sprites["scroll#{i}"].blend_type=@blend_type
@sprites["scroll#{i}"].color=@color
@sprites["scroll#{i}"].visible=@visible
end
for i in ["back","cursor","pause","contents"]
@sprites[i].color=@color
@sprites[i].tone=@tone
@sprites[i].blend_type=@blend_type
end
@sprites["contents"].blend_type=@contents_blend_type
@sprites["back"].opacity=backopac
@sprites["contents"].opacity=contopac
@sprites["cursor"].opacity=cursoropac
@sprites["pause"].opacity=@pauseopacity
@sprites["back"].visible=@visible
@sprites["contents"].visible=@visible && @openness==255
@sprites["pause"].visible=@visible && @pause
@sprites["cursor"].visible=@visible && @openness==255
hascontents=(@contents && !@contents.disposed?)
@sprites["scroll0"].visible = @visible && hascontents && @oy > 0
@sprites["scroll1"].visible = @visible && hascontents && @ox > 0
@sprites["scroll2"].visible = @visible && hascontents &&
(@contents.width - @ox) > @width-32
@sprites["scroll3"].visible = @visible && hascontents &&
(@contents.height - @oy) > @height-32
else
for i in 0...4
@sprites["corner#{i}"].visible=false
@sprites["side#{i}"].visible=false
@sprites["scroll#{i}"].visible=false
end
@sprites["contents"].visible=@visible && @openness==255
@sprites["contents"].color=@color
@sprites["contents"].tone=@tone
@sprites["contents"].blend_type=@contents_blend_type
@sprites["contents"].opacity=contopac
@sprites["back"].visible=false
@sprites["pause"].visible=false
@sprites["cursor"].visible=false
end
for i in @sprites
i[1].z=@z
end
if @rpgvx
@sprites["cursor"].z=@z # For Compatibility
@sprites["contents"].z=@z # For Compatibility
@sprites["pause"].z=@z # For Compatibility
else
@sprites["cursor"].z=@z+1 # For Compatibility
@sprites["contents"].z=@z+2 # For Compatibility
@sprites["pause"].z=@z+2 # For Compatibility
end
if @rpgvx
trimX=64
trimY=0
backRect=Rect.new(0,0,64,64)
blindsRect=Rect.new(0,64,64,64)
else
trimX=128
trimY=0
backRect=Rect.new(0,0,128,128)
blindsRect=nil
end
@sprites["corner0"].src_rect.set(trimX,trimY+0,16,16);
@sprites["corner1"].src_rect.set(trimX+48,trimY+0,16,16);
@sprites["corner2"].src_rect.set(trimX,trimY+48,16,16);
@sprites["corner3"].src_rect.set(trimX+48,trimY+48,16,16);
@sprites["scroll0"].src_rect.set(trimX+24, trimY+16, 16, 8) # up
@sprites["scroll3"].src_rect.set(trimX+24, trimY+40, 16, 8) # down
@sprites["scroll1"].src_rect.set(trimX+16, trimY+24, 8, 16) # left
@sprites["scroll2"].src_rect.set(trimX+40, trimY+24, 8, 16) # right
cursorX=trimX
cursorY=trimY+64
sideRects=[
Rect.new(trimX+16,trimY+0,32,16),
Rect.new(trimX,trimY+16,16,32),
Rect.new(trimX+48,trimY+16,16,32),
Rect.new(trimX+16,trimY+48,32,16)
]
if @width>32 && @height>32
@sprites["contents"].src_rect.set(@ox,@oy,@width-32,@height-32)
else
@sprites["contents"].src_rect.set(0,0,0,0)
end
pauseRects=[
trimX+32,trimY+64,
trimX+48,trimY+64,
trimX+32,trimY+80,
trimX+48,trimY+80,
]
pauseWidth=16
pauseHeight=16
@sprites["pause"].src_rect.set(
pauseRects[@pauseframe*2],
pauseRects[@pauseframe*2+1],
pauseWidth,pauseHeight
)
@sprites["pause"].x=@x+(@width/2)-(pauseWidth/2)
@sprites["pause"].y=@y+@height-16 # 16 refers to skin margin
@sprites["contents"].x=@x+16
@sprites["contents"].y=@y+16
@sprites["corner0"].x=@x
@sprites["corner0"].y=@y
@sprites["corner1"].x=@x+@width-16
@sprites["corner1"].y=@y
@sprites["corner2"].x=@x
@sprites["corner2"].y=@y+@height-16
@sprites["corner3"].x=@x+@width-16
@sprites["corner3"].y=@y+@height-16
@sprites["side0"].x=@x+16
@sprites["side0"].y=@y
@sprites["side1"].x=@x
@sprites["side1"].y=@y+16
@sprites["side2"].x=@x+@width-16
@sprites["side2"].y=@y+16
@sprites["side3"].x=@x+16
@sprites["side3"].y=@y+@height-16
@sprites["scroll0"].x = @x+@width / 2 - 8
@sprites["scroll0"].y = @y+8
@sprites["scroll1"].x = @x+8
@sprites["scroll1"].y = @y+@height / 2 - 8
@sprites["scroll2"].x = @x+@width - 16
@sprites["scroll2"].y = @y+@height / 2 - 8
@sprites["scroll3"].x = @x+@width / 2 - 8
@sprites["scroll3"].y = @y+@height - 16
@sprites["back"].x=@x+2
@sprites["back"].y=@y+2
@sprites["cursor"].x=@x+16+@cursor_rect.x
@sprites["cursor"].y=@y+16+@cursor_rect.y
if changeBitmap && @_windowskin && !@_windowskin.disposed?
width=@cursor_rect.width
height=@cursor_rect.height
if width > 0 && height > 0
cursorrects=[
# sides
Rect.new(cursorX+2, cursorY+0, 28, 2),
Rect.new(cursorX+0, cursorY+2, 2, 28),
Rect.new(cursorX+30, cursorY+2, 2, 28),
Rect.new(cursorX+2, cursorY+30, 28, 2),
# corners
Rect.new(cursorX+0, cursorY+0, 2, 2),
Rect.new(cursorX+30, cursorY+0, 2, 2),
Rect.new(cursorX+0, cursorY+30, 2, 2),
Rect.new(cursorX+30, cursorY+30, 2, 2),
# back
Rect.new(cursorX+2, cursorY+2, 28, 28)
]
margin=2
fullmargin=4
@cursorbitmap = ensureBitmap(@cursorbitmap, width, height)
@cursorbitmap.clear
@sprites["cursor"].bitmap=@cursorbitmap
@sprites["cursor"].src_rect.set(0,0,width,height)
rect = Rect.new(margin,margin,
width - fullmargin, height - fullmargin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[8])
@cursorbitmap.blt(0, 0, @_windowskin, cursorrects[4])# top left
@cursorbitmap.blt(width-margin, 0, @_windowskin, cursorrects[5]) # top right
@cursorbitmap.blt(0, height-margin, @_windowskin, cursorrects[6]) # bottom right
@cursorbitmap.blt(width-margin, height-margin, @_windowskin, cursorrects[7]) # bottom left
rect = Rect.new(margin, 0,
width - fullmargin, margin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[0])
rect = Rect.new(0, margin,
margin, height - fullmargin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[1])
rect = Rect.new(width - margin, margin,
margin, height - fullmargin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[2])
rect = Rect.new(margin, height-margin,
width - fullmargin, margin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[3])
else
@sprites["cursor"].visible=false
@sprites["cursor"].src_rect.set(0,0,0,0)
end
for i in 0...4
dwidth = (i==0 || i==3) ? @width-32 : 16
dheight = (i==0 || i==3) ? 16 : @height-32
@sidebitmaps[i]=ensureBitmap(@sidebitmaps[i],dwidth,dheight)
@sprites["side#{i}"].bitmap=@sidebitmaps[i]
@sprites["side#{i}"].src_rect.set(0,0,dwidth,dheight)
@sidebitmaps[i].clear
if sideRects[i].width>0 && sideRects[i].height>0
@sidebitmaps[i].stretch_blt(@sprites["side#{i}"].src_rect,
@_windowskin,sideRects[i])
end
end
backwidth=@width-4
backheight=@height-4
if backwidth>0 && backheight>0
@backbitmap=ensureBitmap(@backbitmap,backwidth,backheight)
@sprites["back"].bitmap=@backbitmap
@sprites["back"].src_rect.set(0,0,backwidth,backheight)
@backbitmap.clear
if @stretch
@backbitmap.stretch_blt(@sprites["back"].src_rect,@_windowskin,backRect)
else
tileBitmap(@backbitmap,@sprites["back"].src_rect,@_windowskin,backRect)
end
if blindsRect
tileBitmap(@backbitmap,@sprites["back"].src_rect,@_windowskin,blindsRect)
end
else
@sprites["back"].visible=false
@sprites["back"].src_rect.set(0,0,0,0)
end
end
if @openness!=255
opn=@openness/255.0
for k in @spritekeys
sprite=@sprites[k]
ratio=(@height<=0) ? 0 : (sprite.y-@y)*1.0/@height
sprite.zoom_y=opn
sprite.oy=0
sprite.y=(@y+(@height/2.0)+(@height*ratio*opn)-(@height/2*opn)).floor
end
else
for k in @spritekeys
sprite=@sprites[k]
sprite.zoom_y=1.0
end
end
i=0
# Ensure Z order
for k in @spritekeys
sprite=@sprites[k]
y=sprite.y
sprite.y=i
sprite.oy=(sprite.zoom_y<=0) ? 0 : (i-y)/sprite.zoom_y
end
end
end

View File

@@ -0,0 +1,781 @@
module MessageConfig
FontName = "Power Green"
# in Graphics/Windowskins/ (specify empty string to use the default windowskin)
TextSkinName = "speech hgss 1"
ChoiceSkinName = "choice 1"
WindowOpacity = 255
TextSpeed = nil # can be positive to wait frames or negative to
# show multiple characters in a single frame
LIGHTTEXTBASE = Color.new(248,248,248)
LIGHTTEXTSHADOW = Color.new(72,80,88)
DARKTEXTBASE = Color.new(80,80,88)
DARKTEXTSHADOW = Color.new(160,160,168)
# 0 = Pause cursor is displayed at end of text
# 1 = Pause cursor is displayed at bottom right
# 2 = Pause cursor is displayed at lower middle side
CURSORMODE = 1
FontSubstitutes = {
"Power Red and Blue" => "Pokemon RS",
"Power Red and Green" => "Pokemon FireLeaf",
"Power Green" => "Pokemon Emerald",
"Power Green Narrow" => "Pokemon Emerald Narrow",
"Power Green Small" => "Pokemon Emerald Small",
"Power Clear" => "Pokemon DP"
}
@@systemFrame = nil
@@defaultTextSkin = nil
@@systemFont = nil
@@textSpeed = nil
def self.pbTryFonts(*args)
for a in args
if a && a.is_a?(String)
return a if Font.exist?(a)
a=MessageConfig::FontSubstitutes[a] || a
return a if Font.exist?(a)
elsif a && a.is_a?(Array)
for aa in a
ret=MessageConfig.pbTryFonts(aa)
return ret if ret!=""
end
end
end
return ""
end
def self.pbDefaultSystemFrame
return "" if !MessageConfig::ChoiceSkinName
return pbResolveBitmap("Graphics/Windowskins/"+MessageConfig::ChoiceSkinName) || ""
end
def self.pbDefaultSpeechFrame
return "" if !MessageConfig::TextSkinName
return pbResolveBitmap("Graphics/Windowskins/"+MessageConfig::TextSkinName) || ""
end
def self.pbDefaultSystemFontName
return MessageConfig.pbTryFonts(MessageConfig::FontName,"Arial Narrow","Arial")
end
def self.pbDefaultTextSpeed
return (TextSpeed) ? TextSpeed : (Graphics.width>400) ? -2 : 1
end
def self.pbDefaultWindowskin
skin=load_data("Data/System.rxdata").windowskin_name rescue nil
if skin && skin!=""
skin=pbResolveBitmap("Graphics/Windowskins/"+skin) || ""
end
skin=pbResolveBitmap("Graphics/System/Window") if !skin || skin==""
skin=pbResolveBitmap("Graphics/Windowskins/001-Blue01") if !skin || skin==""
return skin || ""
end
def self.pbGetSystemFrame
if !@@systemFrame
skin=MessageConfig.pbDefaultSystemFrame
skin=MessageConfig.pbDefaultWindowskin if !skin || skin==""
@@systemFrame=skin || ""
end
return @@systemFrame
end
def self.pbGetSpeechFrame
if !@@defaultTextSkin
skin=MessageConfig.pbDefaultSpeechFrame
skin=MessageConfig.pbDefaultWindowskin if !skin || skin==""
@@defaultTextSkin=skin || ""
end
return @@defaultTextSkin
end
def self.pbGetSystemFontName
@@systemFont=pbDefaultSystemFontName if !@@systemFont
return @@systemFont
end
def self.pbGetTextSpeed
@@textSpeed=pbDefaultTextSpeed if !@@textSpeed
return @@textSpeed
end
def self.pbSetSystemFrame(value)
@@systemFrame=pbResolveBitmap(value) || ""
end
def self.pbSetSpeechFrame(value)
@@defaultTextSkin=pbResolveBitmap(value) || ""
end
def self.pbSetSystemFontName(value)
@@systemFont=MessageConfig.pbTryFonts(value,"Arial Narrow","Arial")
end
def self.pbSetTextSpeed(value)
@@textSpeed=value
end
end
#===============================================================================
# Position a window
#===============================================================================
def pbBottomRight(window)
window.x=Graphics.width-window.width
window.y=Graphics.height-window.height
end
def pbBottomLeft(window)
window.x=0
window.y=Graphics.height-window.height
end
def pbBottomLeftLines(window,lines,width=nil)
window.x=0
window.width=width ? width : Graphics.width
window.height=(window.borderY rescue 32)+lines*32
window.y=Graphics.height-window.height
end
def pbPositionFaceWindow(facewindow,msgwindow)
return if !facewindow
if msgwindow
if facewindow.height<=msgwindow.height
facewindow.y=msgwindow.y
else
facewindow.y=msgwindow.y+msgwindow.height-facewindow.height
end
facewindow.x=Graphics.width-facewindow.width
msgwindow.x=0
msgwindow.width=Graphics.width-facewindow.width
else
facewindow.height=Graphics.height if facewindow.height>Graphics.height
facewindow.x=0
facewindow.y=0
end
end
def pbPositionNearMsgWindow(cmdwindow,msgwindow,side)
return if !cmdwindow
if msgwindow
height=[cmdwindow.height,Graphics.height-msgwindow.height].min
if cmdwindow.height!=height
cmdwindow.height=height
end
cmdwindow.y=msgwindow.y-cmdwindow.height
if cmdwindow.y<0
cmdwindow.y=msgwindow.y+msgwindow.height
if cmdwindow.y+cmdwindow.height>Graphics.height
cmdwindow.y=msgwindow.y-cmdwindow.height
end
end
case side
when :left
cmdwindow.x=msgwindow.x
when :right
cmdwindow.x=msgwindow.x+msgwindow.width-cmdwindow.width
else
cmdwindow.x=msgwindow.x+msgwindow.width-cmdwindow.width
end
else
cmdwindow.height=Graphics.height if cmdwindow.height>Graphics.height
cmdwindow.x=0
cmdwindow.y=0
end
end
# internal function
def pbRepositionMessageWindow(msgwindow, linecount=2)
msgwindow.height=32*linecount+msgwindow.borderY
msgwindow.y=(Graphics.height)-(msgwindow.height)
if $game_system && $game_system.respond_to?("message_position")
case $game_system.message_position
when 0 # up
msgwindow.y=0
when 1 # middle
msgwindow.y=(Graphics.height/2)-(msgwindow.height/2)
when 2
msgwindow.y=(Graphics.height)-(msgwindow.height)
end
end
if $game_system && $game_system.respond_to?("message_frame")
if $game_system.message_frame != 0
msgwindow.opacity = 0
end
end
if $game_message
case $game_message.background
when 1; msgwindow.opacity=0 # dim
when 2; msgwindow.opacity=0 # transparent
end
end
end
# internal function
def pbUpdateMsgWindowPos(msgwindow,event,eventChanged=false)
if event
if eventChanged
msgwindow.resizeToFit2(msgwindow.text,Graphics.width*2/3,msgwindow.height)
end
msgwindow.y=event.screen_y-48-msgwindow.height
if msgwindow.y<0
msgwindow.y=event.screen_y+24
end
msgwindow.x=event.screen_x-(msgwindow.width/2)
msgwindow.x=0 if msgwindow.x<0
if msgwindow.x>Graphics.width-msgwindow.width
msgwindow.x=Graphics.width-msgwindow.width
end
else
curwidth=msgwindow.width
if curwidth!=Graphics.width
msgwindow.width=Graphics.width
msgwindow.width=Graphics.width
end
end
end
#===============================================================================
# Determine the colour of a background
#===============================================================================
def isDarkBackground(background,rect=nil)
return true if !background || background.disposed?
rect = background.rect if !rect
return true if rect.width<=0 || rect.height<=0
xSeg = (rect.width/16)
xLoop = (xSeg==0) ? 1 : 16
xStart = (xSeg==0) ? rect.x+(rect.width/2) : rect.x+xSeg/2
ySeg = (rect.height/16)
yLoop = (ySeg==0) ? 1 : 16
yStart = (ySeg==0) ? rect.y+(rect.height/2) : rect.y+ySeg/2
count = 0
y = yStart
r = 0; g = 0; b = 0
yLoop.times do
x = xStart
xLoop.times do
clr = background.get_pixel(x,y)
if clr.alpha!=0
r += clr.red
g += clr.green
b += clr.blue
count += 1
end
x += xSeg
end
y += ySeg
end
return true if count==0
r /= count
g /= count
b /= count
return (r*0.299+g*0.587+b*0.114)<160
end
def isDarkWindowskin(windowskin)
return true if !windowskin || windowskin.disposed?
if windowskin.width==192 && windowskin.height==128
return isDarkBackground(windowskin,Rect.new(0,0,128,128))
elsif windowskin.width==128 && windowskin.height==128
return isDarkBackground(windowskin,Rect.new(0,0,64,64))
elsif windowskin.width==96 && windowskin.height==48
return isDarkBackground(windowskin,Rect.new(32,16,16,16))
else
clr = windowskin.get_pixel(windowskin.width/2, windowskin.height/2)
return (clr.red*0.299+clr.green*0.587+clr.blue*0.114)<160
end
end
#===============================================================================
# Determine which text colours to use based on the darkness of the background
#===============================================================================
def getSkinColor(windowskin,color,isDarkSkin)
if !windowskin || windowskin.disposed? ||
windowskin.width!=128 || windowskin.height!=128
# Base color, shadow color (these are reversed on dark windowskins)
textcolors = [
"0070F8","78B8E8", # 1 Blue
"E82010","F8A8B8", # 2 Red
"60B048","B0D090", # 3 Green
"48D8D8","A8E0E0", # 4 Cyan
"D038B8","E8A0E0", # 5 Magenta
"E8D020","F8E888", # 6 Yellow
"A0A0A8","D0D0D8", # 7 Grey
"F0F0F8","C8C8D0", # 8 White
"9040E8","B8A8E0", # 9 Purple
"F89818","F8C898", # 10 Orange
colorToRgb32(MessageConfig::DARKTEXTBASE),
colorToRgb32(MessageConfig::DARKTEXTSHADOW), # 11 Dark default
colorToRgb32(MessageConfig::LIGHTTEXTBASE),
colorToRgb32(MessageConfig::LIGHTTEXTSHADOW) # 12 Light default
]
if color==0 || color>textcolors.length/2 # No special colour, use default
if isDarkSkin # Dark background, light text
return shadowc3tag(MessageConfig::LIGHTTEXTBASE,MessageConfig::LIGHTTEXTSHADOW)
end
# Light background, dark text
return shadowc3tag(MessageConfig::DARKTEXTBASE,MessageConfig::DARKTEXTSHADOW)
end
# Special colour as listed above
if isDarkSkin && color!=12 # Dark background, light text
return sprintf("<c3=%s,%s>",textcolors[2*(color-1)+1],textcolors[2*(color-1)])
end
# Light background, dark text
return sprintf("<c3=%s,%s>",textcolors[2*(color-1)],textcolors[2*(color-1)+1])
else # VX windowskin
color = 0 if color>=32
x = 64 + (color % 8) * 8
y = 96 + (color / 8) * 8
pixel = windowskin.get_pixel(x, y)
return shadowctagFromColor(pixel)
end
end
def getDefaultTextColors(windowskin)
if !windowskin || windowskin.disposed? ||
windowskin.width!=128 || windowskin.height!=128
if isDarkWindowskin(windowskin)
return [MessageConfig::LIGHTTEXTBASE,MessageConfig::LIGHTTEXTSHADOW] # White
else
return [MessageConfig::DARKTEXTBASE,MessageConfig::DARKTEXTSHADOW] # Dark gray
end
else # VX windowskin
color = windowskin.get_pixel(64, 96)
shadow = nil
isDark = (color.red+color.green+color.blue)/3 < 128
if isDark
shadow = Color.new(color.red+64,color.green+64,color.blue+64)
else
shadow = Color.new(color.red-64,color.green-64,color.blue-64)
end
return [color,shadow]
end
end
#===============================================================================
# Makes sure a bitmap exists
#===============================================================================
def pbDoEnsureBitmap(bitmap,dwidth,dheight)
if !bitmap || bitmap.disposed? || bitmap.width<dwidth || bitmap.height<dheight
oldfont = (bitmap && !bitmap.disposed?) ? bitmap.font : nil
bitmap.dispose if bitmap
bitmap = Bitmap.new([1,dwidth].max,[1,dheight].max)
(oldfont) ? bitmap.font = oldfont : pbSetSystemFont(bitmap)
bitmap.font.shadow = false if bitmap.font && bitmap.font.respond_to?("shadow")
end
return bitmap
end
#===============================================================================
# Set a bitmap's font
#===============================================================================
# Gets the name of the system small font.
def pbSmallFontName
return MessageConfig.pbTryFonts("Power Green Small","Pokemon Emerald Small",
"Arial Narrow","Arial")
end
# Gets the name of the system narrow font.
def pbNarrowFontName
return MessageConfig.pbTryFonts("Power Green Narrow","Pokemon Emerald Narrow",
"Arial Narrow","Arial")
end
# Sets a bitmap's font to the system font.
def pbSetSystemFont(bitmap)
fontname=MessageConfig.pbGetSystemFontName
bitmap.font.name=fontname
if fontname=="Pokemon FireLeaf" || fontname=="Power Red and Green"
bitmap.font.size=29
elsif fontname=="Pokemon Emerald Small" || fontname=="Power Green Small"
bitmap.font.size=25
else
bitmap.font.size=31
end
end
# Sets a bitmap's font to the system small font.
def pbSetSmallFont(bitmap)
bitmap.font.name=pbSmallFontName
bitmap.font.size=25
end
# Sets a bitmap's font to the system narrow font.
def pbSetNarrowFont(bitmap)
bitmap.font.name=pbNarrowFontName
bitmap.font.size=31
end
#===============================================================================
# Blend colours, set the colour of all bitmaps in a sprite hash
#===============================================================================
def pbAlphaBlend(dstColor,srcColor)
r=(255*(srcColor.red-dstColor.red)/255)+dstColor.red
g=(255*(srcColor.green-dstColor.green)/255)+dstColor.green
b=(255*(srcColor.blue-dstColor.blue)/255)+dstColor.blue
a=(255*(srcColor.alpha-dstColor.alpha)/255)+dstColor.alpha
return Color.new(r,g,b,a)
end
def pbSrcOver(dstColor,srcColor)
er=srcColor.red*srcColor.alpha/255
eg=srcColor.green*srcColor.alpha/255
eb=srcColor.blue*srcColor.alpha/255
iea=255-srcColor.alpha
cr=dstColor.red*dstColor.alpha/255
cg=dstColor.green*dstColor.alpha/255
cb=dstColor.blue*dstColor.alpha/255
ica=255-dstColor.alpha
a=255-(iea*ica)/255
r=(iea*cr)/255+er
g=(iea*cg)/255+eg
b=(iea*cb)/255+eb
r=(a==0) ? 0 : r*255/a
g=(a==0) ? 0 : g*255/a
b=(a==0) ? 0 : b*255/a
return Color.new(r,g,b,a)
end
def pbSetSpritesToColor(sprites,color)
return if !sprites || !color
colors={}
for i in sprites
next if !i[1] || pbDisposed?(i[1])
colors[i[0]]=i[1].color.clone
i[1].color=pbSrcOver(i[1].color,color)
end
Graphics.update
Input.update
for i in colors
next if !sprites[i[0]]
sprites[i[0]].color=i[1]
end
end
#===============================================================================
# Update and dispose sprite hashes
#===============================================================================
def using(window)
begin
yield if block_given?
ensure
window.dispose
end
end
def pbUpdateSpriteHash(windows)
for i in windows
window=i[1]
if window
if window.is_a?(Sprite) || window.is_a?(Window)
window.update if !pbDisposed?(window)
elsif window.is_a?(Plane)
begin
window.update if !window.disposed?
rescue NoMethodError
end
elsif window.respond_to?("update")
begin
window.update
rescue RGSSError
end
end
end
end
end
# Disposes all objects in the specified hash.
def pbDisposeSpriteHash(sprites)
return if !sprites
for i in sprites.keys
pbDisposeSprite(sprites,i)
end
sprites.clear
end
# Disposes the specified graphics object within the specified hash. Basically
# like: sprites[id].dispose
def pbDisposeSprite(sprites,id)
sprite = sprites[id]
sprite.dispose if sprite && !pbDisposed?(sprite)
sprites[id] = nil
end
def pbDisposed?(x)
return true if !x
return x.disposed? if !x.is_a?(Viewport)
begin
x.rect = x.rect
rescue
return true
end
return false
end
#===============================================================================
# Fades and window activations for sprite hashes
#===============================================================================
class Game_Temp
attr_accessor :fadestate
def fadestate
return (@fadestate) ? @fadestate : 0
end
end
def pbPushFade
$game_temp.fadestate = [$game_temp.fadestate+1,0].max if $game_temp
end
def pbPopFade
$game_temp.fadestate = [$game_temp.fadestate-1,0].max if $game_temp
end
def pbIsFaded?
return ($game_temp) ? $game_temp.fadestate>0 : false
end
# pbFadeOutIn(z) { block }
# Fades out the screen before a block is run and fades it back in after the
# block exits. z indicates the z-coordinate of the viewport used for this effect
def pbFadeOutIn(z=99999,nofadeout=false)
col=Color.new(0,0,0,0)
viewport=Viewport.new(0,0,Graphics.width,Graphics.height)
viewport.z=z
numFrames = (Graphics.frame_rate*0.4).floor
alphaDiff = (255.0/numFrames).ceil
for j in 0..numFrames
col.set(0,0,0,j*alphaDiff)
viewport.color=col
Graphics.update
Input.update
end
pbPushFade
begin
yield if block_given?
ensure
pbPopFade
if !nofadeout
for j in 0..numFrames
col.set(0,0,0,(numFrames-j)*alphaDiff)
viewport.color=col
Graphics.update
Input.update
end
end
viewport.dispose
end
end
def pbFadeOutInWithUpdate(z,sprites,nofadeout=false)
col=Color.new(0,0,0,0)
viewport=Viewport.new(0,0,Graphics.width,Graphics.height)
viewport.z=z
numFrames = (Graphics.frame_rate*0.4).floor
alphaDiff = (255.0/numFrames).ceil
for j in 0..numFrames
col.set(0,0,0,j*alphaDiff)
viewport.color=col
pbUpdateSpriteHash(sprites)
Graphics.update
Input.update
end
pbPushFade
begin
yield if block_given?
ensure
pbPopFade
if !nofadeout
for j in 0..numFrames
col.set(0,0,0,(numFrames-j)*alphaDiff)
viewport.color=col
pbUpdateSpriteHash(sprites)
Graphics.update
Input.update
end
end
viewport.dispose
end
end
def pbFadeOutAndHide(sprites)
visiblesprites = {}
numFrames = (Graphics.frame_rate*0.4).floor
alphaDiff = (255.0/numFrames).ceil
pbDeactivateWindows(sprites) {
for j in 0..numFrames
pbSetSpritesToColor(sprites,Color.new(0,0,0,j*alphaDiff))
(block_given?) ? yield : pbUpdateSpriteHash(sprites)
end
}
for i in sprites
next if !i[1]
next if pbDisposed?(i[1])
visiblesprites[i[0]] = true if i[1].visible
i[1].visible = false
end
return visiblesprites
end
def pbFadeInAndShow(sprites,visiblesprites=nil)
if visiblesprites
for i in visiblesprites
if i[1] && sprites[i[0]] && !pbDisposed?(sprites[i[0]])
sprites[i[0]].visible = true
end
end
end
numFrames = (Graphics.frame_rate*0.4).floor
alphaDiff = (255.0/numFrames).ceil
pbDeactivateWindows(sprites) {
for j in 0..numFrames
pbSetSpritesToColor(sprites,Color.new(0,0,0,((numFrames-j)*alphaDiff)))
(block_given?) ? yield : pbUpdateSpriteHash(sprites)
end
}
end
# Restores which windows are active for the given sprite hash.
# _activeStatuses_ is the result of a previous call to pbActivateWindows
def pbRestoreActivations(sprites,activeStatuses)
return if !sprites || !activeStatuses
for k in activeStatuses.keys
if sprites[k] && sprites[k].is_a?(Window) && !pbDisposed?(sprites[k])
sprites[k].active=activeStatuses[k] ? true : false
end
end
end
# Deactivates all windows. If a code block is given, deactivates all windows,
# runs the code in the block, and reactivates them.
def pbDeactivateWindows(sprites)
if block_given?
pbActivateWindow(sprites,nil) { yield }
else
pbActivateWindow(sprites,nil)
end
end
# Activates a specific window of a sprite hash. _key_ is the key of the window
# in the sprite hash. If a code block is given, deactivates all windows except
# the specified window, runs the code in the block, and reactivates them.
def pbActivateWindow(sprites,key)
return if !sprites
activeStatuses={}
for i in sprites
if i[1] && i[1].is_a?(Window) && !pbDisposed?(i[1])
activeStatuses[i[0]]=i[1].active
i[1].active=(i[0]==key)
end
end
if block_given?
begin
yield
ensure
pbRestoreActivations(sprites,activeStatuses)
end
return {}
else
return activeStatuses
end
end
#===============================================================================
# Create background planes for a sprite hash
#===============================================================================
# Adds a background to the sprite hash.
# _planename_ is the hash key of the background.
# _background_ is a filename within the Graphics/Pictures/ folder and can be
# an animated image.
# _viewport_ is a viewport to place the background in.
def addBackgroundPlane(sprites,planename,background,viewport=nil)
sprites[planename]=AnimatedPlane.new(viewport)
bitmapName=pbResolveBitmap("Graphics/Pictures/#{background}")
if bitmapName==nil
# Plane should exist in any case
sprites[planename].bitmap=nil
sprites[planename].visible=false
else
sprites[planename].setBitmap(bitmapName)
for spr in sprites.values
if spr.is_a?(Window)
spr.windowskin=nil
end
end
end
end
# Adds a background to the sprite hash.
# _planename_ is the hash key of the background.
# _background_ is a filename within the Graphics/Pictures/ folder and can be
# an animated image.
# _color_ is the color to use if the background can't be found.
# _viewport_ is a viewport to place the background in.
def addBackgroundOrColoredPlane(sprites,planename,background,color,viewport=nil)
bitmapName=pbResolveBitmap("Graphics/Pictures/#{background}")
if bitmapName==nil
# Plane should exist in any case
sprites[planename]=ColoredPlane.new(color,@viewport)
else
sprites[planename]=AnimatedPlane.new(viewport)
sprites[planename].setBitmap(bitmapName)
for spr in sprites.values
if spr.is_a?(Window)
spr.windowskin=nil
end
end
end
end
#===============================================================================
# Ensure required method definitions
#===============================================================================
module Graphics
if !self.respond_to?("width")
def self.width; return 640; end
end
if !self.respond_to?("height")
def self.height; return 480; end
end
end
if !defined?(_INTL)
def _INTL(*args)
string=args[0].clone
for i in 1...args.length
string.gsub!(/\{#{i}\}/,"#{args[i]}")
end
return string
end
end
if !defined?(_ISPRINTF)
def _ISPRINTF(*args)
string=args[0].clone
for i in 1...args.length
string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m|
next sprintf("%"+$1,args[i])
}
end
return string
end
end
if !defined?(_MAPINTL)
def _MAPINTL(*args)
string=args[1].clone
for i in 2...args.length
string.gsub!(/\{#{i}\}/,"#{args[i+1]}")
end
return string
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,709 @@
def getCubicPoint2(src,t)
x0 = src[0]; y0 = src[1]
cx0 = src[2]; cy0 = src[3]
cx1 = src[4]; cy1 = src[5]
x1 = src[6]; y1 = src[7]
x1 = cx1+(x1-cx1)*t
x0 = x0+(cx0-x0)*t
cx0 = cx0+(cx1-cx0)*t
cx1 = cx0+(x1-cx0)*t
cx0 = x0+(cx0-x0)*t
cx = cx0+(cx1-cx0)*t
# a = x1 - 3 * cx1 + 3 * cx0 - x0
# b = 3 * (cx1 - 2 * cx0 + x0)
# c = 3 * (cx0 - x0)
# d = x0
# cx = a*t*t*t + b*t*t + c*t + d
y1 = cy1+(y1-cy1)*t
y0 = y0+(cy0-y0)*t
cy0 = cy0+(cy1-cy0)*t
cy1 = cy0+(y1-cy0)*t
cy0 = y0+(cy0-y0)*t
cy = cy0+(cy1-cy0)*t
# a = y1 - 3 * cy1 + 3 * cy0 - y0
# b = 3 * (cy1 - 2 * cy0 + y0)
# c = 3 * (cy0 - y0)
# d = y0
# cy = a*t*t*t + b*t*t + c*t + d
return [cx,cy]
end
class Processes
XY = 0
DeltaXY = 1
Z = 2
Curve = 3
Zoom = 4
Angle = 5
Tone = 6
Color = 7
Hue = 8
Opacity = 9
Visible = 10
BlendType = 11
SE = 12
Name = 13
Origin = 14
Src = 15
SrcSize = 16
CropBottom = 17
end
class PictureEx
attr_accessor :x # x-coordinate
attr_accessor :y # y-coordinate
attr_accessor :z # z value
attr_accessor :zoom_x # x directional zoom rate
attr_accessor :zoom_y # y directional zoom rate
attr_accessor :angle # rotation angle
attr_accessor :tone # tone
attr_accessor :color # color
attr_accessor :hue # filename hue
attr_accessor :opacity # opacity level
attr_accessor :visible # visibility boolean
attr_accessor :blend_type # blend method
attr_accessor :name # file name
attr_accessor :origin # starting point
attr_reader :src_rect # source rect
attr_reader :cropBottom # crops sprite to above this y-coordinate
attr_reader :frameUpdates # Array of processes updated in a frame
def initialize(z)
# process: [type, delay, total_duration, frame_counter, cb, etc.]
@processes = []
@x = 0.0
@y = 0.0
@z = z
@zoom_x = 100.0
@zoom_y = 100.0
@angle = 0
@rotate_speed = 0
@tone = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@color = Color.new(0, 0, 0, 0)
@hue = 0
@opacity = 255.0
@visible = true
@blend_type = 0
@name = ""
@origin = PictureOrigin::TopLeft
@src_rect = Rect.new(0,0,-1,-1)
@cropBottom = -1
@frameUpdates = []
end
def callback(cb)
if cb.is_a?(Proc); proc.call(self)
elsif cb.is_a?(Array); cb[0].method(cb[1]).call(self)
elsif cb.is_a?(Method); cb.call(self)
end
end
def setCallback(delay, cb=nil)
delay = ensureDelayAndDuration(delay)
@processes.push([nil,delay,0,0,cb])
end
def running?
return @processes.length>0
end
def totalDuration
ret = 0
for process in @processes
dur = process[1]+process[2]
ret = dur if dur>ret
end
ret *= 20.0/Graphics.frame_rate
return ret.to_i
end
def ensureDelayAndDuration(delay, duration=nil)
delay = self.totalDuration if delay<0
delay *= Graphics.frame_rate/20.0
if !duration.nil?
duration *= Graphics.frame_rate/20.0
return delay.to_i, duration.to_i
end
return delay.to_i
end
def ensureDelay(delay)
return ensureDelayAndDuration(delay)
end
# speed is the angle to change by in 1/20 of a second. @rotate_speed is the
# angle to change by per frame.
# NOTE: This is not compatible with manually changing the angle at a certain
# point. If you make a sprite auto-rotate, you should not try to alter
# the angle another way too.
def rotate(speed)
@rotate_speed = speed*20.0/Graphics.frame_rate
while @rotate_speed<0; @rotate_speed += 360; end
@rotate_speed %= 360
end
def erase
self.name = ""
end
def clearProcesses
@processes = []
end
def adjustPosition(xOffset, yOffset)
for process in @processes
next if process[0]!=Processes::XY
process[5] += xOffset
process[6] += yOffset
process[7] += xOffset
process[8] += yOffset
end
end
def move(delay, duration, origin, x, y, zoom_x=100.0, zoom_y=100.0, opacity=255)
setOrigin(delay,duration,origin)
moveXY(delay,duration,x,y)
moveZoomXY(delay,duration,zoom_x,zoom_y)
moveOpacity(delay,duration,opacity)
end
def moveXY(delay, duration, x, y, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::XY,delay,duration,0,cb,@x,@y,x,y])
end
def setXY(delay, x, y, cb=nil)
moveXY(delay,0,x,y,cb)
end
def moveCurve(delay, duration, x1, y1, x2, y2, x3, y3, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Curve,delay,duration,0,cb,[@x,@y,x1,y1,x2,y2,x3,y3]])
end
def moveDelta(delay, duration, x, y, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::DeltaXY,delay,duration,0,cb,@x,@y,x,y])
end
def setDelta(delay, x, y, cb=nil)
moveDelta(delay,0,x,y,cb)
end
def moveZ(delay, duration, z, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Z,delay,duration,0,cb,@z,z])
end
def setZ(delay, z, cb=nil)
moveZ(delay,0,z,cb)
end
def moveZoomXY(delay, duration, zoom_x, zoom_y, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Zoom,delay,duration,0,cb,@zoom_x,@zoom_y,zoom_x,zoom_y])
end
def setZoomXY(delay, zoom_x, zoom_y, cb=nil)
moveZoomXY(delay,0,zoom_x,zoom_y,cb)
end
def moveZoom(delay, duration, zoom, cb=nil)
moveZoomXY(delay,duration,zoom,zoom,cb)
end
def setZoom(delay, zoom, cb=nil)
moveZoomXY(delay,0,zoom,zoom,cb)
end
def moveAngle(delay, duration, angle, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Angle,delay,duration,0,cb,@angle,angle])
end
def setAngle(delay, angle, cb=nil)
moveAngle(delay,0,angle,cb)
end
def moveTone(delay, duration, tone, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
target = (tone) ? tone.clone : Tone.new(0,0,0,0)
@processes.push([Processes::Tone,delay,duration,0,cb,@tone.clone,target])
end
def setTone(delay, tone, cb=nil)
moveTone(delay,0,tone,cb)
end
def moveColor(delay, duration, color, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
target = (color) ? color.clone : Color.new(0,0,0,0)
@processes.push([Processes::Color,delay,duration,0,cb,@color.clone,target])
end
def setColor(delay, color, cb=nil)
moveColor(delay,0,color,cb)
end
# Hue changes don't actually work.
def moveHue(delay, duration, hue, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Hue,delay,duration,0,cb,@hue,hue])
end
# Hue changes don't actually work.
def setHue(delay, hue, cb=nil)
moveHue(delay,0,hue,cb)
end
def moveOpacity(delay, duration, opacity, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Opacity,delay,duration,0,cb,@opacity,opacity])
end
def setOpacity(delay, opacity, cb=nil)
moveOpacity(delay,0,opacity,cb)
end
def setVisible(delay, visible, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::Visible,delay,0,0,cb,visible])
end
# Only values of 0 (normal), 1 (additive) and 2 (subtractive) are allowed.
def setBlendType(delay, blend, cb=nil)
delay = ensureDelayAndDuration(delay)
@processes.push([Processes::BlendType,delay,0,0,cb,blend])
end
def setSE(delay, seFile, volume=nil, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::SE,delay,0,0,cb,seFile,volume])
end
def setName(delay, name, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::Name,delay,0,0,cb,name])
end
def setOrigin(delay, origin, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::Origin,delay,0,0,cb,origin])
end
def setSrc(delay, srcX, srcY, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::Src,delay,0,0,cb,srcX,srcY])
end
def setSrcSize(delay, srcWidth, srcHeight, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::SrcSize,delay,0,0,cb,srcWidth,srcHeight])
end
# Used to cut Pokémon sprites off when they faint and sink into the ground.
def setCropBottom(delay, y, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::CropBottom,delay,0,0,cb,y])
end
def update
procEnded = false
@frameUpdates.clear
for i in 0...@processes.length
process = @processes[i]
# Decrease delay of processes that are scheduled to start later
if process[1]>=0
# Set initial values if the process will start this frame
if process[1]==0
case process[0]
when Processes::XY
process[5] = @x
process[6] = @y
when Processes::DeltaXY
process[5] = @x
process[6] = @y
process[7] += @x
process[8] += @y
when Processes::Curve
process[5][0] = @x
process[5][1] = @y
when Processes::Z
process[5] = @z
when Processes::Zoom
process[5] = @zoom_x
process[6] = @zoom_y
when Processes::Angle
process[5] = @angle
when Processes::Tone
process[5] = @tone.clone
when Processes::Color
process[5] = @color.clone
when Processes::Hue
process[5] = @hue
when Processes::Opacity
process[5] = @opacity
end
end
# Decrease delay counter
process[1] -= 1
# Process hasn't started yet, skip to the next one
next if process[1]>=0
end
# Update process
@frameUpdates.push(process[0]) if !@frameUpdates.include?(process[0])
fra = (process[2]==0) ? 1 : process[3] # Frame counter
dur = (process[2]==0) ? 1 : process[2] # Total duration of process
case process[0]
when Processes::XY, Processes::DeltaXY
@x = process[5] + fra * (process[7] - process[5]) / dur
@y = process[6] + fra * (process[8] - process[6]) / dur
when Processes::Curve
@x, @y = getCubicPoint2(process[5],fra.to_f/dur)
when Processes::Z
@z = process[5] + fra * (process[6] - process[5]) / dur
when Processes::Zoom
@zoom_x = process[5] + fra * (process[7] - process[5]) / dur
@zoom_y = process[6] + fra * (process[8] - process[6]) / dur
when Processes::Angle
@angle = process[5] + fra * (process[6] - process[5]) / dur
when Processes::Tone
@tone.red = process[5].red + fra * (process[6].red - process[5].red) / dur
@tone.green = process[5].green + fra * (process[6].green - process[5].green) / dur
@tone.blue = process[5].blue + fra * (process[6].blue - process[5].blue) / dur
@tone.gray = process[5].gray + fra * (process[6].gray - process[5].gray) / dur
when Processes::Color
@color.red = process[5].red + fra * (process[6].red - process[5].red) / dur
@color.green = process[5].green + fra * (process[6].green - process[5].green) / dur
@color.blue = process[5].blue + fra * (process[6].blue - process[5].blue) / dur
@color.alpha = process[5].alpha + fra * (process[6].alpha - process[5].alpha) / dur
when Processes::Hue
@hue = (process[6] - process[5]).to_f / dur
when Processes::Opacity
@opacity = process[5] + fra * (process[6] - process[5]) / dur
when Processes::Visible
@visible = process[5]
when Processes::BlendType
@blend_type = process[5]
when Processes::SE
pbSEPlay(process[5],process[6])
when Processes::Name
@name = process[5]
when Processes::Origin
@origin = process[5]
when Processes::Src
@src_rect.x = process[5]
@src_rect.y = process[6]
when Processes::SrcSize
@src_rect.width = process[5]
@src_rect.height = process[6]
when Processes::CropBottom
@cropBottom = process[5]
end
# Increase frame counter
process[3] += 1
if process[3]>process[2]
# Process has ended, erase it
callback(process[4]) if process[4]
@processes[i] = nil
procEnded = true
end
end
# Clear out empty spaces in @processes array caused by finished processes
@processes.compact! if procEnded
# Add the constant rotation speed
if @rotate_speed != 0
@frameUpdates.push(Processes::Angle) if !@frameUpdates.include?(Processes::Angle)
@angle += @rotate_speed
while @angle<0; @angle += 360; end
@angle %= 360
end
end
end
def setPictureSprite(sprite, picture, iconSprite=false)
return if picture.frameUpdates.length==0
for i in 0...picture.frameUpdates.length
case picture.frameUpdates[i]
when Processes::XY, Processes::DeltaXY
sprite.x = picture.x.round
sprite.y = picture.y.round
when Processes::Z
sprite.z = picture.z
when Processes::Zoom
sprite.zoom_x = picture.zoom_x / 100.0
sprite.zoom_y = picture.zoom_y / 100.0
when Processes::Angle
sprite.angle = picture.angle
when Processes::Tone
sprite.tone = picture.tone
when Processes::Color
sprite.color = picture.color
when Processes::Hue
# This doesn't do anything.
when Processes::BlendType
sprite.blend_type = picture.blend_type
when Processes::Opacity
sprite.opacity = picture.opacity
when Processes::Visible
sprite.visible = picture.visible
when Processes::Name
sprite.name = picture.name if iconSprite && sprite.name != picture.name
when Processes::Origin
case picture.origin
when PictureOrigin::TopLeft, PictureOrigin::Left, PictureOrigin::BottomLeft
sprite.ox = 0
when PictureOrigin::Top, PictureOrigin::Center, PictureOrigin::Bottom
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width/2 : 0
when PictureOrigin::TopRight, PictureOrigin::Right, PictureOrigin::BottomRight
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width : 0
end
case picture.origin
when PictureOrigin::TopLeft, PictureOrigin::Top, PictureOrigin::TopRight
sprite.oy = 0
when PictureOrigin::Left, PictureOrigin::Center, PictureOrigin::Right
sprite.oy = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.height/2 : 0
when PictureOrigin::BottomLeft, PictureOrigin::Bottom, PictureOrigin::BottomRight
sprite.oy = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.height : 0
end
when Processes::Src
next unless iconSprite && sprite.src_rect
sprite.src_rect.x = picture.src_rect.x
sprite.src_rect.y = picture.src_rect.y
when Processes::SrcSize
next unless iconSprite && sprite.src_rect
sprite.src_rect.width = picture.src_rect.width
sprite.src_rect.height = picture.src_rect.height
end
end
if iconSprite && sprite.src_rect && picture.cropBottom>=0
spriteBottom = sprite.y-sprite.oy+sprite.src_rect.height
if spriteBottom>picture.cropBottom
sprite.src_rect.height = [picture.cropBottom-sprite.y+sprite.oy,0].max
end
end
end
def setPictureIconSprite(sprite, picture)
setPictureSprite(sprite,picture,true)
end
class PictureOrigin
TopLeft = 0
Center = 1
TopRight = 2
BottomLeft = 3
LowerLeft = 3
BottomRight = 4
LowerRight = 4
Top = 5
Bottom = 6
Left = 7
Right = 8
end
def pbTextBitmap(text, maxwidth=Graphics.width)
dims = []
tmp = Bitmap.new(maxwidth,Graphics.height)
pbSetSystemFont(tmp)
drawFormattedTextEx(tmp,0,0,maxwidth,text,Color.new(248,248,248),Color.new(168,184,184))
return tmp
end
class PictureSprite < SpriteWrapper
def initialize(viewport, picture)
super(viewport)
@picture = picture
@pictureBitmap = nil
@customBitmap = nil
@customBitmapIsBitmap = true
@hue = 0
update
end
def dispose
@pictureBitmap.dispose if @pictureBitmap
super
end
# Doesn't free the bitmap
def setCustomBitmap(bitmap)
@customBitmap = bitmap
@customBitmapIsBitmap = @customBitmap.is_a?(Bitmap)
end
def update
super
@pictureBitmap.update if @pictureBitmap
# If picture file name is different from current one
if @customBitmap && @picture.name==""
self.bitmap = (@customBitmapIsBitmap) ? @customBitmap : @customBitmap.bitmap
elsif @picture_name != @picture.name || @picture.hue.to_i != @hue.to_i
# Remember file name to instance variables
@picture_name = @picture.name
@hue = @picture.hue.to_i
# If file name is not empty
if @picture_name == ""
@pictureBitmap.dispose if @pictureBitmap
@pictureBitmap = nil
self.visible = false
return
end
# Get picture graphic
@pictureBitmap.dispose if @pictureBitmap
@pictureBitmap = AnimatedBitmap.new(@picture_name, @hue)
self.bitmap = (@pictureBitmap) ? @pictureBitmap.bitmap : nil
elsif @picture_name == ""
# Set sprite to invisible
self.visible = false
return
end
setPictureSprite(self,@picture)
end
end
class EventScene
attr_accessor :onCTrigger,:onBTrigger,:onUpdate
def initialize(viewport=nil)
@viewport = viewport
@onCTrigger = Event.new
@onBTrigger = Event.new
@onUpdate = Event.new
@pictures = []
@picturesprites = []
@usersprites = []
@disposed = false
end
def dispose
return if disposed?
for sprite in @picturesprites
sprite.dispose
end
for sprite in @usersprites
sprite.dispose
end
@onCTrigger.clear
@onBTrigger.clear
@onUpdate.clear
@pictures.clear
@picturesprites.clear
@usersprites.clear
@disposed = true
end
def disposed?
return @disposed
end
def addBitmap(x, y, bitmap)
# _bitmap_ can be a Bitmap or an AnimatedBitmap
# (update method isn't called if it's animated)
# EventScene doesn't take ownership of the passed-in bitmap
num = @pictures.length
picture = PictureEx.new(num)
picture.setXY(0,x,y)
picture.setVisible(0,true)
@pictures[num] = picture
@picturesprites[num] = PictureSprite.new(@viewport,picture)
@picturesprites[num].setCustomBitmap(bitmap)
return picture
end
def addLabel(x, y, width, text)
addBitmap(x,y,pbTextBitmap(text,width))
end
def addImage(x, y, name)
num = @pictures.length
picture = PictureEx.new(num)
picture.name = name
picture.setXY(0,x,y)
picture.setVisible(0,true)
@pictures[num] = picture
@picturesprites[num] = PictureSprite.new(@viewport,picture)
return picture
end
def addUserSprite(sprite)
@usersprites.push(sprite)
end
def getPicture(num)
return @pictures[num]
end
def wait(frames)
frames.times { update }
end
def pictureWait(extraframes=0)
loop do
hasRunning = false
for pic in @pictures
hasRunning = true if pic.running?
end
break if !hasRunning
update
end
extraframes.times { update }
end
def update
return if disposed?
Graphics.update
Input.update
for picture in @pictures
picture.update
end
for sprite in @picturesprites
sprite.update
end
for sprite in @usersprites
next if !sprite || sprite.disposed? || !sprite.is_a?(Sprite)
sprite.update
end
@onUpdate.trigger(self)
if Input.trigger?(Input::B)
@onBTrigger.trigger(self)
elsif Input.trigger?(Input::C)
@onCTrigger.trigger(self)
end
end
def main
while !disposed?
update
end
end
end
def pbEventScreen(cls)
pbFadeOutIn {
viewport = Viewport.new(0,0,Graphics.width,Graphics.height)
viewport.z = 99999
PBDebug.logonerr {
cls.new(viewport).main
}
viewport.dispose
}
end

View File

@@ -0,0 +1,172 @@
class Interpolator
ZOOM_X = 1
ZOOM_Y = 2
X = 3
Y = 4
OPACITY = 5
COLOR = 6
WAIT = 7
def initialize
@tweening = false
@tweensteps = []
@sprite = nil
@frames = 0
@step = 0
end
def tweening?
return @tweening
end
def tween(sprite,items,frames)
@tweensteps = []
if sprite && !sprite.disposed? && frames>0
@frames = frames
@step = 0
@sprite = sprite
for item in items
case item[0]
when ZOOM_X
@tweensteps[item[0]] = [sprite.zoom_x,item[1]-sprite.zoom_x]
when ZOOM_Y
@tweensteps[item[0]] = [sprite.zoom_y,item[1]-sprite.zoom_y]
when X
@tweensteps[item[0]] = [sprite.x,item[1]-sprite.x]
when Y
@tweensteps[item[0]] = [sprite.y,item[1]-sprite.y]
when OPACITY
@tweensteps[item[0]] = [sprite.opacity,item[1]-sprite.opacity]
when COLOR
@tweensteps[item[0]] = [sprite.color.clone,Color.new(
item[1].red-sprite.color.red,
item[1].green-sprite.color.green,
item[1].blue-sprite.color.blue,
item[1].alpha-sprite.color.alpha
)]
end
end
@tweening = true
end
end
def update
if @tweening
t = (@step*1.0)/@frames
for i in 0...@tweensteps.length
item = @tweensteps[i]
next if !item
case i
when ZOOM_X
@sprite.zoom_x = item[0]+item[1]*t
when ZOOM_Y
@sprite.zoom_y = item[0]+item[1]*t
when X
@sprite.x = item[0]+item[1]*t
when Y
@sprite.y = item[0]+item[1]*t
when OPACITY
@sprite.opacity = item[0]+item[1]*t
when COLOR
@sprite.color = Color.new(
item[0].red+item[1].red*t,
item[0].green+item[1].green*t,
item[0].blue+item[1].blue*t,
item[0].alpha+item[1].alpha*t
)
end
end
@step += 1
if @step==@frames
@step = 0
@frames = 0
@tweening = false
end
end
end
end
class RectInterpolator
def initialize(oldrect,newrect,frames)
restart(oldrect,newrect,frames)
end
def restart(oldrect,newrect,frames)
@oldrect = oldrect
@newrect = newrect
@frames = [frames,1].max
@curframe = 0
@rect = oldrect.clone
end
def set(rect)
rect.set(@rect.x,@rect.y,@rect.width,@rect.height)
end
def done?
@curframe>@frames
end
def update
return if done?
t = (@curframe*1.0/@frames)
x1 = @oldrect.x
x2 = @newrect.x
x = x1+t*(x2-x1)
y1 = @oldrect.y
y2 = @newrect.y
y = y1+t*(y2-y1)
rx1 = @oldrect.x+@oldrect.width
rx2 = @newrect.x+@newrect.width
rx = rx1+t*(rx2-rx1)
ry1 = @oldrect.y+@oldrect.height
ry2 = @newrect.y+@newrect.height
ry = ry1+t*(ry2-ry1)
minx = x<rx ? x : rx
maxx = x>rx ? x : rx
miny = y<ry ? y : ry
maxy = y>ry ? y : ry
@rect.set(minx,miny,maxx-minx,maxy-miny)
@curframe += 1
end
end
class PointInterpolator
attr_reader :x
attr_reader :y
def initialize(oldx,oldy,newx,newy,frames)
restart(oldx,oldy,newx,newy,frames)
end
def restart(oldx,oldy,newx,newy,frames)
@oldx = oldx
@oldy = oldy
@newx = newx
@newy = newy
@frames = frames
@curframe = 0
@x = oldx
@y = oldy
end
def done?
@curframe>@frames
end
def update
return if done?
t = (@curframe*1.0/@frames)
rx1 = @oldx
rx2 = @newx
@x = rx1+t*(rx2-rx1)
ry1 = @oldy
ry2 = @newy
@y = ry1+t*(ry2-ry1)
@curframe += 1
end
end

View File

@@ -0,0 +1,239 @@
#===============================================================================
# ** Modified Scene_Map class for Pokémon.
#-------------------------------------------------------------------------------
#
#===============================================================================
class Scene_Map
attr_reader :spritesetGlobal
def spriteset
for i in @spritesets.values
return i if i.map==$game_map
end
return @spritesets.values[0]
end
def createSpritesets
@spritesetGlobal = Spriteset_Global.new
@spritesets = {}
for map in $MapFactory.maps
@spritesets[map.map_id] = Spriteset_Map.new(map)
end
$MapFactory.setSceneStarted(self)
updateSpritesets
end
def createSingleSpriteset(map)
temp = $scene.spriteset.getAnimations
@spritesets[map] = Spriteset_Map.new($MapFactory.maps[map])
$scene.spriteset.restoreAnimations(temp)
$MapFactory.setSceneStarted(self)
updateSpritesets
end
def disposeSpritesets
return if !@spritesets
for i in @spritesets.keys
next if !@spritesets[i]
@spritesets[i].dispose
@spritesets[i] = nil
end
@spritesets.clear
@spritesets = {}
@spritesetGlobal.dispose
@spritesetGlobal = nil
end
def autofade(mapid)
playingBGM = $game_system.playing_bgm
playingBGS = $game_system.playing_bgs
return if !playingBGM && !playingBGS
map = pbLoadRxData(sprintf("Data/Map%03d",mapid))
if playingBGM && map.autoplay_bgm
if (PBDayNight.isNight? rescue false)
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name && playingBGM.name!=map.bgm.name+"_n"
else
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name
end
end
if playingBGS && map.autoplay_bgs
pbBGMFade(0.8) if playingBGS.name!=map.bgs.name
end
Graphics.frame_reset
end
def transfer_player(cancelVehicles=true)
$game_temp.player_transferring = false
pbCancelVehicles($game_temp.player_new_map_id) if cancelVehicles
autofade($game_temp.player_new_map_id)
pbBridgeOff
if $game_map.map_id!=$game_temp.player_new_map_id
$MapFactory.setup($game_temp.player_new_map_id)
end
$game_player.moveto($game_temp.player_new_x, $game_temp.player_new_y)
case $game_temp.player_new_direction
when 2; $game_player.turn_down
when 4; $game_player.turn_left
when 6; $game_player.turn_right
when 8; $game_player.turn_up
end
$game_player.straighten
$game_map.update
disposeSpritesets
GC.start
createSpritesets
if $game_temp.transition_processing
$game_temp.transition_processing = false
Graphics.transition(20)
end
$game_map.autoplay
Graphics.frame_reset
Input.update
end
def call_name
$game_temp.name_calling = false
$game_player.straighten
$game_map.update
end
def call_menu
$game_temp.menu_calling = false
$game_temp.in_menu = true
$game_player.straighten
$game_map.update
sscene = PokemonPauseMenu_Scene.new
sscreen = PokemonPauseMenu.new(sscene)
sscreen.pbStartPokemonMenu
$game_temp.in_menu = false
end
def call_debug
$game_temp.debug_calling = false
pbPlayDecisionSE
$game_player.straighten
pbFadeOutIn { pbDebugMenu }
end
def miniupdate
$PokemonTemp.miniupdate = true
loop do
updateMaps
$game_player.update
$game_system.update
$game_screen.update
break unless $game_temp.player_transferring
transfer_player
break if $game_temp.transition_processing
end
updateSpritesets
$PokemonTemp.miniupdate = false
end
def updateMaps
for map in $MapFactory.maps
map.update
end
$MapFactory.updateMaps(self)
end
def updateSpritesets
@spritesets = {} if !@spritesets
keys = @spritesets.keys.clone
for i in keys
if !$MapFactory.hasMap?(i)
@spritesets[i].dispose if @spritesets[i]
@spritesets[i] = nil
@spritesets.delete(i)
else
@spritesets[i].update
end
end
@spritesetGlobal.update
for map in $MapFactory.maps
@spritesets[map.map_id] = Spriteset_Map.new(map) if !@spritesets[map.map_id]
end
Events.onMapUpdate.trigger(self)
end
def update
loop do
updateMaps
pbMapInterpreter.update
$game_player.update
$game_system.update
$game_screen.update
break unless $game_temp.player_transferring
transfer_player
break if $game_temp.transition_processing
end
updateSpritesets
if $game_temp.to_title
$scene = pbCallTitle
return
end
if $game_temp.transition_processing
$game_temp.transition_processing = false
if $game_temp.transition_name == ""
Graphics.transition(20)
else
Graphics.transition(40, "Graphics/Transitions/" + $game_temp.transition_name)
end
end
return if $game_temp.message_window_showing
if !pbMapInterpreterRunning?
if Input.trigger?(Input::C)
$PokemonTemp.hiddenMoveEventCalling = true
elsif Input.trigger?(Input::B)
unless $game_system.menu_disabled or $game_player.moving?
$game_temp.menu_calling = true
$game_temp.menu_beep = true
end
elsif Input.trigger?(Input::F5)
unless $game_player.moving?
$PokemonTemp.keyItemCalling = true
end
elsif Input.trigger?(Input::A)
if $PokemonSystem.runstyle==1
$PokemonGlobal.runtoggle = !$PokemonGlobal.runtoggle
end
elsif Input.press?(Input::F9)
$game_temp.debug_calling = true if $DEBUG
end
end
unless $game_player.moving?
if $game_temp.name_calling; call_name
elsif $game_temp.menu_calling; call_menu
elsif $game_temp.debug_calling; call_debug
# elsif $game_temp.battle_calling; call_battle
# elsif $game_temp.shop_calling; call_shop
# elsif $game_temp.save_calling; call_save
elsif $PokemonTemp.keyItemCalling
$PokemonTemp.keyItemCalling = false
$game_player.straighten
pbUseKeyItem
elsif $PokemonTemp.hiddenMoveEventCalling
$PokemonTemp.hiddenMoveEventCalling = false
$game_player.straighten
Events.onAction.trigger(self)
end
end
end
def main
createSpritesets
Graphics.transition(20)
loop do
Graphics.update
Input.update
update
break if $scene != self
end
Graphics.freeze
disposeSpritesets
if $game_temp.to_title
Graphics.transition(20)
Graphics.freeze
end
end
end

View File

@@ -0,0 +1,134 @@
class IntroEventScene < EventScene
TICKS_PER_PIC = 40 # 20 ticks per second, so 2 seconds
TICKS_PER_ENTER_FLASH = 40
FADE_TICKS = 8
def initialize(pics,splash,viewport=nil)
super(nil)
@pics = pics
@splash = splash
@pic = addImage(0,0,"")
@pic.setOpacity(0,0) # set opacity to 0 after waiting 0 frames
@pic2 = addImage(0,0,"") # flashing "Press Enter" picture
@pic2.setOpacity(0,0)
@index = 0
data_system = pbLoadRxData("Data/System")
pbBGMPlay(data_system.title_bgm)
openPic(self,nil)
end
def openPic(scene,args)
onCTrigger.clear
@pic.name = "Graphics/Titles/"+@pics[@index]
# fade to opacity 255 in FADE_TICKS ticks after waiting 0 frames
@pic.moveOpacity(0,FADE_TICKS,255)
pictureWait
@timer = 0 # reset the timer
onUpdate.set(method(:picUpdate)) # call picUpdate every frame
onCTrigger.set(method(:closePic)) # call closePic when C key is pressed
end
def closePic(scene,args)
onUpdate.clear
onCTrigger.clear
@pic.moveOpacity(0,FADE_TICKS,0)
pictureWait
@index += 1 # Move to the next picture
if @index>=@pics.length
openSplash(scene,args)
else
openPic(scene,args)
end
end
def picUpdate(scene,args)
@timer += 1
if @timer>TICKS_PER_PIC*Graphics.frame_rate/20
@timer = 0
closePic(scene,args) # Close the picture
end
end
def openSplash(scene,args)
onUpdate.clear
onCTrigger.clear
@pic.name = "Graphics/Titles/"+@splash
@pic.moveOpacity(0,FADE_TICKS,255)
@pic2.name = "Graphics/Titles/start"
@pic2.setXY(0,0,322)
@pic2.setVisible(0,true)
@pic2.moveOpacity(0,FADE_TICKS,255)
pictureWait
onUpdate.set(method(:splashUpdate)) # call splashUpdate every frame
onCTrigger.set(method(:closeSplash)) # call closeSplash when C key is pressed
end
def closeSplash(scene,args)
onUpdate.clear
onCTrigger.clear
# Play random cry
cry = pbCryFile(1+rand(PBSpecies.maxValue))
pbSEPlay(cry,80,100) if cry
@pic.moveXY(0,20,0,0)
pictureWait
# Fade out
@pic.moveOpacity(0,FADE_TICKS,0)
@pic2.clearProcesses
@pic2.moveOpacity(0,FADE_TICKS,0)
pbBGMStop(1.0)
pictureWait
scene.dispose # Close the scene
sscene = PokemonLoad_Scene.new
sscreen = PokemonLoadScreen.new(sscene)
sscreen.pbStartLoadScreen
end
def closeSplashDelete(scene,args)
onUpdate.clear
onCTrigger.clear
# Play random cry
cry = pbCryFile(1+rand(PBSpecies.maxValue))
pbSEPlay(cry,80,100) if cry
@pic.moveXY(0,20,0,0)
pictureWait
# Fade out
@pic.moveOpacity(0,FADE_TICKS,0)
@pic2.clearProcesses
@pic2.moveOpacity(0,FADE_TICKS,0)
pbBGMStop(1.0)
pictureWait
scene.dispose # Close the scene
sscene = PokemonLoad_Scene.new
sscreen = PokemonLoadScreen.new(sscene)
sscreen.pbStartDeleteScreen
end
def splashUpdate(scene,args)
# Flashing of "Press Enter" picture
if !@pic2.running?
@pic2.moveOpacity(TICKS_PER_ENTER_FLASH*2/10,TICKS_PER_ENTER_FLASH*4/10,0)
@pic2.moveOpacity(TICKS_PER_ENTER_FLASH*6/10,TICKS_PER_ENTER_FLASH*4/10,255)
end
if Input.press?(Input::DOWN) &&
Input.press?(Input::B) &&
Input.press?(Input::CTRL)
closeSplashDelete(scene,args)
end
end
end
class Scene_Intro
def initialize(pics, splash = nil)
@pics = pics
@splash = splash
end
def main
Graphics.transition(0)
@eventscene = IntroEventScene.new(@pics,@splash)
@eventscene.main
Graphics.freeze
end
end

View File

@@ -0,0 +1,46 @@
#==============================================================================
# * Scene_Controls
#------------------------------------------------------------------------------
# Shows a help screen listing the keyboard controls.
# Display with:
# pbEventScreen(ButtonEventScene)
#==============================================================================
class ButtonEventScene < EventScene
def initialize(viewport=nil)
super
Graphics.freeze
addImage(0,0,"Graphics/Pictures/helpbg")
@labels=[
addLabel(52*2,13*2,Graphics.width*3/4,_INTL("Moves the main character. Also used to scroll through list entries.")),
addLabel(52*2,53*2,Graphics.width*3/4,_INTL("Used to confirm a choice, check things, and talk to people.")),
addLabel(52*2,93*2,Graphics.width*3/4,_INTL("Used to exit, cancel a choice or mode, and open the pause menu.")),
addLabel(52*2,133*2,Graphics.width*3/4,_INTL("Hold down while walking to run.")),
addLabel(52*2,157*2,Graphics.width*3/4,_INTL("Press to use a registered Key Item."))
]
@keys=[
addImage(26*2,18*2,"Graphics/Pictures/helpArrowKeys"),
addImage(26*2,59*2,"Graphics/Pictures/helpCkey"),
addImage(26*2,99*2,"Graphics/Pictures/helpXkey"),
addImage(26*2,130*2,"Graphics/Pictures/helpZkey"),
addImage(26*2,154*2,"Graphics/Pictures/helpFkey")
]
for key in @keys
key.origin=PictureOrigin::Top
end
for i in 0...5 # Make everything show (almost) immediately
@keys[i].setOrigin(0,PictureOrigin::Top)
@keys[i].setOpacity(0,255)
end
pictureWait # Update event scene with the changes
Graphics.transition(20)
# Go to next screen when user presses C
onCTrigger.set(method(:pbOnScreen1))
end
def pbOnScreen1(scene,args)
# End scene
Graphics.freeze
scene.dispose
Graphics.transition(20)
end
end

View File

@@ -0,0 +1,52 @@
#===============================================================================
# ** Scene_Movie class, created by SoundSpawn, fixed by Popper.
#-------------------------------------------------------------------------------
# Instruction
# 1) Movies must be in a new folder called "Movies" in your directory.
# 2) If you call this script from an event, e.g.
# Call Script: $scene = Scene_Movie.new("INTRO")
# 3) Have fun playing movies with this script!
#===============================================================================
class Scene_Movie
def initialize(movie)
@movie_name = RTP.getPath("Movies\\"+movie+".avi").gsub(/\//,"\\")
end
def main
@temp = Win32API.pbFindRgssWindow.to_s
movie = Win32API.new('winmm','mciSendString','%w(p,p,l,l)','V')
x=movie.call("open \""+@movie_name+
"\" alias FILE style 1073741824 parent " + @temp.to_s,0,0,0)
@message = Win32API.new('user32','SendMessage','%w(l,l,l,l)','V')
@detector = Win32API.new('user32','GetSystemMetrics','%w(l)','L')
@width = @detector.call(0)
if @width == 640
#fullscreen
Graphics.update
sleep(0.1)
Graphics.update
sleep(0.1)
Graphics.update
sleep(0.1)
#fullscreen
end
status = " " * 255
x=movie.call("play FILE",0,0,0)
loop do
sleep(0.1)
@message.call(@temp.to_i,11,0,0)
Graphics.update
@message.call(@temp.to_i,11,1,0)
Input.update
movie.call("status FILE mode",status,255,0)
true_status = status.unpack("aaaa")
break if true_status.to_s != "play"
if Input.trigger?(Input::B)
movie.call("close FILE",0,0,0)
$scene = Scene_Map.new
break
end
end
$scene = Scene_Map.new
end
end

View File

@@ -0,0 +1,231 @@
# Backgrounds to show in credits. Found in Graphics/Titles/ folder
CreditsBackgroundList = ["credits1","credits2","credits3","credits4","credits5"]
CreditsMusic = "Credits"
CreditsScrollSpeed = 2
CreditsFrequency = 9 # Number of seconds per credits slide
CREDITS_OUTLINE = Color.new(0,0,128, 255)
CREDITS_SHADOW = Color.new(0,0,0, 100)
CREDITS_FILL = Color.new(255,255,255, 255)
#==============================================================================
# * Scene_Credits
#------------------------------------------------------------------------------
# Scrolls the credits you make below. Original Author unknown.
#
## Edited by MiDas Mike so it doesn't play over the Title, but runs by calling
# the following:
# $scene = Scene_Credits.new
#
## New Edit 3/6/2007 11:14 PM by AvatarMonkeyKirby.
# Ok, what I've done is changed the part of the script that was supposed to make
# the credits automatically end so that way they actually end! Yes, they will
# actually end when the credits are finished! So, that will make the people you
# should give credit to now is: Unknown, MiDas Mike, and AvatarMonkeyKirby.
# -sincerly yours,
# Your Beloved
# Oh yea, and I also added a line of code that fades out the BGM so it fades
# sooner and smoother.
#
## New Edit 24/1/2012 by Maruno.
# Added the ability to split a line into two halves with <s>, with each half
# aligned towards the centre. Please also credit me if used.
#
## New Edit 22/2/2012 by Maruno.
# Credits now scroll properly when played with a zoom factor of 0.5. Music can
# now be defined. Credits can't be skipped during their first play.
#
## New Edit 25/3/2020 by Maruno.
# Scroll speed is now independent of frame rate. Now supports non-integer values
# for CreditsScrollSpeed.
#
## New Edit 21/8/2020 by Marin.
# Now automatically inserts the credits from the plugins that have been
# registered through the PluginManager module.
#==============================================================================
class Scene_Credits
# This next piece of code is the credits.
#Start Editing
CREDIT=<<_END_
Your credits go here.
Your credits go here.
Your credits go here.
Your credits go here.
Your credits go here.
{INSERTS_PLUGIN_CREDITS_DO_NOT_REMOVE}
"Pokémon Essentials" was created by:
Flameguru
Poccil (Peter O.)
Maruno
With contributions from:
AvatarMonkeyKirby<s>Marin
Boushy<s>MiDas Mike
Brother1440<s>Near Fantastica
FL.<s>PinkMan
Genzai Kawakami<s>Popper
help-14<s>Rataime
IceGod64<s>SoundSpawn
Jacob O. Wobbrock<s>the__end
KitsuneKouta<s>Venom12
Lisa Anthony<s>Wachunga
Luka S.J.<s>
and everyone else who helped out
"RPG Maker XP" by:
Enterbrain
Pokémon is owned by:
The Pokémon Company
Nintendo
Affiliated with Game Freak
This is a non-profit fan-made game.
No copyright infringements intended.
Please support the official games!
_END_
#Stop Editing
def main
#-------------------------------
# Animated Background Setup
#-------------------------------
@sprite = IconSprite.new(0,0)
@backgroundList = CreditsBackgroundList
@frameCounter = 0
# Number of game frames per background frame
@framesPerBackground = CreditsFrequency * Graphics.frame_rate
@sprite.setBitmap("Graphics/Titles/"+@backgroundList[0])
#------------------
# Credits text Setup
#------------------
plugin_credits = ""
PluginManager.plugins.each do |plugin|
pcred = PluginManager.credits(plugin)
plugin_credits << "\"#{plugin}\" version #{PluginManager.version(plugin)}\n"
if pcred.size >= 5
plugin_credits << pcred[0] + "\n"
i = 1
until i >= pcred.size
plugin_credits << pcred[i] + "<s>" + (pcred[i + 1] || "") + "\n"
i += 2
end
else
pcred.each do |name|
plugin_credits << name + "\n"
end
end
plugin_credits << "\n"
end
CREDIT.gsub!(/{INSERTS_PLUGIN_CREDITS_DO_NOT_REMOVE}/, plugin_credits)
credit_lines = CREDIT.split(/\n/)
credit_bitmap = Bitmap.new(Graphics.width,32 * credit_lines.size)
credit_lines.each_index do |i|
line = credit_lines[i]
line = line.split("<s>")
# LINE ADDED: If you use in your own game, you should remove this line
pbSetSystemFont(credit_bitmap) # <--- This line was added
x = 0
xpos = 0
align = 1 # Centre align
linewidth = Graphics.width
for j in 0...line.length
if line.length>1
xpos = (j==0) ? 0 : 20 + Graphics.width/2
align = (j==0) ? 2 : 0 # Right align : left align
linewidth = Graphics.width/2 - 20
end
credit_bitmap.font.color = CREDITS_SHADOW
credit_bitmap.draw_text(xpos,i * 32 + 8,linewidth,32,line[j],align)
credit_bitmap.font.color = CREDITS_OUTLINE
credit_bitmap.draw_text(xpos + 2,i * 32 - 2,linewidth,32,line[j],align)
credit_bitmap.draw_text(xpos,i * 32 - 2,linewidth,32,line[j],align)
credit_bitmap.draw_text(xpos - 2,i * 32 - 2,linewidth,32,line[j],align)
credit_bitmap.draw_text(xpos + 2,i * 32,linewidth,32,line[j],align)
credit_bitmap.draw_text(xpos - 2,i * 32,linewidth,32,line[j],align)
credit_bitmap.draw_text(xpos + 2,i * 32 + 2,linewidth,32,line[j],align)
credit_bitmap.draw_text(xpos,i * 32 + 2,linewidth,32,line[j],align)
credit_bitmap.draw_text(xpos - 2,i * 32 + 2,linewidth,32,line[j],align)
credit_bitmap.font.color = CREDITS_FILL
credit_bitmap.draw_text(xpos,i * 32,linewidth,32,line[j],align)
end
end
@trim = Graphics.height/10
@realOY = -(Graphics.height-@trim) # -430
@oyChangePerFrame = CreditsScrollSpeed*20.0/Graphics.frame_rate
@credit_sprite = Sprite.new(Viewport.new(0,@trim,Graphics.width,Graphics.height-(@trim*2)))
@credit_sprite.bitmap = credit_bitmap
@credit_sprite.z = 9998
@credit_sprite.oy = @realOY
@bg_index = 0
@zoom_adjustment = 1.0/$ResizeFactor
@last_flag = false
#--------
# Setup
#--------
# Stops all audio but background music
previousBGM = $game_system.getPlayingBGM
pbMEStop
pbBGSStop
pbSEStop
pbBGMFade(2.0)
pbBGMPlay(CreditsMusic)
Graphics.transition(20)
loop do
Graphics.update
Input.update
update
break if $scene != self
end
Graphics.freeze
@sprite.dispose
@credit_sprite.dispose
$PokemonGlobal.creditsPlayed = true
pbBGMPlay(previousBGM)
end
# Check if the credits should be cancelled
def cancel?
if Input.trigger?(Input::C) && $PokemonGlobal.creditsPlayed
$scene = Scene_Map.new
pbBGMFade(1.0)
return true
end
return false
end
# Checks if credits bitmap has reached its ending point
def last?
if @realOY > @credit_sprite.bitmap.height + @trim
$scene = ($game_map) ? Scene_Map.new : nil
pbBGMFade(2.0)
return true
end
return false
end
def update
@frameCounter += 1
# Go to next slide
if @frameCounter >= @framesPerBackground
@frameCounter -= @framesPerBackground
@bg_index += 1
@bg_index = 0 if @bg_index >= @backgroundList.length
@sprite.setBitmap("Graphics/Titles/"+@backgroundList[@bg_index])
end
return if cancel?
return if last?
@realOY += @oyChangePerFrame
@credit_sprite.oy = @realOY
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,472 @@
#===============================================================================
# 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
module PhoneMsgType
Generic = 0
Greeting = 1
Body = 2
BattleRequest = 3
end
#===============================================================================
# Global and map metadata
#===============================================================================
MetadataHome = 1
MetadataWildBattleBGM = 2
MetadataTrainerBattleBGM = 3
MetadataWildVictoryME = 4
MetadataTrainerVictoryME = 5
MetadataWildCaptureME = 6
MetadataSurfBGM = 7
MetadataBicycleBGM = 8
MetadataPlayerA = 9
MetadataPlayerB = 10
MetadataPlayerC = 11
MetadataPlayerD = 12
MetadataPlayerE = 13
MetadataPlayerF = 14
MetadataPlayerG = 15
MetadataPlayerH = 16
MetadataOutdoor = 1
MetadataShowArea = 2
MetadataBicycle = 3
MetadataBicycleAlways = 4
MetadataHealingSpot = 5
MetadataWeather = 6
MetadataMapPosition = 7
MetadataDiveMap = 8
MetadataDarkMap = 9
MetadataSafariMap = 10
MetadataSnapEdges = 11
MetadataDungeon = 12
MetadataBattleBack = 13
MetadataMapWildBattleBGM = 14
MetadataMapTrainerBattleBGM = 15
MetadataMapWildVictoryME = 16
MetadataMapTrainerVictoryME = 17
MetadataMapWildCaptureME = 18
MetadataMapSize = 19
MetadataEnvironment = 20
module PokemonMetadata
GlobalTypes = {
"Home" => [MetadataHome, "uuuu"],
"WildBattleBGM" => [MetadataWildBattleBGM, "s"],
"TrainerBattleBGM" => [MetadataTrainerBattleBGM, "s"],
"WildVictoryME" => [MetadataWildVictoryME, "s"],
"TrainerVictoryME" => [MetadataTrainerVictoryME, "s"],
"WildCaptureME" => [MetadataWildCaptureME, "s"],
"SurfBGM" => [MetadataSurfBGM, "s"],
"BicycleBGM" => [MetadataBicycleBGM, "s"],
"PlayerA" => [MetadataPlayerA, "esssssss",:PBTrainers],
"PlayerB" => [MetadataPlayerB, "esssssss",:PBTrainers],
"PlayerC" => [MetadataPlayerC, "esssssss",:PBTrainers],
"PlayerD" => [MetadataPlayerD, "esssssss",:PBTrainers],
"PlayerE" => [MetadataPlayerE, "esssssss",:PBTrainers],
"PlayerF" => [MetadataPlayerF, "esssssss",:PBTrainers],
"PlayerG" => [MetadataPlayerG, "esssssss",:PBTrainers],
"PlayerH" => [MetadataPlayerH, "esssssss",:PBTrainers]
}
NonGlobalTypes = {
"Outdoor" => [MetadataOutdoor, "b"],
"ShowArea" => [MetadataShowArea, "b"],
"Bicycle" => [MetadataBicycle, "b"],
"BicycleAlways" => [MetadataBicycleAlways, "b"],
"HealingSpot" => [MetadataHealingSpot, "uuu"],
"Weather" => [MetadataWeather, "eu",:PBFieldWeather],
"MapPosition" => [MetadataMapPosition, "uuu"],
"DiveMap" => [MetadataDiveMap, "u"],
"DarkMap" => [MetadataDarkMap, "b"],
"SafariMap" => [MetadataSafariMap, "b"],
"SnapEdges" => [MetadataSnapEdges, "b"],
"Dungeon" => [MetadataDungeon, "b"],
"BattleBack" => [MetadataBattleBack, "s"],
"WildBattleBGM" => [MetadataMapWildBattleBGM, "s"],
"TrainerBattleBGM" => [MetadataMapTrainerBattleBGM, "s"],
"WildVictoryME" => [MetadataMapWildVictoryME, "s"],
"TrainerVictoryME" => [MetadataMapTrainerVictoryME, "s"],
"WildCaptureME" => [MetadataMapWildCaptureME, "s"],
"MapSize" => [MetadataMapSize, "us"],
"Environment" => [MetadataEnvironment, "e",:PBEnvironment]
}
end
#===============================================================================
# Pokémon data
#===============================================================================
SpeciesType1 = 0
SpeciesType2 = 1
SpeciesBaseStats = 2
SpeciesGenderRate = 3
SpeciesGrowthRate = 4
SpeciesBaseExp = 5
SpeciesEffortPoints = 6
SpeciesRareness = 7
SpeciesHappiness = 8
SpeciesAbilities = 9
SpeciesHiddenAbility = 10
SpeciesCompatibility = 11
SpeciesStepsToHatch = 12
SpeciesHeight = 13
SpeciesWeight = 14
SpeciesColor = 15
SpeciesShape = 16
SpeciesHabitat = 17
SpeciesWildItemCommon = 18
SpeciesWildItemUncommon = 19
SpeciesWildItemRare = 20
SpeciesIncense = 21
SpeciesPokedexForm = 22 # For alternate forms
SpeciesMegaStone = 23 # For alternate forms
SpeciesMegaMove = 24 # For alternate forms
SpeciesUnmegaForm = 25 # For alternate forms
SpeciesMegaMessage = 26 # For alternate forms
MetricBattlerPlayerX = 0
MetricBattlerPlayerY = 1
MetricBattlerEnemyX = 2
MetricBattlerEnemyY = 3
MetricBattlerAltitude = 4
MetricBattlerShadowX = 5
MetricBattlerShadowSize = 6
module PokemonSpeciesData
def self.requiredValues(compilingForms=false)
ret = {
"Type1" => [SpeciesType1, "e",:PBTypes],
"BaseStats" => [SpeciesBaseStats, "vvvvvv"],
"BaseEXP" => [SpeciesBaseExp, "v"],
"EffortPoints" => [SpeciesEffortPoints, "uuuuuu"],
"Rareness" => [SpeciesRareness, "u"],
"Happiness" => [SpeciesHappiness, "u"],
"Compatibility" => [SpeciesCompatibility, "eE",:PBEggGroups,:PBEggGroups],
"StepsToHatch" => [SpeciesStepsToHatch, "v"],
"Height" => [SpeciesHeight, "f"],
"Weight" => [SpeciesWeight, "f"],
"Color" => [SpeciesColor, "e",:PBColors],
"Shape" => [SpeciesShape, "u"],
"Moves" => [0, "*ue",nil,:PBMoves],
"Kind" => [0, "s"],
"Pokedex" => [0, "q"]
}
if !compilingForms
ret["GenderRate"] = [SpeciesGenderRate, "e",:PBGenderRates]
ret["GrowthRate"] = [SpeciesGrowthRate, "e",:PBGrowthRates]
ret["Name"] = [0, "s"]
ret["InternalName"] = [0, "n"]
end
return ret
end
def self.optionalValues(compilingForms=false)
ret = {
"Type2" => [SpeciesType2, "e",:PBTypes],
"Abilities" => [SpeciesAbilities, "eE",:PBAbilities,:PBAbilities],
"HiddenAbility" => [SpeciesHiddenAbility, "eEEE",:PBAbilities,:PBAbilities,
:PBAbilities,:PBAbilities],
"Habitat" => [SpeciesHabitat, "e",:PBHabitats],
"WildItemCommon" => [SpeciesWildItemCommon, "e",:PBItems],
"WildItemUncommon" => [SpeciesWildItemUncommon, "e",:PBItems],
"WildItemRare" => [SpeciesWildItemRare, "e",:PBItems],
"BattlerPlayerX" => [MetricBattlerPlayerX, "i"],
"BattlerPlayerY" => [MetricBattlerPlayerY, "i"],
"BattlerEnemyX" => [MetricBattlerEnemyX, "i"],
"BattlerEnemyY" => [MetricBattlerEnemyY, "i"],
"BattlerAltitude" => [MetricBattlerAltitude, "i"],
"BattlerShadowX" => [MetricBattlerShadowX, "i"],
"BattlerShadowSize" => [MetricBattlerShadowSize, "u"],
"EggMoves" => [0, "*e",:PBMoves],
"FormName" => [0, "q"],
"Evolutions" => [0, "*ses",nil,:PBEvolution,nil]
}
if compilingForms
ret["PokedexForm"] = [SpeciesPokedexForm, "u"]
ret["MegaStone"] = [SpeciesMegaStone, "e",:PBItems]
ret["MegaMove"] = [SpeciesMegaMove, "e",:PBMoves]
ret["UnmegaForm"] = [SpeciesUnmegaForm, "u"]
ret["MegaMessage"] = [SpeciesMegaMessage, "u"]
else
ret["Incense"] = [SpeciesIncense, "e",:PBItems]
ret["RegionalNumbers"] = [0, "*u"]
end
return ret
end
end
#===============================================================================
# Manipulation methods for metadata, phone data and Pokémon species data
#===============================================================================
class PokemonTemp
attr_accessor :metadata
attr_accessor :townMapData
attr_accessor :encountersData
attr_accessor :phoneData
attr_accessor :regionalDexes
attr_accessor :speciesData
attr_accessor :speciesEggMoves
attr_accessor :speciesMetrics
attr_accessor :speciesMovesets
attr_accessor :speciesTMData
attr_accessor :speciesShadowMovesets
attr_accessor :pokemonFormToSpecies
attr_accessor :trainerTypesData
attr_accessor :trainersData
attr_accessor :moveToAnim
attr_accessor :battleAnims
end
def pbLoadMetadata
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.metadata
$PokemonTemp.metadata = load_data("Data/metadata.dat") || []
end
return $PokemonTemp.metadata
end
def pbGetMetadata(mapid,metadataType)
meta = pbLoadMetadata
return meta[mapid][metadataType] if meta[mapid]
return nil
end
def pbLoadTownMapData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.townMapData
$PokemonTemp.townMapData = load_data("Data/town_map.dat")
end
return $PokemonTemp.townMapData
end
def pbLoadEncountersData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.encountersData
if pbRgssExists?("Data/encounters.dat")
$PokemonTemp.encountersData = load_data("Data/encounters.dat")
end
end
return $PokemonTemp.encountersData
end
def pbLoadPhoneData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.phoneData
if pbRgssExists?("Data/phone.dat")
$PokemonTemp.phoneData = load_data("Data/phone.dat")
end
end
return $PokemonTemp.phoneData
end
def pbLoadRegionalDexes
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.regionalDexes
$PokemonTemp.regionalDexes = load_data("Data/regional_dexes.dat")
end
return $PokemonTemp.regionalDexes
end
def pbLoadSpeciesData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.speciesData
$PokemonTemp.speciesData = load_data("Data/species.dat") || []
end
return $PokemonTemp.speciesData
end
def pbGetSpeciesData(species,form=0,speciesDataType=-1)
species = getID(PBSpecies,species)
s = pbGetFSpeciesFromForm(species,form)
speciesData = pbLoadSpeciesData
if speciesDataType<0
return speciesData[s] || []
end
return speciesData[s][speciesDataType] if speciesData[s] && speciesData[s][speciesDataType]
case speciesDataType
when SpeciesType2; return nil
when SpeciesBaseStats; return [1,1,1,1,1,1]
when SpeciesEffortPoints; return [0,0,0,0,0,0]
when SpeciesStepsToHatch, SpeciesHeight, SpeciesWeight; return 1
end
return 0
end
def pbLoadEggMovesData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.speciesEggMoves
$PokemonTemp.speciesEggMoves = load_data("Data/species_eggmoves.dat") || []
end
return $PokemonTemp.speciesEggMoves
end
def pbGetSpeciesEggMoves(species,form=0)
species = getID(PBSpecies,species)
s = pbGetFSpeciesFromForm(species,form)
eggMovesData = pbLoadEggMovesData
return eggMovesData[s] if eggMovesData[s]
return []
end
def pbLoadSpeciesMetrics
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.speciesMetrics
$PokemonTemp.speciesMetrics = load_data("Data/species_metrics.dat") || []
end
return $PokemonTemp.speciesMetrics
end
def pbLoadMovesetsData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.speciesMovesets
$PokemonTemp.speciesMovesets = load_data("Data/species_movesets.dat") || []
end
return $PokemonTemp.speciesMovesets
end
def pbGetSpeciesMoveset(species,form=0)
species = getID(PBSpecies,species)
s = pbGetFSpeciesFromForm(species,form)
movesetsData = pbLoadMovesetsData
return movesetsData[s] if movesetsData[s]
return []
end
def pbLoadSpeciesTMData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.speciesTMData
$PokemonTemp.speciesTMData = load_data("Data/tm.dat") || []
end
return $PokemonTemp.speciesTMData
end
def pbLoadShadowMovesets
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.speciesShadowMovesets
$PokemonTemp.speciesShadowMovesets = load_data("Data/shadow_movesets.dat") || []
end
return $PokemonTemp.speciesShadowMovesets
end
def pbLoadFormToSpecies
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.pokemonFormToSpecies
$PokemonTemp.pokemonFormToSpecies = load_data("Data/form2species.dat")
end
return $PokemonTemp.pokemonFormToSpecies
end
def pbLoadTrainerTypesData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.trainerTypesData
$PokemonTemp.trainerTypesData = load_data("Data/trainer_types.dat") || []
end
return $PokemonTemp.trainerTypesData
end
def pbGetTrainerTypeData(type)
data = pbLoadTrainerTypesData
return data[type] if data
return nil
end
def pbLoadTrainersData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.trainersData
$PokemonTemp.trainersData = load_data("Data/trainers.dat") || []
end
return $PokemonTemp.trainersData
end
def pbGetTrainerData(trainerID,trainerName,partyID=0)
trainersData = pbLoadTrainersData
ret = nil
for t in trainersData
next if t[0]!=trainerID || t[1]!=trainerName || t[4]!=partyID
ret = t
break
end
return ret
end
def pbLoadMoveToAnim
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.moveToAnim
$PokemonTemp.moveToAnim = load_data("Data/move2anim.dat") || []
end
return $PokemonTemp.moveToAnim
end
def pbLoadBattleAnimations
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.battleAnims
if pbRgssExists?("Data/PkmnAnimations.rxdata")
$PokemonTemp.battleAnims = load_data("Data/PkmnAnimations.rxdata")
end
end
return $PokemonTemp.battleAnims
end
def pbClearData
if $PokemonTemp
$PokemonTemp.metadata = nil
$PokemonTemp.townMapData = nil
$PokemonTemp.encountersData = nil
$PokemonTemp.phoneData = nil
$PokemonTemp.regionalDexes = nil
$PokemonTemp.speciesData = nil
$PokemonTemp.speciesEggMoves = nil
$PokemonTemp.speciesMetrics = nil
$PokemonTemp.speciesMovesets = nil
$PokemonTemp.speciesTMData = nil
$PokemonTemp.speciesShadowMovesets = nil
$PokemonTemp.pokemonFormToSpecies = nil
$PokemonTemp.trainerTypesData = nil
$PokemonTemp.trainersData = nil
$PokemonTemp.moveToAnim = nil
$PokemonTemp.battleAnims = nil
end
MapFactoryHelper.clear
$PokemonEncounters.setup($game_map.map_id) if $game_map && $PokemonEncounters
if pbRgssExists?("Data/Tilesets.rxdata")
$data_tilesets = load_data("Data/Tilesets.rxdata")
end
if pbRgssExists?("Data/Tilesets.rvdata")
$data_tilesets = load_data("Data/Tilesets.rvdata")
end
end

View File

@@ -0,0 +1,104 @@
MOVE_ID = 0
MOVE_INTERNAL_NAME = 1
MOVE_NAME = 2
MOVE_FUNCTION_CODE = 3
MOVE_BASE_DAMAGE = 4
MOVE_TYPE = 5
MOVE_CATEGORY = 6
MOVE_ACCURACY = 7
MOVE_TOTAL_PP = 8
MOVE_EFFECT_CHANCE = 9
MOVE_TARGET = 10
MOVE_PRIORITY = 11
MOVE_FLAGS = 12
MOVE_DESCRIPTION = 13
class PokemonTemp
attr_accessor :movesData
end
def pbLoadMovesData
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
if !$PokemonTemp.movesData
if pbRgssExists?("Data/moves.dat")
$PokemonTemp.movesData = load_data("Data/moves.dat")
else
$PokemonTemp.movesData = []
end
end
return $PokemonTemp.movesData
end
def pbGetMoveData(moveID,moveDataType=-1)
meta = pbLoadMovesData
if moveDataType<0
return meta[moveID] || []
end
return meta[moveID][moveDataType] if meta[moveID]
return nil
end
alias __moveData__pbClearData pbClearData
def pbClearData
$PokemonTemp.movesData = nil if $PokemonTemp
__moveData__pbClearData
end
class PBMoveData
attr_reader :function,:basedamage,:type,:accuracy,:category
attr_reader :totalpp,:addlEffect,:target,:priority,:flags
def initialize(moveid)
moveData = pbGetMoveData(moveID)
@function = moveData[MOVE_FUNCTION_CODE]
@basedamage = moveData[MOVE_BASE_DAMAGE]
@type = moveData[MOVE_TYPE]
@category = moveData[MOVE_CATEGORY]
@accuracy = moveData[MOVE_ACCURACY]
@totalpp = moveData[MOVE_TOTAL_PP]
@addlEffect = moveData[MOVE_EFFECT_CHANCE]
@target = moveData[MOVE_TARGET]
@priority = moveData[MOVE_PRIORITY]
@flags = moveData[MOVE_FLAGS]
end
end
class PBMove
attr_reader(:id) # This move's ID
attr_accessor(:pp) # The amount of PP remaining for this move
attr_accessor(:ppup) # The number of PP Ups used for this move
# Initializes this object to the specified move ID.
def initialize(moveID)
@id = moveID
@pp = pbGetMoveData(moveID,MOVE_TOTAL_PP) || 0
@ppup = 0
end
# Changes this move's ID, and caps the PP amount if it is now greater than the
# new move's total PP.
def id=(value)
oldID = @id
@id = value
@pp = [@pp,self.totalpp].min if oldID>0
end
# Gets this move's type.
def type
return pbGetMoveData(@id,MOVE_TYPE) || 0
end
# Gets the maximum PP for this move.
def totalpp
maxPP = pbGetMoveData(@id,MOVE_TOTAL_PP) || 0
return maxPP+maxPP*@ppup/5
end
end

View File

@@ -0,0 +1,28 @@
#70925035
begin
module PBStatuses
NONE = 0
SLEEP = 1
POISON = 2
BURN = 3
PARALYSIS = 4
FROZEN = 5
def self.getName(id)
id = getID(PBStatuses,id)
names = [
_INTL("healthy"),
_INTL("asleep"),
_INTL("poisoned"),
_INTL("burned"),
_INTL("paralyzed"),
_INTL("frozen")
]
return names[id]
end end
rescue Exception
if $!.is_a?(SystemExit) || "#{$!.class}"=="Reset"
raise $!
end
end

View File

@@ -0,0 +1,90 @@
module PBTypeEffectiveness
INEFFECTIVE = 0
NOT_EFFECTIVE_ONE = 1
NORMAL_EFFECTIVE_ONE = 2
SUPER_EFFECTIVE_ONE = 4
NORMAL_EFFECTIVE = NORMAL_EFFECTIVE_ONE ** 3
end
class PBTypes
@@TypeData = nil
def PBTypes.loadTypeData
if !@@TypeData
@@TypeData = load_data("Data/types.dat")
@@TypeData[0].freeze
@@TypeData[1].freeze
@@TypeData[2].freeze
@@TypeData.freeze
end
return @@TypeData
end
def PBTypes.regularTypesCount
ret = 0
for i in 0..PBTypes.maxValue
next if PBTypes.isPseudoType?(i) || isConst?(i,PBTypes,:SHADOW)
ret += 1
end
return ret
end
def PBTypes.isPseudoType?(type)
return PBTypes.loadTypeData[0].include?(type)
end
def PBTypes.isSpecialType?(type)
return PBTypes.loadTypeData[1].include?(type)
end
def PBTypes.getEffectiveness(attackType,targetType)
return PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if !targetType || targetType<0
return PBTypes.loadTypeData[2][attackType*(PBTypes.maxValue+1)+targetType]
end
def PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2=nil,targetType3=nil)
mod1 = PBTypes.getEffectiveness(attackType,targetType1)
mod2 = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE
mod3 = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE
if targetType2!=nil && targetType2>=0 && targetType1!=targetType2
mod2 = PBTypes.getEffectiveness(attackType,targetType2)
end
if targetType3!=nil && targetType3>=0 &&
targetType1!=targetType3 && targetType2!=targetType3
mod3 = PBTypes.getEffectiveness(attackType,targetType3)
end
return mod1*mod2*mod3
end
def PBTypes.ineffective?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
return attackType==PBTypeEffectiveness::INEFFECTIVE if !targetType1
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
return e==PBTypeEffectiveness::INEFFECTIVE
end
def PBTypes.notVeryEffective?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
return attackType>PBTypeEffectiveness::INEFFECTIVE && attackType<PBTypeEffectiveness::NORMAL_EFFECTIVE if !targetType1
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
return e>PBTypeEffectiveness::INEFFECTIVE && e<PBTypeEffectiveness::NORMAL_EFFECTIVE
end
def PBTypes.resistant?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
return attackType<PBTypeEffectiveness::NORMAL_EFFECTIVE if !targetType1
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
return e<PBTypeEffectiveness::NORMAL_EFFECTIVE
end
def PBTypes.normalEffective?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
return attackType==PBTypeEffectiveness::NORMAL_EFFECTIVE if !targetType1
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
return e==PBTypeEffectiveness::NORMAL_EFFECTIVE
end
def PBTypes.superEffective?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
return attackType>PBTypeEffectiveness::NORMAL_EFFECTIVE if !targetType1
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
return e>PBTypeEffectiveness::NORMAL_EFFECTIVE
end
end

View File

@@ -0,0 +1,87 @@
module PBNatures
HARDY = 0
LONELY = 1
BRAVE = 2
ADAMANT = 3
NAUGHTY = 4
BOLD = 5
DOCILE = 6
RELAXED = 7
IMPISH = 8
LAX = 9
TIMID = 10
HASTY = 11
SERIOUS = 12
JOLLY = 13
NAIVE = 14
MODEST = 15
MILD = 16
QUIET = 17
BASHFUL = 18
RASH = 19
CALM = 20
GENTLE = 21
SASSY = 22
CAREFUL = 23
QUIRKY = 24
def self.maxValue; 24; end
def self.getCount; 25; end
def self.getName(id)
id = getID(PBNatures,id)
names = [
_INTL("Hardy"),
_INTL("Lonely"),
_INTL("Brave"),
_INTL("Adamant"),
_INTL("Naughty"),
_INTL("Bold"),
_INTL("Docile"),
_INTL("Relaxed"),
_INTL("Impish"),
_INTL("Lax"),
_INTL("Timid"),
_INTL("Hasty"),
_INTL("Serious"),
_INTL("Jolly"),
_INTL("Naive"),
_INTL("Modest"),
_INTL("Mild"),
_INTL("Quiet"),
_INTL("Bashful"),
_INTL("Rash"),
_INTL("Calm"),
_INTL("Gentle"),
_INTL("Sassy"),
_INTL("Careful"),
_INTL("Quirky")
]
return names[id]
end
def self.getStatRaised(id)
m = (id%25)/5 # 25 here is (number of stats)**2, not PBNatures.getCount
return [PBStats::ATTACK,PBStats::DEFENSE,PBStats::SPEED,
PBStats::SPATK,PBStats::SPDEF][m]
end
def self.getStatLowered(id)
m = id%5 # Don't need to %25 here because 25 is a multiple of 5
return [PBStats::ATTACK,PBStats::DEFENSE,PBStats::SPEED,
PBStats::SPATK,PBStats::SPDEF][m]
end
def self.getStatChanges(id)
id = getID(PBNatures,id)
up = PBNatures.getStatRaised(id)
dn = PBNatures.getStatLowered(id)
ret = []
PBStats.eachStat do |s|
ret[s] = 100
ret[s] += 10 if s==up
ret[s] -= 10 if s==dn
end
return ret
end
end

View File

@@ -0,0 +1,24 @@
module PBGenderRates
Genderless = 0
AlwaysMale = 1
FemaleOneEighth = 2
Female25Percent = 3
Female50Percent = 4
Female75Percent = 5
FemaleSevenEighths = 6
AlwaysFemale = 7
def self.genderByte(gender)
case gender
when AlwaysMale; return 0
when FemaleOneEighth; return 32
when Female25Percent; return 64
when Female50Percent; return 128
when Female75Percent; return 192
when FemaleSevenEighths; return 224
when AlwaysFemale; return 254
when Genderless; return 255
end
return 255 # Default value (genderless)
end
end

View File

@@ -0,0 +1,197 @@
module PBGrowthRates
Medium = MediumFast = 0
Erratic = 1
Fluctuating = 2
Parabolic = MediumSlow = 3
Fast = 4
Slow = 5
def self.maxValue; return 5; end
end
module PBExperience
@PBExpTable = []
@PBExpTable[PBGrowthRates::Medium] = [
-1, 0, 8, 27, 64, 125, 216, 343, 512, 729,
1000, 1331, 1728, 2197, 2744, 3375, 4096, 4913, 5832, 6859,
8000, 9261, 10648, 12167, 13824, 15625, 17576, 19683, 21952, 24389,
27000, 29791, 32768, 35937, 39304, 42875, 46656, 50653, 54872, 59319,
64000, 68921, 74088, 79507, 85184, 91125, 97336, 103823, 110592, 117649,
125000, 132651, 140608, 148877, 157464, 166375, 175616, 185193, 195112, 205379,
216000, 226981, 238328, 250047, 262144, 274625, 287496, 300763, 314432, 328509,
343000, 357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039,
512000, 531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969,
729000, 753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299,
1000000]
@PBExpTable[PBGrowthRates::Erratic] = [
-1, 0, 15, 52, 122, 237, 406, 637, 942, 1326,
1800, 2369, 3041, 3822, 4719, 5737, 6881, 8155, 9564, 11111,
12800, 14632, 16610, 18737, 21012, 23437, 26012, 28737, 31610, 34632,
37800, 41111, 44564, 48155, 51881, 55737, 59719, 63822, 68041, 72369,
76800, 81326, 85942, 90637, 95406, 100237, 105122, 110052, 115015, 120001,
125000, 131324, 137795, 144410, 151165, 158056, 165079, 172229, 179503, 186894,
194400, 202013, 209728, 217540, 225443, 233431, 241496, 249633, 257834, 267406,
276458, 286328, 296358, 305767, 316074, 326531, 336255, 346965, 357812, 367807,
378880, 390077, 400293, 411686, 423190, 433572, 445239, 457001, 467489, 479378,
491346, 501878, 513934, 526049, 536557, 548720, 560922, 571333, 583539, 591882,
600000]
@PBExpTable[PBGrowthRates::Fluctuating] = [
-1, 0, 4, 13, 32, 65, 112, 178, 276, 393,
540, 745, 967, 1230, 1591, 1957, 2457, 3046, 3732, 4526,
5440, 6482, 7666, 9003, 10506, 12187, 14060, 16140, 18439, 20974,
23760, 26811, 30146, 33780, 37731, 42017, 46656, 50653, 55969, 60505,
66560, 71677, 78533, 84277, 91998, 98415, 107069, 114205, 123863, 131766,
142500, 151222, 163105, 172697, 185807, 196322, 210739, 222231, 238036, 250562,
267840, 281456, 300293, 315059, 335544, 351520, 373744, 390991, 415050, 433631,
459620, 479600, 507617, 529063, 559209, 582187, 614566, 639146, 673863, 700115,
737280, 765275, 804997, 834809, 877201, 908905, 954084, 987754, 1035837, 1071552,
1122660, 1160499, 1214753, 1254796, 1312322, 1354652, 1415577, 1460276, 1524731, 1571884,
1640000]
@PBExpTable[PBGrowthRates::Parabolic] = [
-1, 0, 9, 57, 96, 135, 179, 236, 314, 419,
560, 742, 973, 1261, 1612, 2035, 2535, 3120, 3798, 4575,
5460, 6458, 7577, 8825, 10208, 11735, 13411, 15244, 17242, 19411,
21760, 24294, 27021, 29949, 33084, 36435, 40007, 43808, 47846, 52127,
56660, 61450, 66505, 71833, 77440, 83335, 89523, 96012, 102810, 109923,
117360, 125126, 133229, 141677, 150476, 159635, 169159, 179056, 189334, 199999,
211060, 222522, 234393, 246681, 259392, 272535, 286115, 300140, 314618, 329555,
344960, 360838, 377197, 394045, 411388, 429235, 447591, 466464, 485862, 505791,
526260, 547274, 568841, 590969, 613664, 636935, 660787, 685228, 710266, 735907,
762160, 789030, 816525, 844653, 873420, 902835, 932903, 963632, 995030, 1027103,
1059860]
@PBExpTable[PBGrowthRates::Fast] = [
-1, 0, 6, 21, 51, 100, 172, 274, 409, 583,
800, 1064, 1382, 1757, 2195, 2700, 3276, 3930, 4665, 5487,
6400, 7408, 8518, 9733, 11059, 12500, 14060, 15746, 17561, 19511,
21600, 23832, 26214, 28749, 31443, 34300, 37324, 40522, 43897, 47455,
51200, 55136, 59270, 63605, 68147, 72900, 77868, 83058, 88473, 94119,
100000, 106120, 112486, 119101, 125971, 133100, 140492, 148154, 156089, 164303,
172800, 181584, 190662, 200037, 209715, 219700, 229996, 240610, 251545, 262807,
274400, 286328, 298598, 311213, 324179, 337500, 351180, 365226, 379641, 394431,
409600, 425152, 441094, 457429, 474163, 491300, 508844, 526802, 545177, 563975,
583200, 602856, 622950, 643485, 664467, 685900, 707788, 730138, 752953, 776239,
800000]
@PBExpTable[PBGrowthRates::Slow] = [
-1, 0, 10, 33, 80, 156, 270, 428, 640, 911,
1250, 1663, 2160, 2746, 3430, 4218, 5120, 6141, 7290, 8573,
10000, 11576, 13310, 15208, 17280, 19531, 21970, 24603, 27440, 30486,
33750, 37238, 40960, 44921, 49130, 53593, 58320, 63316, 68590, 74148,
80000, 86151, 92610, 99383, 106480, 113906, 121670, 129778, 138240, 147061,
156250, 165813, 175760, 186096, 196830, 207968, 219520, 231491, 243890, 256723,
270000, 283726, 297910, 312558, 327680, 343281, 359370, 375953, 393040, 410636,
428750, 447388, 466560, 486271, 506530, 527343, 548720, 570666, 593190, 616298,
640000, 664301, 689210, 714733, 740880, 767656, 795070, 823128, 851840, 881211,
911250, 941963, 973360, 1005446, 1038230, 1071718, 1105920, 1140841, 1176490, 1212873,
1250000]
# Returns the maximum level a Pokémon can attain. If you want to make it vary,
# here's where you put your formulae. Note that this is also called by the
# Compiler, which happens before anything (e.g. Game Switches/Variables, the
# player's data) is loaded, so make sure they exist before using them, and
# make this method return the most maximum ever level if they don't.
def self.maxLevel
return MAXIMUM_LEVEL
end
# Erratic (600000):
# For levels 0-50: n**3([100-n]/50)
# For levels 51-68: n**3([150-n]/100)
# For levels 69-98: n**3(1.274-[1/50][n/3]-p(n mod 3))
# where p(x) = array(0.000,0.008,0.014)[x]
# For levels 99-100: n**3([160-n]/100)
# Fluctuating (1640000):
# For levels 0-15 : n**3([24+{(n+1)/3}]/50)
# For levels 16-35: n**3([14+n]/50)
# For levels 36-100: n**3([32+{n/2}]/50)
def self.pbGetExpInternal(level,growth)
if level <= 100
# Refer to experience table for levels 100 and less
return @PBExpTable[growth][level]
end
# Level 101+, use formulae
case growth
when PBGrowthRates::Medium # 1000000
return level ** 3
when PBGrowthRates::Erratic # 600000
# Different formula that causes 600000 EXP at level 100
return ((level ** 4) * 0.6 / 100).floor
when PBGrowthRates::Fluctuating # 1640000
# Different formula that causes 1640000 EXP at level 100
rate = 82
if level > 100
# Slow rate with increasing level
rate -= (level - 100) / 2
rate = 40 if rate < 40
end
return ((level ** 3) * (level * rate / 100) / 50.0).floor
when PBGrowthRates::Parabolic # 1059860
return ((level ** 3) * 6 / 5) - 15 * (level ** 2) + 100 * level - 140
when PBGrowthRates::Fast # 800000
return (level ** 3) * 4 / 5
when PBGrowthRates::Slow # 1250000
return (level ** 3) * 5 / 4
end
return 0
end
# Gets the maximum Exp Points possible for the given growth rate.
# growth -- Growth rate.
def self.pbGetMaxExperience(growth)
if growth<0 || growth>PBGrowthRates.maxValue
return ArgumentError.new("The growth rate is invalid.")
end
return pbGetExpInternal(maxLevel,growth)
end
# Gets the number of Exp Points needed to reach the given
# level with the given growth rate.
# growth -- Growth rate.
def self.pbGetStartExperience(level,growth)
if growth<0 || growth>PBGrowthRates.maxValue
return ArgumentError.new("The growth rate is invalid.")
end
if level<0
return ArgumentError.new("The level is invalid.")
end
mLevel = maxLevel
level = mLevel if level>mLevel
return pbGetExpInternal(level,growth)
end
# Adds experience points ensuring that the new total doesn't
# exceed the maximum Exp. Points for the given growth rate.
# currexp -- Current Exp Points.
# expgain -- Exp. Points to add
# growth -- Growth rate.
def self.pbAddExperience(currexp,expgain,growth)
if growth<0 || growth>PBGrowthRates.maxValue
return ArgumentError.new("The growth rate is invalid.")
end
exp = currexp+expgain
maxexp = pbGetExpInternal(maxLevel,growth)
exp = maxexp if exp>maxexp
return exp
end
# Calculates a level given the number of Exp Points and growth rate.
# growth -- Growth rate.
def self.pbGetLevelFromExperience(exp,growth)
if growth<0 || growth>PBGrowthRates.maxValue
return ArgumentError.new("The growth rate is invalid.")
end
mLevel = maxLevel
maxexp = pbGetExpInternal(mLevel,growth)
exp = maxexp if exp>maxexp
i = 0
for j in 0..mLevel
currentExp = pbGetExpInternal(i,growth)
return i if exp==currentExp
return i-1 if exp<currentExp
i += 1
end
return mLevel
end
end

View File

@@ -0,0 +1,66 @@
begin
module PBStats
# NOTE: You can change the order that the compiler expects Pokémon base
# stats/EV yields (effort points) to be in, by simply renumbering the
# stats here. The "main" stats (i.e. not accuracy/evasion) must still
# use up numbers 0 to 5 inclusive, though. It's up to you to write the
# base stats/EV yields in pokemon.txt and pokemonforms.txt in the
# order expected.
HP = 0
ATTACK = 1
DEFENSE = 2
SPEED = 3
SPATK = 4
SPDEF = 5
ACCURACY = 6
EVASION = 7
def self.getName(id)
id = getID(PBStats,id)
names = []
names[HP] = _INTL("HP")
names[ATTACK] = _INTL("Attack")
names[DEFENSE] = _INTL("Defense")
names[SPEED] = _INTL("Speed")
names[SPATK] = _INTL("Special Attack")
names[SPDEF] = _INTL("Special Defense")
names[ACCURACY] = _INTL("accuracy")
names[EVASION] = _INTL("evasiveness")
return names[id]
end
def self.getNameBrief(id)
id = getID(PBStats,id)
names = []
names[HP] = _INTL("HP")
names[ATTACK] = _INTL("Atk")
names[DEFENSE] = _INTL("Def")
names[SPEED] = _INTL("Spd")
names[SPATK] = _INTL("SpAtk")
names[SPDEF] = _INTL("SpDef")
names[ACCURACY] = _INTL("acc")
names[EVASION] = _INTL("eva")
return names[id]
end
def self.eachStat
[HP,ATTACK,DEFENSE,SPATK,SPDEF,SPEED].each { |s| yield s }
end
def self.eachMainBattleStat
[ATTACK,DEFENSE,SPATK,SPDEF,SPEED].each { |s| yield s }
end
def self.eachBattleStat
[ATTACK,DEFENSE,SPATK,SPDEF,SPEED,ACCURACY,EVASION].each { |s| yield s }
end
def self.validBattleStat?(stat)
self.eachBattleStat { |s| return true if s==stat }
return false
end
end
rescue Exception
if $!.is_a?(SystemExit) || "#{$!.class}"=="Reset"
raise $!
end
end

View File

@@ -0,0 +1,259 @@
module PBRibbons
HOENNCOOL = 1
HOENNCOOLSUPER = 2
HOENNCOOLHYPER = 3
HOENNCOOLMASTER = 4
HOENNBEAUTY = 5
HOENNBEAUTYSUPER = 6
HOENNBEAUTYHYPER = 7
HOENNBEAUTYMASTER = 8
HOENNCUTE = 9
HOENNCUTESUPER = 10
HOENNCUTEHYPER = 11
HOENNCUTEMASTER = 12
HOENNSMART = 13
HOENNSMARTSUPER = 14
HOENNSMARTHYPER = 15
HOENNSMARTMASTER = 16
HOENNTOUGH = 17
HOENNTOUGHSUPER = 18
HOENNTOUGHHYPER = 19
HOENNTOUGHMASTER = 20
SINNOHCOOL = 21
SINNOHCOOLSUPER = 22
SINNOHCOOLHYPER = 23
SINNOHCOOLMASTER = 24
SINNOHBEAUTY = 25
SINNOHBEAUTYSUPER = 26
SINNOHBEAUTYHYPER = 27
SINNOHBEAUTYMASTER = 28
SINNOHCUTE = 29
SINNOHCUTESUPER = 30
SINNOHCUTEHYPER = 31
SINNOHCUTEMASTER = 32
SINNOHSMART = 33
SINNOHSMARTSUPER = 34
SINNOHSMARTHYPER = 35
SINNOHSMARTMASTER = 36
SINNOHTOUGH = 37
SINNOHTOUGHSUPER = 38
SINNOHTOUGHHYPER = 39
SINNOHTOUGHMASTER = 40
WINNING = 41
VICTORY = 42
ABILITY = 43
GREATABILITY = 44
DOUBLEABILITY = 45
MULTIABILITY = 46
PAIRABILITY = 47
WORLDABILITY = 48
CHAMPION = 49
SINNOHCHAMP = 50
RECORD = 51
EVENT = 52
LEGEND = 53
GORGEOUS = 54
ROYAL = 55
GORGEOUSROYAL = 56
ALERT = 57
SHOCK = 58
DOWNCAST = 59
CARELESS = 60
RELAX = 61
SNOOZE = 62
SMILE = 63
FOOTPRINT = 64
ARTIST = 65
EFFORT = 66
BIRTHDAY = 67
SPECIAL = 68
CLASSIC = 69
PREMIER = 70
SOUVENIR = 71
WISHING = 72
NATIONAL = 73
COUNTRY = 74
BATTLECHAMPION = 75
REGIONALCHAMPION = 76
EARTH = 77
WORLD = 78
NATIONALCHAMPION = 79
WORLDCHAMPION = 80
def self.maxValue; 80; end
def self.getCount; 80; end
def self.getName(id)
id = getID(PBRibbons,id)
names = ["",
_INTL("Cool Ribbon"),
_INTL("Cool Ribbon Super"),
_INTL("Cool Ribbon Hyper"),
_INTL("Cool Ribbon Master"),
_INTL("Beauty Ribbon"),
_INTL("Beauty Ribbon Super"),
_INTL("Beauty Ribbon Hyper"),
_INTL("Beauty Ribbon Master"),
_INTL("Cute Ribbon"),
_INTL("Cute Ribbon Super"),
_INTL("Cute Ribbon Hyper"),
_INTL("Cute Ribbon Master"),
_INTL("Smart Ribbon"),
_INTL("Smart Ribbon Super"),
_INTL("Smart Ribbon Hyper"),
_INTL("Smart Ribbon Master"),
_INTL("Tough Ribbon"),
_INTL("Tough Ribbon Super"),
_INTL("Tough Ribbon Hyper"),
_INTL("Tough Ribbon Master"),
_INTL("Cool Ribbon"),
_INTL("Cool Ribbon Great"),
_INTL("Cool Ribbon Ultra"),
_INTL("Cool Ribbon Master"),
_INTL("Beauty Ribbon"),
_INTL("Beauty Ribbon Great"),
_INTL("Beauty Ribbon Ultra"),
_INTL("Beauty Ribbon Master"),
_INTL("Cute Ribbon"),
_INTL("Cute Ribbon Great"),
_INTL("Cute Ribbon Ultra"),
_INTL("Cute Ribbon Master"),
_INTL("Smart Ribbon"),
_INTL("Smart Ribbon Great"),
_INTL("Smart Ribbon Ultra"),
_INTL("Smart Ribbon Master"),
_INTL("Tough Ribbon"),
_INTL("Tough Ribbon Great"),
_INTL("Tough Ribbon Ultra"),
_INTL("Tough Ribbon Master"),
_INTL("Winning Ribbon"),
_INTL("Victory Ribbon"),
_INTL("Ability Ribbon"),
_INTL("Great Ability Ribbon"),
_INTL("Double Ability Ribbon"),
_INTL("Multi Ability Ribbon"),
_INTL("Pair Ability Ribbon"),
_INTL("World Ability Ribbon"),
_INTL("Champion Ribbon"),
_INTL("Sinnoh Champ Ribbon"),
_INTL("Record Ribbon"),
_INTL("Event Ribbon"),
_INTL("Legend Ribbon"),
_INTL("Gorgeous Ribbon"),
_INTL("Royal Ribbon"),
_INTL("Gorgeous Royal Ribbon"),
_INTL("Alert Ribbon"),
_INTL("Shock Ribbon"),
_INTL("Downcast Ribbon"),
_INTL("Careless Ribbon"),
_INTL("Relax Ribbon"),
_INTL("Snooze Ribbon"),
_INTL("Smile Ribbon"),
_INTL("Footprint Ribbon"),
_INTL("Artist Ribbon"),
_INTL("Effort Ribbon"),
_INTL("Birthday Ribbon"),
_INTL("Special Ribbon"),
_INTL("Classic Ribbon"),
_INTL("Premier Ribbon"),
_INTL("Souvenir Ribbon"),
_INTL("Wishing Ribbon"),
_INTL("National Ribbon"),
_INTL("Country Ribbon"),
_INTL("Battle Champion Ribbon"),
_INTL("Regional Champion Ribbon"),
_INTL("Earth Ribbon"),
_INTL("World Ribbon"),
_INTL("National Champion Ribbon"),
_INTL("World Champion Ribbon")
]
return names[id]
end
def self.getDescription(id)
id = getID(PBRibbons,id)
desc = ["",
_INTL("Hoenn Cool Contest Normal Rank winner!"),
_INTL("Hoenn Cool Contest Super Rank winner!"),
_INTL("Hoenn Cool Contest Hyper Rank winner!"),
_INTL("Hoenn Cool Contest Master Rank winner!"),
_INTL("Hoenn Beauty Contest Normal Rank winner!"),
_INTL("Hoenn Beauty Contest Super Rank winner!"),
_INTL("Hoenn Beauty Contest Hyper Rank winner!"),
_INTL("Hoenn Beauty Contest Master Rank winner!"),
_INTL("Hoenn Cute Contest Normal Rank winner!"),
_INTL("Hoenn Cute Contest Super Rank winner!"),
_INTL("Hoenn Cute Contest Hyper Rank winner!"),
_INTL("Hoenn Cute Contest Master Rank winner!"),
_INTL("Hoenn Smart Contest Normal Rank winner!"),
_INTL("Hoenn Smart Contest Super Rank winner!"),
_INTL("Hoenn Smart Contest Hyper Rank winner!"),
_INTL("Hoenn Smart Contest Master Rank winner!"),
_INTL("Hoenn Tough Contest Normal Rank winner!"),
_INTL("Hoenn Tough Contest Super Rank winner!"),
_INTL("Hoenn Tough Contest Hyper Rank winner!"),
_INTL("Hoenn Tough Contest Master Rank winner!"),
_INTL("Super Contest Cool Category Normal Rank winner!"),
_INTL("Super Contest Cool Category Great Rank winner!"),
_INTL("Super Contest Cool Category Ultra Rank winner!"),
_INTL("Super Contest Cool Category Master Rank winner!"),
_INTL("Super Contest Beauty Category Normal Rank winner!"),
_INTL("Super Contest Beauty Category Great Rank winner!"),
_INTL("Super Contest Beauty Category Ultra Rank winner!"),
_INTL("Super Contest Beauty Category Master Rank winner!"),
_INTL("Super Contest Cute Category Normal Rank winner!"),
_INTL("Super Contest Cute Category Great Rank winner!"),
_INTL("Super Contest Cute Category Ultra Rank winner!"),
_INTL("Super Contest Cute Category Master Rank winner!"),
_INTL("Super Contest Smart Category Normal Rank winner!"),
_INTL("Super Contest Smart Category Great Rank winner!"),
_INTL("Super Contest Smart Category Ultra Rank winner!"),
_INTL("Super Contest Smart Category Master Rank winner!"),
_INTL("Super Contest Tough Category Normal Rank winner!"),
_INTL("Super Contest Tough Category Great Rank winner!"),
_INTL("Super Contest Tough Category Ultra Rank winner!"),
_INTL("Super Contest Tough Category Master Rank winner!"),
_INTL("Ribbon awarded for clearing Hoenn's Battle Tower's Lv. 50 challenge."),
_INTL("Ribbon awarded for clearing Hoenn's Battle Tower's Lv. 100 challenge."),
_INTL("A Ribbon awarded for defeating the Tower Tycoon at the Battle Tower."),
_INTL("A Ribbon awarded for defeating the Tower Tycoon at the Battle Tower."),
_INTL("A Ribbon awarded for completing the Battle Tower Double challenge."),
_INTL("A Ribbon awarded for completing the Battle Tower Multi challenge."),
_INTL("A Ribbon awarded for completing the Battle Tower Link Multi challenge."),
_INTL("A Ribbon awarded for completing the Wi-Fi Battle Tower challenge."),
_INTL("Ribbon for clearing the Pokémon League and entering the Hall of Fame in another region. "),
_INTL("Ribbon awarded for beating the Sinnoh Champion and entering the Hall of Fame."),
_INTL("A Ribbon awarded for setting an incredible record."),
_INTL("Pokémon Event Participation Ribbon."),
_INTL("A Ribbon awarded for setting a legendary record."),
_INTL("An extraordinarily gorgeous and extravagant Ribbon."),
_INTL("An incredibly regal Ribbon with an air of nobility."),
_INTL("A gorgeous and regal Ribbon that is the peak of fabulous."),
_INTL("A Ribbon for recalling an invigorating event that created life energy."),
_INTL("A Ribbon for recalling a thrilling event that made life more exciting."),
_INTL("A Ribbon for recalling feelings of sadness that added spice to life."),
_INTL("A Ribbon for recalling a careless error that helped steer life decisions."),
_INTL("A Ribbon for recalling a refreshing event that added sparkle to life."),
_INTL("A Ribbon for recalling a deep slumber that made life soothing."),
_INTL("A Ribbon for recalling that smiles enrich the quality of life."),
_INTL("A Ribbon awarded to a Pokémon deemed to have a top-quality footprint."),
_INTL("Ribbon awarded for being chosen as a super sketch model in Hoenn."),
_INTL("Ribbon awarded for being an exceptionally hard worker."),
_INTL("A Ribbon to celebrate a birthday."),
_INTL("A special Ribbon for a special day."),
_INTL("A Ribbon that proclaims love for Pokémon."),
_INTL("Special Holiday Ribbon."),
_INTL("A Ribbon to cherish a special memory."),
_INTL("A Ribbon said to make your wish come true."),
_INTL("A Ribbon awarded for overcoming all difficult challenges."),
_INTL("Pokémon League Champion Ribbon."),
_INTL("Battle Competition Champion Ribbon."),
_INTL("Pokémon World Championships Regional Champion Ribbon."),
_INTL("A Ribbon awarded for winning 100 matches in a row."),
_INTL("Pokémon League Champion Ribbon."),
_INTL("Pokémon World Championships National Champion Ribbon."),
_INTL("Pokémon World Championships World Champion Ribbon.")
]
return desc[id]
end
end

View File

@@ -0,0 +1,42 @@
module PBEggGroups
Undiscovered = 0 # NoEggs, None, NA
Monster = 1
Water1 = 2
Bug = 3
Flying = 4
Field = 5 # Ground
Fairy = 6
Grass = 7 # Plant
Humanlike = 8 # Humanoid, Humanshape, Human
Water3 = 9
Mineral = 10
Amorphous = 11 # Indeterminate
Water2 = 12
Ditto = 13
Dragon = 14
def self.maxValue; 14; end
def self.getCount; 15; end
def self.getName(id)
id = getID(PBEggGroups,id)
names = [
_INTL("Undiscovered"),
_INTL("Monster"),
_INTL("Water 1"),
_INTL("Bug"),
_INTL("Flying"),
_INTL("Field"),
_INTL("Fairy"),
_INTL("Grass"),
_INTL("Human-like"),
_INTL("Water 3"),
_INTL("Mineral"),
_INTL("Amorphous"),
_INTL("Water 2"),
_INTL("Ditto"),
_INTL("Dragon")
]
return names[id]
end
end

View File

@@ -0,0 +1,33 @@
# Colors must begin at 0 and have no missing numbers
module PBColors
Red = 0
Blue = 1
Yellow = 2
Green = 3
Black = 4
Brown = 5
Purple = 6
Gray = 7
White = 8
Pink = 9
def self.maxValue; 9; end
def self.getCount; 10; end
def self.getName(id)
id = getID(PBColors,id)
names = [
_INTL("Red"),
_INTL("Blue"),
_INTL("Yellow"),
_INTL("Green"),
_INTL("Black"),
_INTL("Brown"),
_INTL("Purple"),
_INTL("Gray"),
_INTL("White"),
_INTL("Pink")
]
return names[id]
end
end

View File

@@ -0,0 +1,32 @@
module PBHabitats
None = 0
Grassland = 1
Forest = 2
WatersEdge = 3
Sea = 4
Cave = 5
Mountain = 6
RoughTerrain = 7
Urban = 8
Rare = 9
def self.maxValue; 9; end
def self.getCount; 10; end
def self.getName(id)
id = getID(PBHabitats,id)
names = [
_INTL("None"),
_INTL("Grassland"),
_INTL("Forest"),
_INTL("Water's Edge"),
_INTL("Sea"),
_INTL("Cave"),
_INTL("Mountain"),
_INTL("Rough Terrain"),
_INTL("Urban"),
_INTL("Rare")
]
return names[id]
end
end

View File

@@ -0,0 +1,665 @@
class PokeBattle_Battler
# Fundamental to this object
attr_reader :battle
attr_accessor :index
# The Pokémon and its properties
attr_reader :pokemon
attr_accessor :pokemonIndex
attr_accessor :species
attr_accessor :type1
attr_accessor :type2
attr_accessor :ability
attr_accessor :moves
attr_accessor :gender
attr_accessor :iv
attr_accessor :attack
attr_accessor :spatk
attr_accessor :speed
attr_accessor :stages
attr_reader :totalhp
attr_reader :fainted # Boolean to mark whether self has fainted properly
attr_accessor :captured # Boolean to mark whether self was captured
attr_reader :dummy
attr_accessor :effects
# Things the battler has done in battle
attr_accessor :turnCount
attr_accessor :participants
attr_accessor :lastAttacker
attr_accessor :lastFoeAttacker
attr_accessor :lastHPLost
attr_accessor :lastHPLostFromFoe
attr_accessor :lastMoveUsed
attr_accessor :lastMoveUsedType
attr_accessor :lastRegularMoveUsed
attr_accessor :lastRegularMoveTarget # For Instruct
attr_accessor :lastRoundMoved
attr_accessor :lastMoveFailed # For Stomping Tantrum
attr_accessor :lastRoundMoveFailed # For Stomping Tantrum
attr_accessor :movesUsed
attr_accessor :currentMove # ID of multi-turn move currently being used
attr_accessor :tookDamage # Boolean for whether self took damage this round
attr_accessor :tookPhysicalHit
attr_accessor :damageState
attr_accessor :initialHP # Set at the start of each move's usage
#=============================================================================
# Complex accessors
#=============================================================================
attr_reader :level
def level=(value)
@level = value
@pokemon.level = value if @pokemon
end
attr_reader :form
def form=(value)
@form = value
@pokemon.form = value if @pokemon
end
attr_reader :item
def item=(value)
@item = value
@pokemon.setItem(value) if @pokemon
end
def defense
return @spdef if @battle.field.effects[PBEffects::WonderRoom]>0
return @defense
end
attr_writer :defense
def spdef
return @defense if @battle.field.effects[PBEffects::WonderRoom]>0
return @spdef
end
attr_writer :spdef
attr_reader :hp
def hp=(value)
@hp = value.to_i
@pokemon.hp = value.to_i if @pokemon
end
def fainted?; return @hp<=0; end
alias isFainted? fainted?
attr_reader :status
def status=(value)
@effects[PBEffects::Truant] = false if @status==PBStatuses::SLEEP && value!=PBStatuses::SLEEP
@effects[PBEffects::Toxic] = 0 if value!=PBStatuses::POISON
@status = value
@pokemon.status = value if @pokemon
self.statusCount = 0 if value!=PBStatuses::POISON && value!=PBStatuses::SLEEP
@battle.scene.pbRefreshOne(@index)
end
attr_reader :statusCount
def statusCount=(value)
@statusCount = value
@pokemon.statusCount = value if @pokemon
end
#=============================================================================
# Properties from Pokémon
#=============================================================================
def happiness; return @pokemon ? @pokemon.happiness : 0; end
def nature; return @pokemon ? @pokemon.nature : 0; end
def pokerusStage; return @pokemon ? @pokemon.pokerusStage : 0; end
#=============================================================================
# Mega Evolution, Primal Reversion, Shadow Pokémon
#=============================================================================
def hasMega?
return false if @effects[PBEffects::Transform]
return @pokemon && @pokemon.hasMegaForm?
end
def mega?; return @pokemon && @pokemon.mega?; end
alias isMega? mega?
def hasPrimal?
return false if @effects[PBEffects::Transform]
return @pokemon && @pokemon.hasPrimalForm?
end
def primal?; return @pokemon && @pokemon.primal?; end
alias isPrimal? primal?
def shadowPokemon?; return false; end
alias isShadow? shadowPokemon?
def inHyperMode?; return false; end
#=============================================================================
# Display-only properties
#=============================================================================
def name
return @effects[PBEffects::Illusion].name if @effects[PBEffects::Illusion]
return @name
end
attr_writer :name
def displayPokemon
return @effects[PBEffects::Illusion] if @effects[PBEffects::Illusion]
return self.pokemon
end
def displaySpecies
return @effects[PBEffects::Illusion].species if @effects[PBEffects::Illusion]
return self.species
end
def displayGender
return @effects[PBEffects::Illusion].gender if @effects[PBEffects::Illusion]
return self.gender
end
def displayForm
return @effects[PBEffects::Illusion].form if @effects[PBEffects::Illusion]
return self.form
end
def shiny?
return @effects[PBEffects::Illusion].shiny? if @effects[PBEffects::Illusion]
return @pokemon && @pokemon.shiny?
end
alias isShiny? shiny?
def owned?
return false if !@battle.wildBattle?
return $Trainer.owned[displaySpecies]
end
alias owned owned?
def abilityName; return PBAbilities.getName(@ability); end
def itemName; return PBItems.getName(@item); end
def pbThis(lowerCase=false)
if opposes?
if @battle.trainerBattle?
return lowerCase ? _INTL("the opposing {1}",name) : _INTL("The opposing {1}",name)
else
return lowerCase ? _INTL("the wild {1}",name) : _INTL("The wild {1}",name)
end
elsif !pbOwnedByPlayer?
return lowerCase ? _INTL("the ally {1}",name) : _INTL("The ally {1}",name)
end
return name
end
def pbTeam(lowerCase=false)
if opposes?
return lowerCase ? _INTL("the opposing team") : _INTL("The opposing team")
end
return lowerCase ? _INTL("your team") : _INTL("Your team")
end
def pbOpposingTeam(lowerCase=false)
if opposes?
return lowerCase ? _INTL("your team") : _INTL("Your team")
end
return lowerCase ? _INTL("the opposing team") : _INTL("The opposing team")
end
#=============================================================================
# Calculated properties
#=============================================================================
def pbSpeed
return 1 if fainted?
stageMul = [2,2,2,2,2,2, 2, 3,4,5,6,7,8]
stageDiv = [8,7,6,5,4,3, 2, 2,2,2,2,2,2]
stage = @stages[PBStats::SPEED] + 6
speed = @speed*stageMul[stage]/stageDiv[stage]
speedMult = 0x1000
# Ability effects that alter calculated Speed
if abilityActive?
speedMult = BattleHandlers.triggerSpeedCalcAbility(@ability,self,speedMult)
end
# Item effects that alter calculated Speed
if itemActive?
speedMult = BattleHandlers.triggerSpeedCalcItem(@item,self,speedMult)
end
# Other effects
speedMult *= 2 if pbOwnSide.effects[PBEffects::Tailwind]>0
speedMult /= 2 if pbOwnSide.effects[PBEffects::Swamp]>0
# Paralysis
if status==PBStatuses::PARALYSIS && !hasActiveAbility?(:QUICKFEET)
speedMult /= (NEWEST_BATTLE_MECHANICS) ? 2 : 4
end
# Badge multiplier
if @battle.internalBattle && pbOwnedByPlayer? &&
@battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_SPEED
speedMult *= 1.1
end
# Calculation
return [(speed.to_f*speedMult/0x1000).round,1].max
end
def pbWeight
ret = (@pokemon) ? @pokemon.weight : 500
ret += @effects[PBEffects::WeightChange]
ret = 1 if ret<1
if abilityActive? && !@battle.moldBreaker
ret = BattleHandlers.triggerWeightCalcAbility(@ability,self,ret)
end
if itemActive?
ret = BattleHandlers.triggerWeightCalcItem(@item,self,ret)
end
return [ret,1].max
end
#=============================================================================
# Queries about what the battler has
#=============================================================================
def plainStats
ret = []
ret[PBStats::ATTACK] = self.attack
ret[PBStats::DEFENSE] = self.defense
ret[PBStats::SPATK] = self.spatk
ret[PBStats::SPDEF] = self.spdef
ret[PBStats::SPEED] = self.speed
return ret
end
# Returns the active types of this Pokémon. The array should not include the
# same type more than once, and should not include any invalid type numbers
# (e.g. -1).
def pbTypes(withType3=false)
ret = [@type1]
ret.push(@type2) if @type2!=@type1
# Burn Up erases the Fire-type.
if @effects[PBEffects::BurnUp]
ret.reject! { |type| isConst?(type,PBTypes,:FIRE) }
end
# Roost erases the Flying-type. If there are no types left, adds the Normal-
# type.
if @effects[PBEffects::Roost]
ret.reject! { |type| isConst?(type,PBTypes,:FLYING) }
ret.push(getConst(PBTypes,:NORMAL) || 0) if ret.length==0
end
# Add the third type specially.
if withType3 && @effects[PBEffects::Type3]>=0
ret.push(@effects[PBEffects::Type3]) if !ret.include?(@effects[PBEffects::Type3])
end
return ret
end
def pbHasType?(type)
type = getConst(PBTypes,type) if type.is_a?(Symbol) || type.is_a?(String)
return false if !type || type<0
activeTypes = pbTypes(true)
return activeTypes.include?(type)
end
def pbHasOtherType?(type)
type = getConst(PBTypes,type) if type.is_a?(Symbol) || type.is_a?(String)
return false if !type || type<0
activeTypes = pbTypes(true)
activeTypes.reject! { |t| t==type }
return activeTypes.length>0
end
# NOTE: Do not create any held item which affects whether a Pokémon's ability
# is active. The ability Klutz affects whether a Pokémon's item is
# active, and the code for the two combined would cause an infinite loop
# (regardless of whether any Pokémon actualy has either the ability or
# the item - the code existing is enough to cause the loop).
def abilityActive?(ignoreFainted=false)
return false if fainted? && !ignoreFainted
return false if @effects[PBEffects::GastroAcid]
return true
end
def hasActiveAbility?(ability,ignoreFainted=false)
return false if !abilityActive?(ignoreFainted)
if ability.is_a?(Array)
ability.each do |a|
a = getID(PBAbilities,a)
return true if a!=0 && a==@ability
end
return false
end
ability = getID(PBAbilities,ability)
return ability!=0 && ability==@ability
end
alias hasWorkingAbility hasActiveAbility?
def nonNegatableAbility?
abilityBlacklist = [
# Form-changing abilities
:BATTLEBOND,
:DISGUISE,
# :FLOWERGIFT, # This can be negated
# :FORECAST, # This can be negated
:MULTITYPE,
:POWERCONSTRUCT,
:SCHOOLING,
:SHIELDSDOWN,
:STANCECHANGE,
:ZENMODE,
# Abilities intended to be inherent properties of a certain species
:COMATOSE,
:RKSSYSTEM
]
failed = false
abilityBlacklist.each do |abil|
return true if isConst?(@ability,PBAbilities,abil)
end
return false
end
def itemActive?(ignoreFainted=false)
return false if fainted? && !ignoreFainted
return false if @effects[PBEffects::Embargo]>0
return false if @battle.field.effects[PBEffects::MagicRoom]>0
return false if hasActiveAbility?(:KLUTZ,ignoreFainted)
return true
end
def hasActiveItem?(item,ignoreFainted=false)
return false if !itemActive?(ignoreFainted)
if item.is_a?(Array)
item.each do |i|
i = getID(PBItems,i)
return true if i!=0 && i==@item
end
return false
end
item = getID(PBItems,item)
return item!=0 && item==@item
end
alias hasWorkingItem hasActiveItem?
# Returns whether the specified item will be unlosable for this Pokémon.
def unlosableItem?(item)
return false if item<=0
return true if pbIsMail?(item)
return false if @effects[PBEffects::Transform]
# Items that change a Pokémon's form
return true if @pokemon && @pokemon.getMegaForm(true)>0 # Mega Stone
return pbIsUnlosableItem?(item,@species,@ability)
end
def eachMove
@moves.each { |m| yield m if m && m.id!=0 }
end
def eachMoveWithIndex
@moves.each_with_index { |m,i| yield m,i if m && m.id!=0 }
end
def pbHasMove?(id)
id = getID(PBMoves,id)
return false if !id || id<=0
eachMove { |m| return true if m.id==id }
return false
end
def pbHasMoveType?(type)
type = getConst(PBTypes,type)
return false if !type || type<0
eachMove { |m| return true if m.type==type }
return false
end
def pbHasMoveFunction?(*arg)
return false if !code
eachMove do |m|
arg.each { |code| return true if m.function==code }
end
return false
end
def hasMoldBreaker?
return hasActiveAbility?([:MOLDBREAKER,:TERAVOLT,:TURBOBLAZE])
end
def canChangeType?
return false if isConst?(@ability,PBAbilities,:MULTITYPE) ||
isConst?(@ability,PBAbilities,:RKSSYSTEM)
return true
end
def airborne?
return false if hasActiveItem?(:IRONBALL)
return false if @effects[PBEffects::Ingrain]
return false if @effects[PBEffects::SmackDown]
return false if @battle.field.effects[PBEffects::Gravity]>0
return true if pbHasType?(:FLYING)
return true if hasActiveAbility?(:LEVITATE) && !@battle.moldBreaker
return true if hasActiveItem?(:AIRBALLOON)
return true if @effects[PBEffects::MagnetRise]>0
return true if @effects[PBEffects::Telekinesis]>0
return false
end
def affectedByTerrain?
return false if airborne?
return false if semiInvulnerable?
return true
end
def takesIndirectDamage?(showMsg=false)
return false if fainted?
if hasActiveAbility?(:MAGICGUARD)
if showMsg
@battle.pbShowAbilitySplash(self)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis))
else
@battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,abilityName))
end
@battle.pbHideAbilitySplash(self)
end
return false
end
return true
end
def takesSandstormDamage?
return false if !takesIndirectDamage?
return false if pbHasType?(:GROUND) || pbHasType?(:ROCK) || pbHasType?(:STEEL)
return false if inTwoTurnAttack?("0CA","0CB") # Dig, Dive
return false if hasActiveAbility?([:OVERCOAT,:SANDFORCE,:SANDRUSH,:SANDVEIL])
return false if hasActiveItem?(:SAFETYGOGGLES)
return true
end
def takesHailDamage?
return false if !takesIndirectDamage?
return false if pbHasType?(:ICE)
return false if inTwoTurnAttack?("0CA","0CB") # Dig, Dive
return false if hasActiveAbility?([:OVERCOAT,:ICEBODY,:SNOWCLOAK])
return false if hasActiveItem?(:SAFETYGOGGLES)
return true
end
def takesShadowSkyDamage?
return false if fainted?
return false if shadowPokemon?
return true
end
def affectedByPowder?(showMsg=false)
return false if fainted?
return true if !NEWEST_BATTLE_MECHANICS
if pbHasType?(:GRASS)
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMsg
return false
end
if hasActiveAbility?(:OVERCOAT) && !@battle.moldBreaker
if showMsg
@battle.pbShowAbilitySplash(self)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis))
else
@battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,abilityName))
end
@battle.pbHideAbilitySplash(self)
end
return false
end
if hasActiveItem?(:SAFETYGOGGLES)
if showMsg
@battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,itemName))
end
return false
end
return true
end
def canHeal?
return false if fainted? || @hp>=@totalhp
return false if @effects[PBEffects::HealBlock]>0
return true
end
def affectedByContactEffect?(showMsg=false)
return false if fainted?
if hasActiveItem?(:PROTECTIVEPADS)
@battle.pbDisplay(_INTL("{1} protected itself with the {2}!",pbThis,itemName)) if showMsg
return false
end
return true
end
def movedThisRound?
return @lastRoundMoved && @lastRoundMoved==@battle.turnCount
end
def usingMultiTurnAttack?
return true if @effects[PBEffects::TwoTurnAttack]>0
return true if @effects[PBEffects::HyperBeam]>0
return true if @effects[PBEffects::Rollout]>0
return true if @effects[PBEffects::Outrage]>0
return true if @effects[PBEffects::Uproar]>0
return true if @effects[PBEffects::Bide]>0
return false
end
def inTwoTurnAttack?(*arg)
return false if @effects[PBEffects::TwoTurnAttack]==0
ttaFunction = pbGetMoveData(@effects[PBEffects::TwoTurnAttack],MOVE_FUNCTION_CODE)
arg.each { |a| return true if a==ttaFunction }
return false
end
def semiInvulnerable?
return inTwoTurnAttack?("0C9","0CA","0CB","0CC","0CD","0CE","14D")
end
def pbEncoredMoveIndex
return -1 if @effects[PBEffects::Encore]==0 || @effects[PBEffects::EncoreMove]==0
ret = -1
eachMoveWithIndex do |m,i|
next if m.id!=@effects[PBEffects::EncoreMove]
ret = i
break
end
return ret
end
def initialItem
return @battle.initialItems[@index&1][@pokemonIndex]
end
def setInitialItem(newItem)
@battle.initialItems[@index&1][@pokemonIndex] = newItem
end
def recycleItem
return @battle.recycleItems[@index&1][@pokemonIndex]
end
def setRecycleItem(newItem)
@battle.recycleItems[@index&1][@pokemonIndex] = newItem
end
def belched?
return @battle.belch[@index&1][@pokemonIndex]
end
def setBelched
@battle.belch[@index&1][@pokemonIndex] = true
end
#=============================================================================
# Methods relating to this battler's position on the battlefield
#=============================================================================
# Returns whether the given position belongs to the opposing Pokémon's side.
def opposes?(i=0)
i = i.index if i.respond_to?("index")
return (@index&1)!=(i&1)
end
# Returns whether the given position/battler is near to self.
def near?(i)
i = i.index if i.respond_to?("index")
return @battle.nearBattlers?(@index,i)
end
# Returns whether self is owned by the player.
def pbOwnedByPlayer?
return @battle.pbOwnedByPlayer?(@index)
end
# Returns 0 if self is on the player's side, or 1 if self is on the opposing
# side.
def idxOwnSide
return @index&1
end
# Returns 1 if self is on the player's side, or 0 if self is on the opposing
# side.
def idxOpposingSide
return (@index&1)^1
end
# Returns the data structure for this battler's side.
def pbOwnSide
return @battle.sides[idxOwnSide]
end
# Returns the data structure for the opposing Pokémon's side.
def pbOpposingSide
return @battle.sides[idxOpposingSide]
end
# Yields each unfainted ally Pokémon.
def eachAlly
@battle.battlers.each do |b|
yield b if b && !b.fainted? && !b.opposes?(@index) && b.index!=@index
end
end
# Yields each unfainted opposing Pokémon.
def eachOpposing
@battle.battlers.each { |b| yield b if b && !b.fainted? && b.opposes?(@index) }
end
# Returns the battler that is most directly opposite to self. unfaintedOnly is
# whether it should prefer to return a non-fainted battler.
def pbDirectOpposing(unfaintedOnly=false)
@battle.pbGetOpposingIndicesInOrder(@index).each do |i|
next if !@battle.battlers[i]
break if unfaintedOnly && @battle.battlers[i].fainted?
return @battle.battlers[i]
end
# Wanted an unfainted battler but couldn't find one; make do with a fainted
# battler
@battle.pbGetOpposingIndicesInOrder(@index).each do |i|
return @battle.battlers[i] if @battle.battlers[i]
end
return @battle.battlers[(@index^1)]
end
end

View File

@@ -0,0 +1,326 @@
class PokeBattle_Battler
#=============================================================================
# Creating a battler
#=============================================================================
def initialize(btl,idxBattler)
@battle = btl
@index = idxBattler
@captured = false
@dummy = false
@stages = []
@effects = []
@damageState = PokeBattle_DamageState.new
pbInitBlank
pbInitEffects(false)
end
def pbInitBlank
@name = ""
@species = 0
@form = 0
@level = 0
@hp = @totalhp = 0
@type1 = @type2 = 0
@ability = 0
@item = 0
@gender = 0
@attack = @defense = @spatk = @spdef = @speed = 0
@status = PBStatuses::NONE
@statusCount = 0
@pokemon = nil
@pokemonIndex = -1
@participants = []
@moves = []
@iv = [0,0,0,0,0,0]
end
# Used by Future Sight only, when Future Sight's user is no longer in battle.
def pbInitDummyPokemon(pkmn,idxParty)
raise _INTL("An egg can't be an active Pokémon.") if pkmn.egg?
@name = pkmn.name
@species = pkmn.species
@form = pkmn.form
@level = pkmn.level
@hp = pkmn.hp
@totalhp = pkmn.totalhp
@type1 = pkmn.type1
@type2 = pkmn.type2
# ability and item intentionally not copied across here
@gender = pkmn.gender
@attack = pkmn.attack
@defense = pkmn.defense
@spatk = pkmn.spatk
@spdef = pkmn.spdef
@speed = pkmn.speed
@status = pkmn.status
@statusCount = pkmn.statusCount
@pokemon = pkmn
@pokemonIndex = idxParty
@participants = []
# moves intentionally not copied across here
@iv = pkmn.iv.clone
@dummy = true
end
def pbInitialize(pkmn,idxParty,batonPass=false)
pbInitPokemon(pkmn,idxParty)
pbInitEffects(batonPass)
end
def pbInitPokemon(pkmn,idxParty)
raise _INTL("An egg can't be an active Pokémon.") if pkmn.egg?
@name = pkmn.name
@species = pkmn.species
@form = pkmn.form
@level = pkmn.level
@hp = pkmn.hp
@totalhp = pkmn.totalhp
@type1 = pkmn.type1
@type2 = pkmn.type2
@ability = pkmn.ability
@item = pkmn.item
@gender = pkmn.gender
@attack = pkmn.attack
@defense = pkmn.defense
@spatk = pkmn.spatk
@spdef = pkmn.spdef
@speed = pkmn.speed
@status = pkmn.status
@statusCount = pkmn.statusCount
@pokemon = pkmn
@pokemonIndex = idxParty
@participants = [] # Participants earn Exp. if this battler is defeated
@moves = []
pkmn.moves.each_with_index do |m,i|
@moves[i] = PokeBattle_Move.pbFromPBMove(@battle,m)
end
@iv = pkmn.iv.clone
end
def pbInitEffects(batonPass)
if batonPass
# These effects are passed on if Baton Pass is used, but they need to be
# reapplied
@effects[PBEffects::LaserFocus] = (@effects[PBEffects::LaserFocus]>0) ? 2 : 0
@effects[PBEffects::LockOn] = (@effects[PBEffects::LockOn]>0) ? 2 : 0
if @effects[PBEffects::PowerTrick]
@attack,@defense = @defense,@attack
end
# These effects are passed on if Baton Pass is used, but they need to be
# cancelled in certain circumstances anyway
@effects[PBEffects::Telekinesis] = 0 if isConst?(@species,PBSpecies,:GENGAR) && mega?
@effects[PBEffects::GastroAcid] = false if nonNegatableAbility?
else
# These effects are passed on if Baton Pass is used
@stages[PBStats::ATTACK] = 0
@stages[PBStats::DEFENSE] = 0
@stages[PBStats::SPEED] = 0
@stages[PBStats::SPATK] = 0
@stages[PBStats::SPDEF] = 0
@stages[PBStats::EVASION] = 0
@stages[PBStats::ACCURACY] = 0
@effects[PBEffects::AquaRing] = false
@effects[PBEffects::Confusion] = 0
@effects[PBEffects::Curse] = false
@effects[PBEffects::Embargo] = 0
@effects[PBEffects::FocusEnergy] = 0
@effects[PBEffects::GastroAcid] = false
@effects[PBEffects::HealBlock] = 0
@effects[PBEffects::Ingrain] = false
@effects[PBEffects::LaserFocus] = 0
@effects[PBEffects::LeechSeed] = -1
@effects[PBEffects::LockOn] = 0
@effects[PBEffects::LockOnPos] = -1
@effects[PBEffects::MagnetRise] = 0
@effects[PBEffects::PerishSong] = 0
@effects[PBEffects::PerishSongUser] = -1
@effects[PBEffects::PowerTrick] = false
@effects[PBEffects::Substitute] = 0
@effects[PBEffects::Telekinesis] = 0
end
@damageState.reset
@fainted = (@hp==0)
@initialHP = 0
@lastAttacker = []
@lastFoeAttacker = []
@lastHPLost = 0
@lastHPLostFromFoe = 0
@tookDamage = false
@tookPhysicalHit = false
@lastMoveUsed = -1
@lastMoveUsedType = -1
@lastRegularMoveUsed = -1
@lastRegularMoveTarget = -1
@lastRoundMoved = -1
@lastMoveFailed = false
@lastRoundMoveFailed = false
@movesUsed = []
@turnCount = 0
@effects[PBEffects::Attract] = -1
@battle.eachBattler do |b| # Other battlers no longer attracted to self
b.effects[PBEffects::Attract] = -1 if b.effects[PBEffects::Attract]==@index
end
@effects[PBEffects::BanefulBunker] = false
@effects[PBEffects::BeakBlast] = false
@effects[PBEffects::Bide] = 0
@effects[PBEffects::BideDamage] = 0
@effects[PBEffects::BideTarget] = -1
@effects[PBEffects::BurnUp] = false
@effects[PBEffects::Charge] = 0
@effects[PBEffects::ChoiceBand] = -1
@effects[PBEffects::Counter] = -1
@effects[PBEffects::CounterTarget] = -1
@effects[PBEffects::Dancer] = false
@effects[PBEffects::DefenseCurl] = false
@effects[PBEffects::DestinyBond] = false
@effects[PBEffects::DestinyBondPrevious] = false
@effects[PBEffects::DestinyBondTarget] = -1
@effects[PBEffects::Disable] = 0
@effects[PBEffects::DisableMove] = 0
@effects[PBEffects::Electrify] = false
@effects[PBEffects::Encore] = 0
@effects[PBEffects::EncoreMove] = 0
@effects[PBEffects::Endure] = false
@effects[PBEffects::FirstPledge] = 0
@effects[PBEffects::FlashFire] = false
@effects[PBEffects::Flinch] = false
@effects[PBEffects::FocusPunch] = false
@effects[PBEffects::FollowMe] = 0
@effects[PBEffects::Foresight] = false
@effects[PBEffects::FuryCutter] = 0
@effects[PBEffects::GemConsumed] = 0
@effects[PBEffects::Grudge] = false
@effects[PBEffects::HelpingHand] = false
@effects[PBEffects::HyperBeam] = 0
@effects[PBEffects::Illusion] = nil
if hasActiveAbility?(:ILLUSION)
idxLastParty = @battle.pbLastInTeam(@index)
if idxLastParty!=@pokemonIndex
@effects[PBEffects::Illusion] = @battle.pbParty(@index)[idxLastParty]
end
end
@effects[PBEffects::Imprison] = false
@effects[PBEffects::Instruct] = false
@effects[PBEffects::Instructed] = false
@effects[PBEffects::KingsShield] = false
@battle.eachBattler do |b| # Other battlers lose their lock-on against self
next if b.effects[PBEffects::LockOn]==0
next if b.effects[PBEffects::LockOnPos]!=@index
b.effects[PBEffects::LockOn] = 0
b.effects[PBEffects::LockOnPos] = -1
end
@effects[PBEffects::MagicBounce] = false
@effects[PBEffects::MagicCoat] = false
@effects[PBEffects::MeanLook] = -1
@battle.eachBattler do |b| # Other battlers no longer blocked by self
b.effects[PBEffects::MeanLook] = -1 if b.effects[PBEffects::MeanLook]==@index
end
@effects[PBEffects::MeFirst] = false
@effects[PBEffects::Metronome] = 0
@effects[PBEffects::MicleBerry] = false
@effects[PBEffects::Minimize] = false
@effects[PBEffects::MiracleEye] = false
@effects[PBEffects::MirrorCoat] = -1
@effects[PBEffects::MirrorCoatTarget] = -1
@effects[PBEffects::MoveNext] = false
@effects[PBEffects::MudSport] = false
@effects[PBEffects::Nightmare] = false
@effects[PBEffects::Outrage] = 0
@effects[PBEffects::ParentalBond] = 0
@effects[PBEffects::PickupItem] = 0
@effects[PBEffects::PickupUse] = 0
@effects[PBEffects::Pinch] = false
@effects[PBEffects::Powder] = false
@effects[PBEffects::Prankster] = false
@effects[PBEffects::PriorityAbility] = false
@effects[PBEffects::PriorityItem] = false
@effects[PBEffects::Protect] = false
@effects[PBEffects::ProtectRate] = 1
@effects[PBEffects::Pursuit] = false
@effects[PBEffects::Quash] = 0
@effects[PBEffects::Rage] = false
@effects[PBEffects::RagePowder] = false
@effects[PBEffects::Revenge] = 0
@effects[PBEffects::Rollout] = 0
@effects[PBEffects::Roost] = false
@effects[PBEffects::SkyDrop] = -1
@battle.eachBattler do |b| # Other battlers no longer Sky Dropped by self
b.effects[PBEffects::SkyDrop] = -1 if b.effects[PBEffects::SkyDrop]==@index
end
@effects[PBEffects::SlowStart] = 0
@effects[PBEffects::SmackDown] = false
@effects[PBEffects::Snatch] = 0
@effects[PBEffects::SpikyShield] = false
@effects[PBEffects::Spotlight] = 0
@effects[PBEffects::Stockpile] = 0
@effects[PBEffects::StockpileDef] = 0
@effects[PBEffects::StockpileSpDef] = 0
@effects[PBEffects::Taunt] = 0
@effects[PBEffects::ThroatChop] = 0
@effects[PBEffects::Torment] = false
@effects[PBEffects::Toxic] = 0
@effects[PBEffects::Transform] = false
@effects[PBEffects::TransformSpecies] = 0
@effects[PBEffects::Trapping] = 0
@effects[PBEffects::TrappingMove] = 0
@effects[PBEffects::TrappingUser] = -1
@battle.eachBattler do |b| # Other battlers no longer trapped by self
next if b.effects[PBEffects::TrappingUser]!=@index
b.effects[PBEffects::Trapping] = 0
b.effects[PBEffects::TrappingUser] = -1
end
@effects[PBEffects::Truant] = false
@effects[PBEffects::TwoTurnAttack] = 0
@effects[PBEffects::Type3] = -1
@effects[PBEffects::Unburden] = false
@effects[PBEffects::Uproar] = 0
@effects[PBEffects::WaterSport] = false
@effects[PBEffects::WeightChange] = 0
@effects[PBEffects::Yawn] = 0
end
#=============================================================================
# Refreshing a battler's properties
#=============================================================================
def pbUpdate(fullChange=false)
return if !@pokemon
@pokemon.calcStats
@level = @pokemon.level
@hp = @pokemon.hp
@totalhp = @pokemon.totalhp
if !@effects[PBEffects::Transform]
@attack = @pokemon.attack
@defense = @pokemon.defense
@spatk = @pokemon.spatk
@spdef = @pokemon.spdef
@speed = @pokemon.speed
if fullChange
@type1 = @pokemon.type1
@type2 = @pokemon.type2
@ability = @pokemon.ability
end
end
end
# Used only to erase the battler of a Shadow Pokémon that has been snagged.
def pbReset
@pokemon = nil
@pokemonIndex = -1
@hp = 0
pbInitEffects(false)
@participants = []
# Reset status
@status = PBStatuses::NONE
@statusCount = 0
# Reset choice
@battle.pbClearChoice(@index)
end
# Update which Pokémon will gain Exp if this battler is defeated.
def pbUpdateParticipants
return if fainted? || !@battle.opposes?(@index)
eachOpposing do |b|
@participants.push(b.pokemonIndex) if !@participants.include?(b.pokemonIndex)
end
end
end

View File

@@ -0,0 +1,301 @@
class PokeBattle_Battler
#=============================================================================
# Change HP
#=============================================================================
def pbReduceHP(amt,anim=true,registerDamage=true,anyAnim=true)
amt = amt.round
amt = @hp if amt>@hp
amt = 1 if amt<1 && !fainted?
oldHP = @hp
self.hp -= amt
PBDebug.log("[HP change] #{pbThis} lost #{amt} HP (#{oldHP}=>#{@hp})")
raise _INTL("HP less than 0") if @hp<0
raise _INTL("HP greater than total HP") if @hp>@totalhp
@battle.scene.pbHPChanged(self,oldHP,anim) if anyAnim && amt>0
@tookDamage = true if amt>0 && registerDamage
return amt
end
def pbRecoverHP(amt,anim=true,anyAnim=true)
amt = amt.round
amt = @totalhp-@hp if amt>@totalhp-@hp
amt = 1 if amt<1 && @hp<@totalhp
oldHP = @hp
self.hp += amt
PBDebug.log("[HP change] #{pbThis} gained #{amt} HP (#{oldHP}=>#{@hp})")
raise _INTL("HP less than 0") if @hp<0
raise _INTL("HP greater than total HP") if @hp>@totalhp
@battle.scene.pbHPChanged(self,oldHP,anim) if anyAnim && amt>0
return amt
end
def pbRecoverHPFromDrain(amt,target,msg=nil)
if target.hasActiveAbility?(:LIQUIDOOZE)
oldHP = @hp
@battle.pbShowAbilitySplash(target)
pbReduceHP(amt)
@battle.pbDisplay(_INTL("{1} sucked up the liquid ooze!",pbThis))
@battle.pbHideAbilitySplash(target)
pbItemHPHealCheck
else
msg = _INTL("{1} had its energy drained!",target.pbThis) if !msg || msg==""
@battle.pbDisplay(msg)
if canHeal?
amt = (amt*1.3).floor if hasActiveItem?(:BIGROOT)
pbRecoverHP(amt)
end
end
end
def pbFaint(showMessage=true)
if !fainted?
PBDebug.log("!!!***Can't faint with HP greater than 0")
return
end
return if @fainted # Has already fainted properly
@battle.pbDisplayBrief(_INTL("{1} fainted!",pbThis)) if showMessage
PBDebug.log("[Pokémon fainted] #{pbThis} (#{@index})") if !showMessage
@battle.scene.pbFaintBattler(self)
pbInitEffects(false)
# Reset status
self.status = PBStatuses::NONE
self.statusCount = 0
# Lose happiness
if @pokemon && @battle.internalBattle
badLoss = false
@battle.eachOtherSideBattler(@index) do |b|
badLoss = true if b.level>=self.level+30
end
@pokemon.changeHappiness((badLoss) ? "faintbad" : "faint")
end
# Reset form
@battle.peer.pbOnLeavingBattle(@battle,@pokemon,@battle.usedInBattle[idxOwnSide][@index/2])
@pokemon.makeUnmega if mega?
@pokemon.makeUnprimal if primal?
# Do other things
@battle.pbClearChoice(@index) # Reset choice
pbOwnSide.effects[PBEffects::LastRoundFainted] = @battle.turnCount
# Check other battlers' abilities that trigger upon a battler fainting
pbAbilitiesOnFainting
# Check for end of primordial weather
@battle.pbEndPrimordialWeather
end
#=============================================================================
# Move PP
#=============================================================================
def pbSetPP(move,pp)
move.pp = pp
# No need to care about @effects[PBEffects::Mimic], since Mimic can't copy
# Mimic
if move.realMove && move.id==move.realMove.id && !@effects[PBEffects::Transform]
move.realMove.pp = pp
end
end
def pbReducePP(move)
return true if usingMultiTurnAttack?
return true if move.pp<0 # Don't reduce PP for special calls of moves
return true if move.totalpp<=0 # Infinite PP, can always be used
return false if move.pp==0 # Ran out of PP, couldn't reduce
pbSetPP(move,move.pp-1) if move.pp>0
return true
end
def pbReducePPOther(move)
pbSetPP(move,move.pp-1) if move.pp>0
end
#=============================================================================
# Change type
#=============================================================================
def pbChangeTypes(newType)
if newType.is_a?(PokeBattle_Battler)
newTypes = newType.pbTypes
newTypes.push(getConst(PBTypes,:NORMAL) || 0) if newTypes.length==0
newType3 = newType.effects[PBEffects::Type3]
newType3 = -1 if newTypes.include?(newType3)
@type1 = newTypes[0]
@type2 = (newTypes.length==1) ? newTypes[0] : newTypes[1]
@effects[PBEffects::Type3] = newType3
else
newType = getConst(PBTypes,newType) if newType.is_a?(Symbol) || newType.is_a?(String)
@type1 = newType
@type2 = newType
@effects[PBEffects::Type3] = -1
end
@effects[PBEffects::BurnUp] = false
@effects[PBEffects::Roost] = false
end
#=============================================================================
# Forms
#=============================================================================
def pbChangeForm(newForm,msg)
return if fainted? || @effects[PBEffects::Transform] || @form==newForm
oldForm = @form
oldDmg = @totalhp-@hp
self.form = newForm
pbUpdate(true)
@hp = @totalhp-oldDmg
@effects[PBEffects::WeightChange] = 0 if NEWEST_BATTLE_MECHANICS
@battle.scene.pbChangePokemon(self,@pokemon)
@battle.scene.pbRefreshOne(@index)
@battle.pbDisplay(msg) if msg && msg!=""
PBDebug.log("[Form changed] #{pbThis} changed from form #{oldForm} to form #{newForm}")
@battle.pbSetSeen(self)
end
def pbCheckFormOnStatusChange
return if fainted? || @effects[PBEffects::Transform]
# Shaymin - reverts if frozen
if isConst?(@species,PBSpecies,:SHAYMIN) && frozen?
pbChangeForm(0,_INTL("{1} transformed!",pbThis))
end
end
def pbCheckFormOnMovesetChange
return if fainted? || @effects[PBEffects::Transform]
# Keldeo - knowing Secret Sword
if isConst?(@species,PBSpecies,:KELDEO)
newForm = 0
newForm = 1 if pbHasMove?(:SECRETSWORD)
pbChangeForm(newForm,_INTL("{1} transformed!",pbThis))
end
end
def pbCheckFormOnWeatherChange
return if fainted? || @effects[PBEffects::Transform]
# Castform - Forecast
if isConst?(@species,PBSpecies,:CASTFORM)
if hasActiveAbility?(:FORECAST)
newForm = 0
case @battle.pbWeather
when PBWeather::Sun, PBWeather::HarshSun; newForm = 1
when PBWeather::Rain, PBWeather::HeavyRain; newForm = 2
when PBWeather::Hail; newForm = 3
end
if @form!=newForm
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(newForm,_INTL("{1} transformed!",pbThis))
end
else
pbChangeForm(0,_INTL("{1} transformed!",pbThis))
end
end
# Cherrim - Flower Gift
if isConst?(@species,PBSpecies,:CHERRIM)
if hasActiveAbility?(:FLOWERGIFT)
newForm = 0
case @battle.pbWeather
when PBWeather::Sun, PBWeather::HarshSun; newForm = 1
end
if @form!=newForm
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(newForm,_INTL("{1} transformed!",pbThis))
end
else
pbChangeForm(0,_INTL("{1} transformed!",pbThis))
end
end
end
# Checks the Pokémon's form and updates it if necessary. Used for when a
# Pokémon enters battle (endOfRound=false) and at the end of each round
# (endOfRound=true).
def pbCheckForm(endOfRound=false)
return if fainted? || @effects[PBEffects::Transform]
# Form changes upon entering battle and when the weather changes
pbCheckFormOnWeatherChange if !endOfRound
# Darmanitan - Zen Mode
if isConst?(@species,PBSpecies,:DARMANITAN) && isConst?(@ability,PBAbilities,:ZENMODE)
if @hp<=@totalhp/2
if @form!=1
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(1,_INTL("{1} triggered!",abilityName))
end
elsif @form!=0
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(0,_INTL("{1} triggered!",abilityName))
end
end
# Minior - Shields Down
if isConst?(@species,PBSpecies,:MINIOR) && isConst?(@ability,PBAbilities,:SHIELDSDOWN)
if @hp>@totalhp/2 # Turn into Meteor form
newForm = (@form>=7) ? @form-7 : @form
if @form!=newForm
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(newForm,_INTL("{1} deactivated!",abilityName))
elsif !endOfRound
@battle.pbDisplay(_INTL("{1} deactivated!",abilityName))
end
elsif @form<7 # Turn into Core form
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(@form+7,_INTL("{1} activated!",abilityName))
end
end
# Wishiwashi - Schooling
if isConst?(@species,PBSpecies,:WISHIWASHI) && isConst?(@ability,PBAbilities,:SCHOOLING)
if @level>=20 && @hp>@totalhp/4
if @form!=1
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(1,_INTL("{1} formed a school!",pbThis))
end
elsif @form!=0
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(0,_INTL("{1} stopped schooling!",pbThis))
end
end
# Zygarde - Power Construct
if isConst?(@species,PBSpecies,:ZYGARDE) && isConst?(@ability,PBAbilities,:POWERCONSTRUCT) &&
endOfRound
if @hp<=@totalhp/2 && @form<2 # Turn into Complete Forme
newForm = @form+2
@battle.pbDisplay(_INTL("You sense the presence of many!"))
@battle.pbShowAbilitySplash(self,true)
@battle.pbHideAbilitySplash(self)
pbChangeForm(newForm,_INTL("{1} transformed into its Complete Forme!",pbThis))
end
end
end
def pbTransform(target)
oldAbil = @ability
@effects[PBEffects::Transform] = true
@effects[PBEffects::TransformSpecies] = target.species
pbChangeTypes(target)
@ability = target.ability
@attack = target.attack
@defense = target.defense
@spatk = target.spatk
@spdef = target.spdef
@speed = target.speed
PBStats.eachBattleStat { |s| @stages[s] = target.stages[s] }
if NEWEST_BATTLE_MECHANICS
@effects[PBEffects::FocusEnergy] = target.effects[PBEffects::FocusEnergy]
@effects[PBEffects::LaserFocus] = target.effects[PBEffects::LaserFocus]
end
@moves.clear
target.moves.each_with_index do |m,i|
@moves[i] = PokeBattle_Move.pbFromPBMove(@battle,PBMove.new(m.id))
@moves[i].pp = 5
@moves[i].totalpp = 5
end
@effects[PBEffects::Disable] = 0
@effects[PBEffects::DisableMove] = 0
@effects[PBEffects::WeightChange] = target.effects[PBEffects::WeightChange]
@battle.scene.pbRefreshOne(@index)
@battle.pbDisplay(_INTL("{1} transformed into {2}!",pbThis,target.pbThis(true)))
pbOnAbilityChanged(oldAbil)
end
def pbHyperMode; end
end

View File

@@ -0,0 +1,573 @@
class PokeBattle_Battler
#=============================================================================
# Generalised checks for whether a status problem can be inflicted
#=============================================================================
# NOTE: Not all "does it have this status?" checks use this method. If the
# check is leading up to curing self of that status condition, then it
# will look at the value of @status directly instead - if it is that
# PBStatuses value then it is curable. This method only checks for
# "counts as having that status", which includes Comatose which can't be
# cured.
def pbHasStatus?(checkStatus)
if BattleHandlers.triggerStatusCheckAbilityNonIgnorable(@ability,self,checkStatus)
return true
end
return @status==checkStatus
end
def pbHasAnyStatus?
if BattleHandlers.triggerStatusCheckAbilityNonIgnorable(@ability,self,nil)
return true
end
return @status!=PBStatuses::NONE
end
def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false)
return false if fainted?
selfInflicted = (user && user.index==@index)
# Already have that status problem
if self.status==newStatus && !ignoreStatus
if showMessages
msg = ""
case self.status
when PBStatuses::SLEEP; msg = _INTL("{1} is already asleep!",pbThis)
when PBStatuses::POISON; msg = _INTL("{1} is already poisoned!",pbThis)
when PBStatuses::BURN; msg = _INTL("{1} already has a burn!",pbThis)
when PBStatuses::PARALYSIS; msg = _INTL("{1} is already paralyzed!",pbThis)
when PBStatuses::FROZEN; msg = _INTL("{1} is already frozen solid!",pbThis)
end
@battle.pbDisplay(msg)
end
return false
end
# Trying to replace a status problem with another one
if self.status!=PBStatuses::NONE && !ignoreStatus && !selfInflicted
@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
@battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages
return false
end
# Weather immunity
if newStatus==PBStatuses::FROZEN &&
(@battle.pbWeather==PBWeather::Sun || @battle.pbWeather==PBWeather::HarshSun)
@battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages
return false
end
# Terrains immunity
if affectedByTerrain?
case @battle.field.terrain
when PBBattleTerrains::Electric
if newStatus==PBStatuses::SLEEP
@battle.pbDisplay(_INTL("{1} surrounds itself with electrified terrain!",
pbThis(true))) if showMessages
return false
end
when PBBattleTerrains::Misty
@battle.pbDisplay(_INTL("{1} surrounds itself with misty terrain!",pbThis(true))) if showMessages
return false
end
end
# Uproar immunity
if newStatus==PBStatuses::SLEEP &&
!(hasActiveAbility?(:SOUNDPROOF) && !@battle.moldBreaker)
@battle.eachBattler do |b|
next if b.effects[PBEffects::Uproar]==0
@battle.pbDisplay(_INTL("But the uproar kept {1} awake!",pbThis(true))) if showMessages
return false
end
end
# Type immunities
hasImmuneType = false
case newStatus
when PBStatuses::SLEEP
# No type is immune to sleep
when PBStatuses::POISON
if !(user && user.hasActiveAbility?(:CORROSION))
hasImmuneType |= pbHasType?(:POISON)
hasImmuneType |= pbHasType?(:STEEL)
end
when PBStatuses::BURN
hasImmuneType |= pbHasType?(:FIRE)
when PBStatuses::PARALYSIS
hasImmuneType |= pbHasType?(:ELECTRIC) && NEWEST_BATTLE_MECHANICS
when PBStatuses::FROZEN
hasImmuneType |= pbHasType?(:ICE)
end
if hasImmuneType
@battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages
return false
end
# Ability immunity
immuneByAbility = false; immAlly = nil
if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(@ability,self,newStatus)
immuneByAbility = true
elsif selfInflicted || !@battle.moldBreaker
if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(@ability,self,newStatus)
immuneByAbility = true
else
eachAlly do |b|
next if !b.abilityActive?
next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability,self,newStatus)
immuneByAbility = true
immAlly = b
break
end
end
end
if immuneByAbility
if showMessages
@battle.pbShowAbilitySplash(immAlly || self)
msg = ""
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
case newStatus
when PBStatuses::SLEEP; msg = _INTL("{1} stays awake!",pbThis)
when PBStatuses::POISON; msg = _INTL("{1} cannot be poisoned!",pbThis)
when PBStatuses::BURN; msg = _INTL("{1} cannot be burned!",pbThis)
when PBStatuses::PARALYSIS; msg = _INTL("{1} cannot be paralyzed!",pbThis)
when PBStatuses::FROZEN; msg = _INTL("{1} cannot be frozen solid!",pbThis)
end
elsif immAlly
case newStatus
when PBStatuses::SLEEP
msg = _INTL("{1} stays awake because of {2}'s {3}!",
pbThis,immAlly.pbThis(true),immAlly.abilityName)
when PBStatuses::POISON
msg = _INTL("{1} cannot be poisoned because of {2}'s {3}!",
pbThis,immAlly.pbThis(true),immAlly.abilityName)
when PBStatuses::BURN
msg = _INTL("{1} cannot be burned because of {2}'s {3}!",
pbThis,immAlly.pbThis(true),immAlly.abilityName)
when PBStatuses::PARALYSIS
msg = _INTL("{1} cannot be paralyzed because of {2}'s {3}!",
pbThis,immAlly.pbThis(true),immAlly.abilityName)
when PBStatuses::FROZEN
msg = _INTL("{1} cannot be frozen solid because of {2}'s {3}!",
pbThis,immAlly.pbThis(true),immAlly.abilityName)
end
else
case newStatus
when PBStatuses::SLEEP; msg = _INTL("{1} stays awake because of its {2}!",pbThis,abilityName)
when PBStatuses::POISON; msg = _INTL("{1}'s {2} prevents poisoning!",pbThis,abilityName)
when PBStatuses::BURN; msg = _INTL("{1}'s {2} prevents burns!",pbThis,abilityName)
when PBStatuses::PARALYSIS; msg = _INTL("{1}'s {2} prevents paralysis!",pbThis,abilityName)
when PBStatuses::FROZEN; msg = _INTL("{1}'s {2} prevents freezing!",pbThis,abilityName)
end
end
@battle.pbDisplay(msg)
@battle.pbHideAbilitySplash(immAlly || self)
end
return false
end
# Safeguard immunity
if pbOwnSide.effects[PBEffects::Safeguard]>0 && !selfInflicted && move &&
!(user && user.hasActiveAbility?(:INFILTRATOR))
@battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!",pbThis)) if showMessages
return false
end
return true
end
def pbCanSynchronizeStatus?(status,target)
return false if fainted?
# Trying to replace a status problem with another one
return false if self.status!=PBStatuses::NONE
# Terrain immunity
return false if @battle.field.terrain==PBBattleTerrains::Misty && affectedByTerrain?
# Type immunities
hasImmuneType = false
case self.status
when PBStatuses::POISON
# NOTE: target will have Synchronize, so it can't have Corrosion.
if !(target && target.hasActiveAbility?(:CORROSION))
hasImmuneType |= pbHasType?(:POISON)
hasImmuneType |= pbHasType?(:STEEL)
end
when PBStatuses::BURN
hasImmuneType |= pbHasType?(:FIRE)
when PBStatuses::PARALYSIS
hasImmuneType |= pbHasType?(:ELECTRIC) && NEWEST_BATTLE_MECHANICS
end
return false if hasImmuneType
# Ability immunity
if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(@ability,self,status)
return false
end
if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(@ability,self,status)
return false
end
eachAlly do |b|
next if !b.abilityActive?
next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability,self,status)
return false
end
# Safeguard immunity
if pbOwnSide.effects[PBEffects::Safeguard]>0 &&
!(user && user.hasActiveAbility?(:INFILTRATOR))
return false
end
return true
end
#=============================================================================
# Generalised infliction of status problem
#=============================================================================
def pbInflictStatus(newStatus,newStatusCount=0,msg=nil,user=nil)
# Inflict the new status
self.status = newStatus
self.statusCount = newStatusCount
@effects[PBEffects::Toxic] = 0
# Record status change in debug log, generate default message, show animation
case newStatus
when PBStatuses::SLEEP
@battle.pbCommonAnimation("Sleep",self)
msg = _INTL("{1} fell asleep!",pbThis) if !msg || msg==""
when PBStatuses::POISON
if newStatusCount>0
@battle.pbCommonAnimation("Toxic",self)
msg = _INTL("{1} was badly poisoned!",pbThis) if !msg || msg==""
else
@battle.pbCommonAnimation("Poison",self)
msg = _INTL("{1} was poisoned!",pbThis) if !msg || msg==""
end
when PBStatuses::BURN
@battle.pbCommonAnimation("Burn",self)
msg = _INTL("{1} was burned!",pbThis) if !msg || msg==""
when PBStatuses::PARALYSIS
@battle.pbCommonAnimation("Paralysis",self)
msg = _INTL("{1} is paralyzed! It may be unable to move!",pbThis) if !msg || msg==""
when PBStatuses::FROZEN
@battle.pbCommonAnimation("Frozen",self)
msg = _INTL("{1} was frozen solid!",pbThis) if !msg || msg==""
end
# Show message
@battle.pbDisplay(msg) if msg && msg!=""
PBDebug.log("[Status change] #{pbThis}'s sleep count is #{newStatusCount}") if newStatus==PBStatuses::SLEEP
pbCheckFormOnStatusChange
# Synchronize
if abilityActive?
BattleHandlers.triggerAbilityOnStatusInflicted(@ability,self,user,newStatus)
end
# Status cures
pbItemStatusCureCheck
pbAbilityStatusCureCheck
# Petal Dance/Outrage/Thrash get cancelled immediately by falling asleep
# NOTE: I don't know why this applies only to Outrage and only to falling
# asleep (i.e. it doesn't cancel Rollout/Uproar/other multi-turn
# moves, and it doesn't cancel any moves if self becomes frozen/
# disabled/anything else). This behaviour was tested in Gen 5.
if @status==PBStatuses::SLEEP && @effects[PBEffects::Outrage]>0
@effects[PBEffects::Outrage] = 0
@currentMove = 0
end
end
#=============================================================================
# Sleep
#=============================================================================
def asleep?
return pbHasStatus?(PBStatuses::SLEEP)
end
def pbCanSleep?(user,showMessages,move=nil,ignoreStatus=false)
return pbCanInflictStatus?(PBStatuses::SLEEP,user,showMessages,move,ignoreStatus)
end
def pbCanSleepYawn?
return false if self.status!=PBStatuses::NONE
if affectedByTerrain?
return false if @battle.field.terrain==PBBattleTerrains::Electric
return false if @battle.field.terrain==PBBattleTerrains::Misty
end
if !hasActiveAbility?(:SOUNDPROOF)
@battle.eachBattler do |b|
return false if b.effects[PBEffects::Uproar]>0
end
end
if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(@ability,self,PBStatuses::SLEEP)
return false
end
# NOTE: Bulbapedia claims that Flower Veil shouldn't prevent sleep due to
# drowsiness, but I disagree because that makes no sense. Also, the
# comparable Sweet Veil does prevent sleep due to drowsiness.
if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(@ability,self,PBStatuses::SLEEP)
return false
end
eachAlly do |b|
next if !b.abilityActive?
next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability,self,PBStatuses::SLEEP)
return false
end
# NOTE: Bulbapedia claims that Safeguard shouldn't prevent sleep due to
# drowsiness. I disagree with this too. Compare with the other sided
# effects Misty/Electric Terrain, which do prevent it.
return false if pbOwnSide.effects[PBEffects::Safeguard]>0
return true
end
def pbSleep(msg=nil)
pbInflictStatus(PBStatuses::SLEEP,pbSleepDuration,msg)
end
def pbSleepSelf(msg=nil,duration=-1)
pbInflictStatus(PBStatuses::SLEEP,pbSleepDuration(duration),msg)
end
def pbSleepDuration(duration=-1)
duration = 2+@battle.pbRandom(3) if duration<=0
duration = (duration/2).floor if hasActiveAbility?(:EARLYBIRD)
return duration
end
#=============================================================================
# Poison
#=============================================================================
def poisoned?
return pbHasStatus?(PBStatuses::POISON)
end
def pbCanPoison?(user,showMessages,move=nil)
return pbCanInflictStatus?(PBStatuses::POISON,user,showMessages,move)
end
def pbCanPoisonSynchronize?(target)
return pbCanSynchronizeStatus?(PBStatuses::POISON,target)
end
def pbPoison(user=nil,msg=nil,toxic=false)
pbInflictStatus(PBStatuses::POISON,(toxic) ? 1 : 0,msg,user)
end
#=============================================================================
# Burn
#=============================================================================
def burned?
return pbHasStatus?(PBStatuses::BURN)
end
def pbCanBurn?(user,showMessages,move=nil)
return pbCanInflictStatus?(PBStatuses::BURN,user,showMessages,move)
end
def pbCanBurnSynchronize?(target)
return pbCanSynchronizeStatus?(PBStatuses::BURN,target)
end
def pbBurn(user=nil,msg=nil)
pbInflictStatus(PBStatuses::BURN,0,msg,user)
end
#=============================================================================
# Paralyze
#=============================================================================
def paralyzed?
return pbHasStatus?(PBStatuses::PARALYSIS)
end
def pbCanParalyze?(user,showMessages,move=nil)
return pbCanInflictStatus?(PBStatuses::PARALYSIS,user,showMessages,move)
end
def pbCanParalyzeSynchronize?(target)
return pbCanSynchronizeStatus?(PBStatuses::PARALYSIS,target)
end
def pbParalyze(user=nil,msg=nil)
pbInflictStatus(PBStatuses::PARALYSIS,0,msg,user)
end
#=============================================================================
# Freeze
#=============================================================================
def frozen?
return pbHasStatus?(PBStatuses::FROZEN)
end
def pbCanFreeze?(user,showMessages,move=nil)
return pbCanInflictStatus?(PBStatuses::FROZEN,user,showMessages,move)
end
def pbFreeze(msg=nil)
pbInflictStatus(PBStatuses::FROZEN,0,msg)
end
#=============================================================================
# Generalised status displays
#=============================================================================
def pbContinueStatus
anim = ""; msg = ""
case self.status
when PBStatuses::SLEEP
anim = "Sleep"; msg = _INTL("{1} is fast asleep.",pbThis)
when PBStatuses::POISON
anim = (@statusCount>0) ? "Toxic" : "Poison"
msg = _INTL("{1} was hurt by poison!",pbThis)
when PBStatuses::BURN
anim = "Burn"; msg = _INTL("{1} was hurt by its burn!",pbThis)
when PBStatuses::PARALYSIS
anim = "Paralysis"; msg = _INTL("{1} is paralyzed! It can't move!",pbThis)
when PBStatuses::FROZEN
anim = "Frozen"; msg = _INTL("{1} is frozen solid!",pbThis)
end
@battle.pbCommonAnimation(anim,self) if anim!=""
yield if block_given?
@battle.pbDisplay(msg) if msg!=""
PBDebug.log("[Status continues] #{pbThis}'s sleep count is #{@statusCount}") if self.status==PBStatuses::SLEEP
end
def pbCureStatus(showMessages=true)
oldStatus = status
self.status = PBStatuses::NONE
if showMessages
case oldStatus
when PBStatuses::SLEEP; @battle.pbDisplay(_INTL("{1} woke up!",pbThis))
when PBStatuses::POISON; @battle.pbDisplay(_INTL("{1} was cured of its poisoning.",pbThis))
when PBStatuses::BURN; @battle.pbDisplay(_INTL("{1}'s burn was healed.",pbThis))
when PBStatuses::PARALYSIS; @battle.pbDisplay(_INTL("{1} was cured of paralysis.",pbThis))
when PBStatuses::FROZEN; @battle.pbDisplay(_INTL("{1} thawed out!",pbThis))
end
end
PBDebug.log("[Status change] #{pbThis}'s status was cured") if !showMessages
end
#=============================================================================
# Confusion
#=============================================================================
def pbCanConfuse?(user=nil,showMessages=true,move=nil,selfInflicted=false)
return false if fainted?
if @effects[PBEffects::Confusion]>0
@battle.pbDisplay(_INTL("{1} is already confused.",pbThis)) if showMessages
return false
end
if @effects[PBEffects::Substitute]>0 && !(move && move.ignoresSubstitute?(user)) &&
!selfInflicted
@battle.pbDisplay(_INTL("But it failed!")) if showMessages
return false
end
# Terrains immunity
if affectedByTerrain? && @battle.field.terrain==PBBattleTerrains::Misty
@battle.pbDisplay(_INTL("{1} surrounds itself with misty terrain!",pbThis(true))) if showMessages
return false
end
if selfInflicted || !@battle.moldBreaker
if hasActiveAbility?(:OWNTEMPO)
if showMessages
@battle.pbShowAbilitySplash(self)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} doesn't become confused!",pbThis))
else
@battle.pbDisplay(_INTL("{1}'s {2} prevents confusion!",pbThis,abilityName))
end
@battle.pbHideAbilitySplash(self)
end
return false
end
end
if pbOwnSide.effects[PBEffects::Safeguard]>0 && !selfInflicted &&
!(user && user.hasActiveAbility?(:INFILTRATOR))
@battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!",pbThis)) if showMessages
return false
end
return true
end
def pbCanConfuseSelf?(showMessages)
return pbCanConfuse?(nil,showMessages,nil,true)
end
def pbConfuse(msg=nil)
@effects[PBEffects::Confusion] = pbConfusionDuration
@battle.pbCommonAnimation("Confusion",self)
msg = _INTL("{1} became confused!",pbThis) if !msg || msg==""
@battle.pbDisplay(msg)
PBDebug.log("[Lingering effect] #{pbThis}'s confusion count is #{@effects[PBEffects::Confusion]}")
# Confusion cures
pbItemStatusCureCheck
pbAbilityStatusCureCheck
end
def pbConfusionDuration(duration=-1)
duration = 2+@battle.pbRandom(4) if duration<=0
return duration
end
def pbCureConfusion
@effects[PBEffects::Confusion] = 0
end
#=============================================================================
# Attraction
#=============================================================================
def pbCanAttract?(user,showMessages=true)
return false if fainted?
return false if !user || user.fainted?
if @effects[PBEffects::Attract]>=0
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMessages
return false
end
agender = user.gender
ogender = gender
if agender==2 || ogender==2 || agender==ogender
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMessages
return false
end
if !@battle.moldBreaker
if hasActiveAbility?([:AROMAVEIL,:OBLIVIOUS])
if showMessages
@battle.pbShowAbilitySplash(self)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis))
else
@battle.pbDisplay(_INTL("{1}'s {2} prevents romance!",pbThis,abilityName))
end
@battle.pbHideAbilitySplash(self)
end
return false
else
eachAlly do |b|
next if !b.hasActiveAbility?(:AROMAVEIL)
if showMessages
@battle.pbShowAbilitySplash(self)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis))
else
@battle.pbDisplay(_INTL("{1}'s {2} prevents romance!",b.pbThis,b.abilityName))
end
@battle.pbHideAbilitySplash(self)
end
return true
end
end
end
return true
end
def pbAttract(user,msg=nil)
@effects[PBEffects::Attract] = user.index
@battle.pbCommonAnimation("Attract",self)
msg = _INTL("{1} fell in love!",pbThis) if !msg || msg==""
@battle.pbDisplay(msg)
# Destiny Knot
if hasActiveItem?(:DESTINYKNOT) && user.pbCanAttract?(self,false)
user.pbAttract(self,_INTL("{1} fell in love from the {2}!",user.pbThis(true),itemName))
end
# Attraction cures
pbItemStatusCureCheck
pbAbilityStatusCureCheck
end
def pbCureAttract
@effects[PBEffects::Attract] = -1
end
#=============================================================================
# Flinching
#=============================================================================
def pbFlinch(user=nil)
return if hasActiveAbility?(:INNERFOCUS) && !@battle.moldBreaker
@effects[PBEffects::Flinch] = true
end
end

View File

@@ -0,0 +1,310 @@
class PokeBattle_Battler
#=============================================================================
# Increase stat stages
#=============================================================================
def statStageAtMax?(stat)
return @stages[stat]>=6
end
def pbCanRaiseStatStage?(stat,user=nil,move=nil,showFailMsg=false,ignoreContrary=false)
return false if fainted?
# Contrary
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
return pbCanLowerStatStage?(stat,user,move,showFailMsg,true)
end
# Check the stat stage
if statStageAtMax?(stat)
@battle.pbDisplay(_INTL("{1}'s {2} won't go any higher!",
pbThis,PBStats.getName(stat))) if showFailMsg
return false
end
return true
end
def pbRaiseStatStageBasic(stat,increment,ignoreContrary=false)
if !@battle.moldBreaker
# Contrary
if hasActiveAbility?(:CONTRARY) && !ignoreContrary
return pbLowerStatStageBasic(stat,increment,true)
end
# Simple
increment *= 2 if hasActiveAbility?(:SIMPLE)
end
# Change the stat stage
increment = [increment,6-@stages[stat]].min
if increment>0
s = PBStats.getName(stat); new = @stages[stat]+increment
PBDebug.log("[Stat change] #{pbThis}'s #{s}: #{@stages[stat]} -> #{new} (+#{increment})")
@stages[stat] += increment
end
return increment
end
def pbRaiseStatStage(stat,increment,user,showAnim=true,ignoreContrary=false)
return false if !PBStats.validBattleStat?(stat)
# Contrary
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
return pbLowerStatStage(stat,increment,user,showAnim,true)
end
# Perform the stat stage change
increment = pbRaiseStatStageBasic(stat,increment,ignoreContrary)
return false if increment<=0
# Stat up animation and message
@battle.pbCommonAnimation("StatUp",self) if showAnim
arrStatTexts = [
_INTL("{1}'s {2} rose!",pbThis,PBStats.getName(stat)),
_INTL("{1}'s {2} rose sharply!",pbThis,PBStats.getName(stat)),
_INTL("{1}'s {2} rose drastically!",pbThis,PBStats.getName(stat))]
@battle.pbDisplay(arrStatTexts[[increment-1,2].min])
# Trigger abilities upon stat gain
if abilityActive?
BattleHandlers.triggerAbilityOnStatGain(@ability,self,stat,user)
end
return true
end
def pbRaiseStatStageByCause(stat,increment,user,cause,showAnim=true,ignoreContrary=false)
return false if !PBStats.validBattleStat?(stat)
# Contrary
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
return pbLowerStatStageByCause(stat,increment,user,cause,showAnim,true)
end
# Perform the stat stage change
increment = pbRaiseStatStageBasic(stat,increment,ignoreContrary)
return false if increment<=0
# Stat up animation and message
@battle.pbCommonAnimation("StatUp",self) if showAnim
if user.index==@index
arrStatTexts = [
_INTL("{1}'s {2} raised its {3}!",pbThis,cause,PBStats.getName(stat)),
_INTL("{1}'s {2} sharply raised its {3}!",pbThis,cause,PBStats.getName(stat)),
_INTL("{1}'s {2} drastically raised its {3}!",pbThis,cause,PBStats.getName(stat))]
else
arrStatTexts = [
_INTL("{1}'s {2} raised {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat)),
_INTL("{1}'s {2} sharply raised {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat)),
_INTL("{1}'s {2} drastically raised {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat))]
end
@battle.pbDisplay(arrStatTexts[[increment-1,2].min])
# Trigger abilities upon stat gain
if abilityActive?
BattleHandlers.triggerAbilityOnStatGain(@ability,self,stat,user)
end
return true
end
def pbRaiseStatStageByAbility(stat,increment,user,splashAnim=true)
return false if fainted?
ret = false
@battle.pbShowAbilitySplash(user) if splashAnim
if pbCanRaiseStatStage?(stat,user,nil,PokeBattle_SceneConstants::USE_ABILITY_SPLASH)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
ret = pbRaiseStatStage(stat,increment,user)
else
ret = pbRaiseStatStageByCause(stat,increment,user,user.abilityName)
end
end
@battle.pbHideAbilitySplash(user) if splashAnim
return ret
end
#=============================================================================
# Decrease stat stages
#=============================================================================
def statStageAtMin?(stat)
return @stages[stat]<=-6
end
def pbCanLowerStatStage?(stat,user=nil,move=nil,showFailMsg=false,ignoreContrary=false)
return false if fainted?
# Contrary
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
return pbCanRaiseStatStage?(stat,user,move,showFailMsg,true)
end
if !user || user.index!=@index # Not self-inflicted
if @effects[PBEffects::Substitute]>0 && !(move && move.ignoresSubstitute?(user))
@battle.pbDisplay(_INTL("{1} is protected by its substitute!",pbThis)) if showFailMsg
return false
end
if pbOwnSide.effects[PBEffects::Mist]>0 &&
!(user && user.hasActiveAbility?(:INFILTRATOR))
@battle.pbDisplay(_INTL("{1} is protected by Mist!",pbThis)) if showFailMsg
return false
end
if abilityActive?
return false if BattleHandlers.triggerStatLossImmunityAbility(
@ability,self,stat,@battle,showFailMsg) if !@battle.moldBreaker
return false if BattleHandlers.triggerStatLossImmunityAbilityNonIgnorable(
@ability,self,stat,@battle,showFailMsg)
end
if !@battle.moldBreaker
eachAlly do |b|
next if !b.abilityActive?
return false if BattleHandlers.triggerStatLossImmunityAllyAbility(
b.ability,b,self,stat,@battle,showFailMsg)
end
end
end
# Check the stat stage
if statStageAtMin?(stat)
@battle.pbDisplay(_INTL("{1}'s {2} won't go any lower!",
pbThis,PBStats.getName(stat))) if showFailMsg
return false
end
return true
end
def pbLowerStatStageBasic(stat,increment,ignoreContrary=false)
if !@battle.moldBreaker
# Contrary
if hasActiveAbility?(:CONTRARY) && !ignoreContrary
return pbRaiseStatStageBasic(stat,increment,true)
end
# Simple
increment *= 2 if hasActiveAbility?(:SIMPLE)
end
# Change the stat stage
increment = [increment,6+@stages[stat]].min
if increment>0
s = PBStats.getName(stat); new = @stages[stat]-increment
PBDebug.log("[Stat change] #{pbThis}'s #{s}: #{@stages[stat]} -> #{new} (-#{increment})")
@stages[stat] -= increment
end
return increment
end
def pbLowerStatStage(stat,increment,user,showAnim=true,ignoreContrary=false)
return false if !PBStats.validBattleStat?(stat)
# Contrary
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
return pbRaiseStatStage(stat,increment,user,showAnim,true)
end
# Perform the stat stage change
increment = pbLowerStatStageBasic(stat,increment,ignoreContrary)
return false if increment<=0
# Stat down animation and message
@battle.pbCommonAnimation("StatDown",self) if showAnim
arrStatTexts = [
_INTL("{1}'s {2} fell!",pbThis,PBStats.getName(stat)),
_INTL("{1}'s {2} harshly fell!",pbThis,PBStats.getName(stat)),
_INTL("{1}'s {2} severely fell!",pbThis,PBStats.getName(stat))]
@battle.pbDisplay(arrStatTexts[[increment-1,2].min])
# Trigger abilities upon stat loss
if abilityActive?
BattleHandlers.triggerAbilityOnStatLoss(@ability,self,stat,user)
end
return true
end
def pbLowerStatStageByCause(stat,increment,user,cause,showAnim=true,ignoreContrary=false)
return false if !PBStats.validBattleStat?(stat)
# Contrary
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
return pbRaiseStatStageByCause(stat,increment,user,cause,showAnim,true)
end
# Perform the stat stage change
increment = pbLowerStatStageBasic(stat,increment,ignoreContrary)
return false if increment<=0
# Stat down animation and message
@battle.pbCommonAnimation("StatDown",self) if showAnim
if user.index==@index
arrStatTexts = [
_INTL("{1}'s {2} lowered its {3}!",pbThis,cause,PBStats.getName(stat)),
_INTL("{1}'s {2} harshly lowered its {3}!",pbThis,cause,PBStats.getName(stat)),
_INTL("{1}'s {2} severely lowered its {3}!",pbThis,cause,PBStats.getName(stat))]
else
arrStatTexts = [
_INTL("{1}'s {2} lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat)),
_INTL("{1}'s {2} harshly lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat)),
_INTL("{1}'s {2} severely lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat))]
end
@battle.pbDisplay(arrStatTexts[[increment-1,2].min])
# Trigger abilities upon stat loss
if abilityActive?
BattleHandlers.triggerAbilityOnStatLoss(@ability,self,stat,user)
end
return true
end
def pbLowerStatStageByAbility(stat,increment,user,splashAnim=true,checkContact=false)
ret = false
@battle.pbShowAbilitySplash(user) if splashAnim
if pbCanLowerStatStage?(stat,user,nil,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) &&
(!checkContact || affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH))
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
ret = pbLowerStatStage(stat,increment,user)
else
ret = pbLowerStatStageByCause(stat,increment,user,user.abilityName)
end
end
@battle.pbHideAbilitySplash(user) if splashAnim
return ret
end
def pbLowerAttackStatStageIntimidate(user)
return false if fainted?
# NOTE: Substitute intentially blocks Intimidate even if self has Contrary.
if @effects[PBEffects::Substitute]>0
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is protected by its substitute!",pbThis))
else
@battle.pbDisplay(_INTL("{1}'s substitute protected it from {2}'s {3}!",
pbThis,user.pbThis(true),user.abilityName))
end
return false
end
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
return pbLowerStatStageByAbility(PBStats::ATTACK,1,user,false)
end
# NOTE: These checks exist to ensure appropriate messages are shown if
# Intimidate is blocked somehow (i.e. the messages should mention the
# Intimidate ability by name).
if !hasActiveAbility?(:CONTRARY)
if pbOwnSide.effects[PBEffects::Mist]>0
@battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by Mist!",
pbThis,user.pbThis(true),user.abilityName))
return false
end
if abilityActive?
if BattleHandlers.triggerStatLossImmunityAbility(@ability,self,PBStats::ATTACK,@battle,false) ||
BattleHandlers.triggerStatLossImmunityAbilityNonIgnorable(@ability,self,PBStats::ATTACK,@battle,false)
@battle.pbDisplay(_INTL("{1}'s {2} prevented {3}'s {4} from working!",
pbThis,abilityName,user.pbThis(true),user.abilityName))
return false
end
end
eachAlly do |b|
next if !b.abilityActive?
if BattleHandlers.triggerStatLossImmunityAllyAbility(b.ability,b,self,PBStats::ATTACK,@battle,false)
@battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by {4}'s {5}!",
pbThis,user.pbThis(true),user.abilityName,b.pbThis(true),b.abilityName))
return false
end
end
end
return false if !pbCanLowerStatStage?(PBStats::ATTACK,user)
return pbLowerStatStageByCause(PBStats::ATTACK,1,user,user.abilityName)
end
#=============================================================================
# Reset stat stages
#=============================================================================
def hasAlteredStatStages?
PBStats.eachBattleStat { |s| return true if @stages[s]!=0 }
return false
end
def hasRaisedStatStages?
PBStats.eachBattleStat { |s| return true if @stages[s]>0 }
return false
end
def hasLoweredStatStages?
PBStats.eachBattleStat { |s| return true if @stages[s]<0 }
return false
end
def pbResetStatStages
PBStats.eachBattleStat { |s| @stages[s] = 0 }
end
end

View File

@@ -0,0 +1,322 @@
class PokeBattle_Battler
#=============================================================================
# Called when a Pokémon (self) is sent into battle or its ability changes.
#=============================================================================
def pbEffectsOnSwitchIn(switchIn=false)
# Healing Wish/Lunar Dance/entry hazards
@battle.pbOnActiveOne(self) if switchIn
# Primal Revert upon entering battle
@battle.pbPrimalReversion(@index) if !fainted?
# Ending primordial weather, checking Trace
pbContinualAbilityChecks(true)
# Abilities that trigger upon switching in
if (!fainted? && nonNegatableAbility?) || abilityActive?
BattleHandlers.triggerAbilityOnSwitchIn(@ability,self,@battle)
end
# Check for end of primordial weather
@battle.pbEndPrimordialWeather
# Items that trigger upon switching in (Air Balloon message)
if switchIn && itemActive?
BattleHandlers.triggerItemOnSwitchIn(@item,self,@battle)
end
# Berry check, status-curing ability check
pbHeldItemTriggerCheck if switchIn
pbAbilityStatusCureCheck
end
#=============================================================================
# Ability effects
#=============================================================================
def pbAbilitiesOnSwitchOut
if abilityActive?
BattleHandlers.triggerAbilityOnSwitchOut(@ability,self,false)
end
# Reset form
@battle.peer.pbOnLeavingBattle(@battle,@pokemon,@battle.usedInBattle[idxOwnSide][@index/2])
# Treat self as fainted
@hp = 0
@fainted = true
# Check for end of primordial weather
@battle.pbEndPrimordialWeather
end
def pbAbilitiesOnFainting
# Self fainted; check all other battlers to see if their abilities trigger
@battle.pbPriority(true).each do |b|
next if !b || !b.abilityActive?
BattleHandlers.triggerAbilityChangeOnBattlerFainting(b.ability,b,self,@battle)
end
@battle.pbPriority(true).each do |b|
next if !b || !b.abilityActive?
BattleHandlers.triggerAbilityOnBattlerFainting(b.ability,b,self,@battle)
end
end
# Used for Emergency Exit/Wimp Out.
def pbAbilitiesOnDamageTaken(oldHP,newHP=-1)
return false if !abilityActive?
newHP = @hp if newHP<0
return false if oldHP<@totalhp/2 || newHP>=@totalhp/2 # Didn't drop below half
ret = BattleHandlers.triggerAbilityOnHPDroppedBelowHalf(@ability,self,@battle)
return ret # Whether self has switched out
end
# Called when a Pokémon (self) enters battle, at the end of each move used,
# and at the end of each round.
def pbContinualAbilityChecks(onSwitchIn=false)
# Check for end of primordial weather
@battle.pbEndPrimordialWeather
# Trace
if hasActiveAbility?(:TRACE)
# NOTE: In Gen 5 only, Trace only triggers upon the Trace bearer switching
# in and not at any later times, even if a traceable ability turns
# up later. Essentials ignores this, and allows Trace to trigger
# whenever it can even in the old battle mechanics.
abilityBlacklist = [
# Replaces self with another ability
:POWEROFALCHEMY,
:RECEIVER,
:TRACE,
# Form-changing abilities
:BATTLEBOND,
:DISGUISE,
:FLOWERGIFT,
:FORECAST,
:MULTITYPE,
:POWERCONSTRUCT,
:SCHOOLING,
:SHIELDSDOWN,
:STANCECHANGE,
:ZENMODE,
# Appearance-changing abilities
:ILLUSION,
:IMPOSTER,
# Abilities intended to be inherent properties of a certain species
:COMATOSE,
:RKSSYSTEM
]
choices = []
@battle.eachOtherSideBattler(@index) do |b|
abilityBlacklist.each do |abil|
next if !isConst?(b.ability,PBAbilities,abil)
choices.push(b)
break
end
end
if choices.length>0
choice = choices[@battle.pbRandom(choices.length)]
@battle.pbShowAbilitySplash(self)
@ability = choice.ability
@battle.pbDisplay(_INTL("{1} traced {2}'s {3}!",pbThis,choice.pbThis(true),choice.abilityName))
@battle.pbHideAbilitySplash(self)
if !onSwitchIn && (nonNegatableAbility? || abilityActive?)
BattleHandlers.triggerAbilityOnSwitchIn(@ability,self,@battle)
end
end
end
end
#=============================================================================
# Ability curing
#=============================================================================
# Cures status conditions, confusion and infatuation.
def pbAbilityStatusCureCheck
if abilityActive?
BattleHandlers.triggerStatusCureAbility(@ability,self)
end
end
#=============================================================================
# Ability change
#=============================================================================
def pbOnAbilityChanged(oldAbil)
if @effects[PBEffects::Illusion] && isConst?(oldAbil,PBAbilities,:ILLUSION)
@effects[PBEffects::Illusion] = nil
if !@effects[PBEffects::Transform]
@battle.scene.pbChangePokemon(self,@pokemon)
@battle.pbDisplay(_INTL("{1}'s {2} wore off!",pbThis,PBAbilities.getName(oldAbil)))
@battle.pbSetSeen(self)
end
end
@effects[PBEffects::GastroAcid] = false if nonNegatableAbility?
@effects[PBEffects::SlowStart] = 0 if !isConst?(@ability,PBAbilities,:SLOWSTART)
# Revert form if Flower Gift/Forecast was lost
pbCheckFormOnWeatherChange
# Check for end of primordial weather
@battle.pbEndPrimordialWeather
end
#=============================================================================
# Held item consuming/removing
#=============================================================================
def pbCanConsumeBerry?(item,alwaysCheckGluttony=true)
return false if @battle.pbCheckOpposingAbility(:UNNERVE,@index)
return true if @hp<=@totalhp/4
if alwaysCheckGluttony || NEWEST_BATTLE_MECHANICS
return true if @hp<=@totalhp/2 && hasActiveAbility?(:GLUTTONY)
end
return false
end
# permanent is whether the item is lost even after battle. Is false for Knock
# Off.
def pbRemoveItem(permanent=true)
@effects[PBEffects::ChoiceBand] = -1
@effects[PBEffects::Unburden] = true if @item>0
setInitialItem(0) if self.initialItem==@item && permanent
self.item = 0
end
def pbConsumeItem(recoverable=true,symbiosis=true,belch=true)
PBDebug.log("[Item consumed] #{pbThis} consumed its held #{PBItems.getName(@item)}")
if recoverable
setRecycleItem(@item)
@effects[PBEffects::PickupItem] = @item
@effects[PBEffects::PickupUse] = @battle.nextPickupUse
end
setBelched if belch && pbIsBerry?(@item)
pbRemoveItem
pbSymbiosis if symbiosis
end
def pbSymbiosis
return if fainted?
return if @item!=0
@battle.pbPriority(true).each do |b|
next if b.opposes?
next if !b.hasActiveAbility?(:SYMBIOSIS)
next if b.item==0 || b.unlosableItem?(b.item)
next if unlosableItem?(b.item)
@battle.pbShowAbilitySplash(b)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} shared its {2} with {3}!",
b.pbThis,b.itemName,pbThis(true)))
else
@battle.pbDisplay(_INTL("{1}'s {2} let it share its {3} with {4}!",
b.pbThis,b.abilityName,b.itemName,pbThis(true)))
end
self.item = b.item
b.item = 0
b.effects[PBEffects::Unburden] = true
@battle.pbHideAbilitySplash(b)
pbHeldItemTriggerCheck
break
end
end
def pbHeldItemTriggered(thisItem,forcedItem=0,fling=false)
# Cheek Pouch
if hasActiveAbility?(:CHEEKPOUCH) && pbIsBerry?(thisItem) && canHeal?
@battle.pbShowAbilitySplash(self)
pbRecoverHP(@totalhp/3)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1}'s HP was restored.",pbThis))
else
@battle.pbDisplay(_INTL("{1}'s {2} restored its HP.",pbThis,abilityName))
end
@battle.pbHideAbilitySplash(self)
end
pbConsumeItem if forcedItem<=0
pbSymbiosis if forcedItem>0 && !fling # Bug Bite/Pluck users trigger Symbiosis
end
#=============================================================================
# Held item trigger checks
#=============================================================================
# NOTE: A Pokémon using Bug Bite/Pluck, and a Pokémon having an item thrown at
# it via Fling, will gain the effect of the item even if the Pokémon is
# affected by item-negating effects.
# If forcedItem is -1, the Pokémon's held item is forced to be consumed. If it
# is greater than 0, a different item (of that ID) is forced to be consumed
# (not the Pokémon's held one).
def pbHeldItemTriggerCheck(forcedItem=0,fling=false)
return if fainted?
return if forcedItem==0 && !itemActive?
pbItemHPHealCheck(forcedItem,fling)
pbItemStatusCureCheck(forcedItem,fling)
pbItemEndOfMoveCheck(forcedItem,fling)
# For Enigma Berry, Kee Berry and Maranga Berry, which have their effects
# when forcibly consumed by Pluck/Fling.
if forcedItem!=0
thisItem = (forcedItem>0) ? forcedItem : @item
if BattleHandlers.triggerTargetItemOnHitPositiveBerry(thisItem,self,@battle,true)
pbHeldItemTriggered(thisItem,forcedItem,fling)
end
end
end
# forcedItem is an item ID for Pluck/Fling, and 0 otherwise. fling is for
# Fling only.
def pbItemHPHealCheck(forcedItem=0,fling=false)
return if !canHeal?
return if forcedItem==0 && !itemActive?
thisItem = (forcedItem>0) ? forcedItem : @item
if BattleHandlers.triggerHPHealItem(thisItem,self,@battle,(forcedItem!=0))
pbHeldItemTriggered(thisItem,forcedItem,fling)
elsif forcedItem==0
pbItemTerrainStatBoostCheck
end
end
# Cures status conditions, confusion, infatuation and the other effects cured
# by Mental Herb.
# forcedItem is an item ID for Pluck/Fling, and 0 otherwise. fling is for
# Fling only.
def pbItemStatusCureCheck(forcedItem=0,fling=false)
return if fainted?
return if forcedItem==0 && !itemActive?
thisItem = (forcedItem>0) ? forcedItem : @item
if BattleHandlers.triggerStatusCureItem(thisItem,self,@battle,(forcedItem!=0))
pbHeldItemTriggered(thisItem,forcedItem,fling)
end
end
# Called at the end of using a move.
# forcedItem is an item ID for Pluck/Fling, and 0 otherwise. fling is for
# Fling only.
def pbItemEndOfMoveCheck(forcedItem=0,fling=false)
return if fainted?
return if forcedItem==0 && !itemActive?
thisItem = (forcedItem>0) ? forcedItem : @item
if BattleHandlers.triggerEndOfMoveItem(thisItem,self,@battle,(forcedItem!=0))
pbHeldItemTriggered(thisItem,forcedItem,fling)
elsif BattleHandlers.triggerEndOfMoveStatRestoreItem(thisItem,self,@battle,(forcedItem!=0))
pbHeldItemTriggered(thisItem,forcedItem,fling)
end
end
# Used for White Herb (restore lowered stats). Only called by Moody and Sticky
# Web, as all other stat reduction happens because of/during move usage and
# this handler is also called at the end of each move's usage.
# forcedItem is an item ID for Pluck/Fling, and 0 otherwise. fling is for
# Fling only.
def pbItemStatRestoreCheck(forcedItem=0,fling=false)
return if fainted?
return if forcedItem==0 && !itemActive?
thisItem = (forcedItem>0) ? forcedItem : @item
if BattleHandlers.triggerEndOfMoveStatRestoreItem(thisItem,self,@battle,(forcedItem!=0))
pbHeldItemTriggered(thisItem,forcedItem,fling)
end
end
# Called when the battle terrain changes and when a Pokémon loses HP.
# forcedItem is an item ID for Pluck/Fling, and 0 otherwise. fling is for
# Fling only.
def pbItemTerrainStatBoostCheck
return if !itemActive?
if BattleHandlers.triggerTerrainStatBoostItem(@item,self,@battle)
pbHeldItemTriggered(@item)
end
end
# Used for Adrenaline Orb. Called when Intimidate is triggered (even if
# Intimidate has no effect on the Pokémon).
# forcedItem is an item ID for Pluck/Fling, and 0 otherwise. fling is for
# Fling only.
def pbItemOnIntimidatedCheck
return if !itemActive?
if BattleHandlers.triggerItemOnIntimidated(@item,self,@battle)
pbHeldItemTriggered(@item)
end
end
end

View File

@@ -0,0 +1,729 @@
class PokeBattle_Battler
#=============================================================================
# Turn processing
#=============================================================================
def pbProcessTurn(choice,tryFlee=true)
return false if fainted?
# Wild roaming Pokémon always flee if possible
if tryFlee && @battle.wildBattle? && opposes? &&
@battle.rules["alwaysflee"] && @battle.pbCanRun?(@index)
pbBeginTurn(choice)
@battle.pbDisplay(_INTL("{1} fled from battle!",pbThis)) { pbSEPlay("Battle flee") }
@battle.decision = 3
pbEndTurn(choice)
return true
end
# Shift with the battler next to this one
if choice[0]==:Shift
idxOther = -1
case @battle.pbSideSize(@index)
when 2
idxOther = (@index+2)%4
when 3
if @index!=2 && @index!=3 # If not in middle spot already
idxOther = ((@index%2)==0) ? 2 : 3
end
end
if idxOther>=0
@battle.pbSwapBattlers(@index,idxOther)
case @battle.pbSideSize(@index)
when 2
@battle.pbDisplay(_INTL("{1} moved across!",pbThis))
when 3
@battle.pbDisplay(_INTL("{1} moved to the center!",pbThis))
end
end
pbBeginTurn(choice)
pbCancelMoves
@lastRoundMoved = @battle.turnCount # Done something this round
return true
end
# If this battler's action for this round wasn't "use a move"
if choice[0]!=:UseMove
# Clean up effects that end at battler's turn
pbBeginTurn(choice)
pbEndTurn(choice)
return false
end
# Turn is skipped if Pursuit was used during switch
if @effects[PBEffects::Pursuit]
@effects[PBEffects::Pursuit] = false
pbCancelMoves
pbEndTurn(choice)
@battle.pbJudge
return false
end
# Use the move
PBDebug.log("[Move usage] #{pbThis} started using #{choice[2].name}")
PBDebug.logonerr{
pbUseMove(choice,choice[2]==@battle.struggle)
}
@battle.pbJudge
# Update priority order
# @battle.pbCalculatePriority if NEWEST_BATTLE_MECHANICS
return true
end
#=============================================================================
#
#=============================================================================
def pbBeginTurn(choice)
# Cancel some lingering effects which only apply until the user next moves
@effects[PBEffects::BeakBlast] = false
@effects[PBEffects::DestinyBondPrevious] = @effects[PBEffects::DestinyBond]
@effects[PBEffects::DestinyBond] = false
@effects[PBEffects::Grudge] = false
@effects[PBEffects::MoveNext] = false
@effects[PBEffects::Quash] = 0
@effects[PBEffects::ShellTrap] = false
# Encore's effect ends if the encored move is no longer available
if @effects[PBEffects::Encore]>0 && pbEncoredMoveIndex<0
@effects[PBEffects::Encore] = 0
@effects[PBEffects::EncoreMove] = 0
end
end
# Called when the usage of various multi-turn moves is disrupted due to
# failing pbTryUseMove, being ineffective against all targets, or because
# Pursuit was used specially to intercept a switching foe.
# Cancels the use of multi-turn moves and counters thereof. Note that Hyper
# Beam's effect is NOT cancelled.
def pbCancelMoves
# Outragers get confused anyway if they are disrupted during their final
# turn of using the move
if @effects[PBEffects::Outrage]==1 && pbCanConfuseSelf?(false)
pbConfuse(_INTL("{1} became confused due to fatigue!",pbThis))
end
# Cancel usage of most multi-turn moves
@effects[PBEffects::TwoTurnAttack] = 0
@effects[PBEffects::Rollout] = 0
@effects[PBEffects::Outrage] = 0
@effects[PBEffects::Uproar] = 0
@effects[PBEffects::Bide] = 0
@currentMove = 0
# Reset counters for moves which increase them when used in succession
@effects[PBEffects::FuryCutter] = 0
end
def pbEndTurn(choice)
@lastRoundMoved = @battle.turnCount # Done something this round
if @effects[PBEffects::ChoiceBand]<0 &&
hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF])
if @lastMoveUsed>=0 && pbHasMove?(@lastMoveUsed)
@effects[PBEffects::ChoiceBand] = @lastMoveUsed
elsif @lastRegularMoveUsed>=0 && pbHasMove?(@lastRegularMoveUsed)
@effects[PBEffects::ChoiceBand] = @lastRegularMoveUsed
end
end
@effects[PBEffects::Charge] = 0 if @effects[PBEffects::Charge]==1
@effects[PBEffects::GemConsumed] = 0
@battle.eachBattler { |b| b.pbContinualAbilityChecks } # Trace, end primordial weathers
end
def pbConfusionDamage(msg)
@damageState.reset
@damageState.initialHP = @hp
confusionMove = PokeBattle_Confusion.new(@battle,nil)
confusionMove.calcType = confusionMove.pbCalcType(self) # -1
@damageState.typeMod = confusionMove.pbCalcTypeMod(confusionMove.calcType,self,self) # 8
confusionMove.pbCheckDamageAbsorption(self,self)
confusionMove.pbCalcDamage(self,self)
confusionMove.pbReduceDamage(self,self)
self.hp -= @damageState.hpLost
confusionMove.pbAnimateHitAndHPLost(self,[self])
@battle.pbDisplay(msg) # "It hurt itself in its confusion!"
confusionMove.pbRecordDamageLost(self,self)
confusionMove.pbEndureKOMessage(self)
pbFaint if fainted?
pbItemHPHealCheck
end
#=============================================================================
# Simple "use move" method, used when a move calls another move and for Future
# Sight's attack
#=============================================================================
def pbUseMoveSimple(moveID,target=-1,idxMove=-1,specialUsage=true)
choice = []
choice[0] = :UseMove # "Use move"
choice[1] = idxMove # Index of move to be used in user's moveset
if idxMove>=0
choice[2] = @moves[idxMove]
else
choice[2] = PokeBattle_Move.pbFromPBMove(@battle,PBMove.new(moveID)) # PokeBattle_Move object
choice[2].pp = -1
end
choice[3] = target # Target (-1 means no target yet)
PBDebug.log("[Move usage] #{pbThis} started using the called/simple move #{choice[2].name}")
pbUseMove(choice,specialUsage)
end
#=============================================================================
# Master "use move" method
#=============================================================================
def pbUseMove(choice,specialUsage=false)
# NOTE: This is intentionally determined before a multi-turn attack can
# set specialUsage to true.
skipAccuracyCheck = (specialUsage && choice[2]!=@battle.struggle)
# Start using the move
pbBeginTurn(choice)
# Force the use of certain moves if they're already being used
if usingMultiTurnAttack?
choice[2] = PokeBattle_Move.pbFromPBMove(@battle,PBMove.new(@currentMove))
specialUsage = true
elsif @effects[PBEffects::Encore]>0 && choice[1]>=0 &&
@battle.pbCanShowCommands?(@index)
idxEncoredMove = pbEncoredMoveIndex
if idxEncoredMove>=0 && @battle.pbCanChooseMove?(@index,idxEncoredMove,false)
if choice[1]!=idxEncoredMove # Change move if battler was Encored mid-round
choice[1] = idxEncoredMove
choice[2] = @moves[idxEncoredMove]
choice[3] = -1 # No target chosen
end
end
end
# Labels the move being used as "move"
move = choice[2]
return if !move || move.id==0 # if move was not chosen somehow
# Try to use the move (inc. disobedience)
@lastMoveFailed = false
if !pbTryUseMove(choice,move,specialUsage,skipAccuracyCheck)
@lastMoveUsed = -1
@lastMoveUsedType = -1
if !specialUsage
@lastRegularMoveUsed = -1
@lastRegularMoveTarget = -1
end
@battle.pbGainExp # In case self is KO'd due to confusion
pbCancelMoves
pbEndTurn(choice)
return
end
move = choice[2] # In case disobedience changed the move to be used
return if !move || move.id==0 # if move was not chosen somehow
# Subtract PP
if !specialUsage
if !pbReducePP(move)
@battle.pbDisplay(_INTL("{1} used {2}!",pbThis,move.name))
@battle.pbDisplay(_INTL("But there was no PP left for the move!"))
@lastMoveUsed = -1
@lastMoveUsedType = -1
@lastRegularMoveUsed = -1
@lastRegularMoveTarget = -1
@lastMoveFailed = true
pbCancelMoves
pbEndTurn(choice)
return
end
end
# Stance Change
if isConst?(@species,PBSpecies,:AEGISLASH) && isConst?(@ability,PBAbilities,:STANCECHANGE)
if move.damagingMove?
pbChangeForm(1,_INTL("{1} changed to Blade Forme!",pbThis))
elsif isConst?(move.id,PBMoves,:KINGSSHIELD)
pbChangeForm(0,_INTL("{1} changed to Shield Forme!",pbThis))
end
end
# Calculate the move's type during this usage
move.calcType = move.pbCalcType(self)
# Start effect of Mold Breaker
@battle.moldBreaker = hasMoldBreaker?
# Remember that user chose a two-turn move
if move.pbIsChargingTurn?(self)
# Beginning the use of a two-turn attack
@effects[PBEffects::TwoTurnAttack] = move.id
@currentMove = move.id
else
@effects[PBEffects::TwoTurnAttack] = 0 # Cancel use of two-turn attack
end
# Add to counters for moves which increase them when used in succession
move.pbChangeUsageCounters(self,specialUsage)
# Charge up Metronome item
if hasActiveItem?(:METRONOME) && !move.callsAnotherMove?
if @lastMoveUsed==move.id && !@lastMoveFailed
@effects[PBEffects::Metronome] += 1
else
@effects[PBEffects::Metronome] = 0
end
end
# Record move as having been used
@lastMoveUsed = move.id
@lastMoveUsedType = move.calcType # For Conversion 2
if !specialUsage
@lastRegularMoveUsed = move.id # For Disable, Encore, Instruct, Mimic, Mirror Move, Sketch, Spite
@lastRegularMoveTarget = choice[3] # For Instruct (remembering original target is fine)
@movesUsed.push(move.id) if !@movesUsed.include?(move.id) # For Last Resort
end
@battle.lastMoveUsed = move.id # For Copycat
@battle.lastMoveUser = @index # For "self KO" battle clause to avoid draws
@battle.successStates[@index].useState = 1 # Battle Arena - assume failure
# Find the default user (self or Snatcher) and target(s)
user = pbFindUser(choice,move)
user = pbChangeUser(choice,move,user)
targets = pbFindTargets(choice,move,user)
targets = pbChangeTargets(move,user,targets)
# Pressure
if !specialUsage
targets.each do |b|
next unless b.opposes?(user) && b.hasActiveAbility?(:PRESSURE)
PBDebug.log("[Ability triggered] #{b.pbThis}'s #{b.abilityName}")
user.pbReducePP(move)
end
if PBTargets.targetsFoeSide?(move.pbTarget(user))
@battle.eachOtherSideBattler(user) do |b|
next unless b.hasActiveAbility?(:PRESSURE)
PBDebug.log("[Ability triggered] #{b.pbThis}'s #{b.abilityName}")
user.pbReducePP(move)
end
end
end
# Dazzling/Queenly Majesty make the move fail here
@battle.pbPriority(true).each do |b|
next if !b || !b.abilityActive?
if BattleHandlers.triggerMoveBlockingAbility(b.ability,b,user,targets,move,@battle)
@battle.pbDisplayBrief(_INTL("{1} used {2}!",user.pbThis,move.name))
@battle.pbShowAbilitySplash(b)
@battle.pbDisplay(_INTL("{1} cannot use {2}!",user.pbThis,move.name))
@battle.pbHideAbilitySplash(b)
user.lastMoveFailed = true
pbCancelMoves
pbEndTurn(choice)
return
end
end
# "X used Y!" message
# Can be different for Bide, Fling, Focus Punch and Future Sight
# NOTE: This intentionally passes self rather than user. The user is always
# self except if Snatched, but this message should state the original
# user (self) even if the move is Snatched.
move.pbDisplayUseMessage(self)
# Snatch's message (user is the new user, self is the original user)
if move.snatched
@lastMoveFailed = true # Intentionally applies to self, not user
@battle.pbDisplay(_INTL("{1} snatched {2}'s move!",user.pbThis,pbThis(true)))
end
# "But it failed!" checks
if move.pbMoveFailed?(user,targets)
PBDebug.log(sprintf("[Move failed] In function code %s's def pbMoveFailed?",move.function))
user.lastMoveFailed = true
pbCancelMoves
pbEndTurn(choice)
return
end
# Perform set-up actions and display messages
# Messages include Magnitude's number and Pledge moves' "it's a combo!"
move.pbOnStartUse(user,targets)
# Self-thawing due to the move
if user.status==PBStatuses::FROZEN && move.thawsUser?
user.pbCureStatus(false)
@battle.pbDisplay(_INTL("{1} melted the ice!",user.pbThis))
end
# Powder
if user.effects[PBEffects::Powder] && isConst?(move.calcType,PBTypes,:FIRE)
@battle.pbCommonAnimation("Powder",user)
@battle.pbDisplay(_INTL("When the flame touched the powder on the Pokémon, it exploded!"))
user.lastMoveFailed = true
w = @battle.pbWeather
if w!=PBWeather.RAINDANCE && w!=PBWeather.HEAVYRAIN && user.takesIndirectDamage?
oldHP = user.hp
user.pbReduceHP((user.totalhp/4.0).round,false)
user.pbFaint if user.fainted?
@battle.pbGainExp # In case user is KO'd by this
user.pbItemHPHealCheck
if user.pbAbilitiesOnDamageTaken(oldHP)
user.pbEffectsOnSwitchIn(true)
end
end
pbCancelMoves
pbEndTurn(choice)
return
end
# Primordial Sea, Desolate Land
if move.damagingMove?
case @battle.pbWeather
when PBWeather::HeavyRain
if isConst?(move.calcType,PBTypes,:FIRE)
@battle.pbDisplay(_INTL("The Fire-type attack fizzled out in the heavy rain!"))
user.lastMoveFailed = true
pbCancelMoves
pbEndTurn(choice)
return
end
when PBWeather::HarshSun
if isConst?(move.calcType,PBTypes,:WATER)
@battle.pbDisplay(_INTL("The Water-type attack evaporated in the harsh sunlight!"))
user.lastMoveFailed = true
pbCancelMoves
pbEndTurn(choice)
return
end
end
end
# Protean
if user.hasActiveAbility?(:PROTEAN) && !move.callsAnotherMove? && !move.snatched
if user.pbHasOtherType?(moveType) && !PBTypes.isPseudoType?(move.calcType)
@battle.pbShowAbilitySplash(user)
user.pbChangeTypes(move.calcType)
typeName = PBTypes.getName(move.calcType)
@battle.pbDisplay(_INTL("{1} transformed into the {2} type!",user.pbThis,typeName))
@battle.pbHideAbilitySplash(user)
# NOTE: The GF games say that if Curse is used by a non-Ghost-type
# Pokémon which becomes Ghost-type because of Protean, it should
# target and curse itself. I think this is silly, so I'm making it
# choose a random opponent to curse instead.
if move.function=="10D" && targets.length==0 # Curse
choice[3] = -1
targets = pbFindTargets(choice,move,user)
end
end
end
#---------------------------------------------------------------------------
magicCoater = -1
magicBouncer = -1
if targets.length==0 && !PBTargets.noTargets?(move.pbTarget(user)) &&
!move.worksWithNoTargets?
# def pbFindTargets should have found a target(s), but it didn't because
# they were all fainted
# All target types except: None, User, UserSide, FoeSide, BothSides
@battle.pbDisplay(_INTL("But there was no target..."))
user.lastMoveFailed = true
else # We have targets, or move doesn't use targets
# Reset whole damage state, perform various success checks (not accuracy)
user.initialHP = user.hp
targets.each do |b|
b.damageState.reset
b.damageState.initialHP = b.hp
if !pbSuccessCheckAgainstTarget(move,user,b)
b.damageState.unaffected = true
end
end
# Magic Coat/Magic Bounce checks (for moves which don't target Pokémon)
if targets.length==0 && move.canMagicCoat?
@battle.pbPriority(true).each do |b|
next if b.fainted? || !b.opposes?(user)
next if b.semiInvulnerable?
if b.effects[PBEffects::MagicCoat]
magicCoater = b.index
b.effects[PBEffects::MagicCoat] = false
break
elsif b.hasActiveAbility?(:MAGICBOUNCE) && !@battle.moldBreaker &&
!b.effects[PBEffects::MagicBounce]
magicBouncer = b.index
b.effects[PBEffects::MagicBounce] = true
break
end
end
end
# Get the number of hits
numHits = move.pbNumHits(user,targets)
# Process each hit in turn
realNumHits = 0
for i in 0...numHits
break if magicCoater>=0 || magicBouncer>=0
success = pbProcessMoveHit(move,user,targets,i,skipAccuracyCheck)
if !success
if i==0 && targets.length>0
hasFailed = false
targets.each do |t|
next if t.damageState.protected
hasFailed = t.damageState.unaffected
break if !t.damageState.unaffected
end
user.lastMoveFailed = hasFailed
end
break
end
realNumHits += 1
break if user.fainted?
break if user.status==PBStatuses::SLEEP || user.status==PBStatuses::FROZEN
# NOTE: If a multi-hit move becomes disabled partway through doing those
# hits (e.g. by Cursed Body), the rest of the hits continue as
# normal.
notFainted = false
break if !targets.any? { |t| !t.fainted? } # All targets are fainted
end
# Battle Arena only - attack is successful
@battle.successStates[user.index].useState = 2
if targets.length>0
@battle.successStates[user.index].typeMod = 0
targets.each do |b|
next if b.damageState.unaffected
@battle.successStates[user.index].typeMod += b.damageState.typeMod
end
end
# Effectiveness message for multi-hit moves
# NOTE: No move is both multi-hit and multi-target, and the messages below
# aren't quite right for such a hypothetical move.
if numHits>1
if move.damagingMove?
targets.each do |b|
next if b.damageState.unaffected || b.damageState.substitute
move.pbEffectivenessMessage(user,b,targets.length)
end
end
if realNumHits==1
@battle.pbDisplay(_INTL("Hit 1 time!"))
elsif realNumHits>1
@battle.pbDisplay(_INTL("Hit {1} times!",realNumHits))
end
end
# Magic Coat's bouncing back (move has targets)
targets.each do |b|
next if b.fainted?
next if !b.damageState.magicCoat && !b.damageState.magicBounce
@battle.pbShowAbilitySplash(b) if b.damageState.magicBounce
@battle.pbDisplay(_INTL("{1} bounced the {2} back!",b.pbThis,move.name))
@battle.pbHideAbilitySplash(b) if b.damageState.magicBounce
newChoice = choice.clone
newChoice[3] = user.index
newTargets = pbFindTargets(newChoice,move,b)
newTargets = pbChangeTargets(move,b,newTargets)
success = pbProcessMoveHit(move,b,newTargets,0,false)
b.lastMoveFailed = true if !success
targets.each { |b| b.pbFaint if b && b.fainted? }
user.pbFaint if user.fainted?
end
# Magic Coat's bouncing back (move has no targets)
if magicCoater>=0 || magicBouncer>=0
mc = @battle.battlers[(magicCoater>=0) ? magicCoater : magicBouncer]
if !mc.fainted?
user.lastMoveFailed = true
@battle.pbShowAbilitySplash(mc) if magicBouncer>=0
@battle.pbDisplay(_INTL("{1} bounced the {2} back!",mc.pbThis,move.name))
@battle.pbHideAbilitySplash(mc) if magicBouncer>=0
success = pbProcessMoveHit(move,mc,[],0,false)
mc.lastMoveFailed = true if !success
targets.each { |b| b.pbFaint if b && b.fainted? }
user.pbFaint if user.fainted?
end
end
# Move-specific effects after all hits
targets.each { |b| move.pbEffectAfterAllHits(user,b) }
# Faint if 0 HP
targets.each { |b| b.pbFaint if b && b.fainted? }
user.pbFaint if user.fainted?
# External/general effects after all hits. Eject Button, Shell Bell, etc.
pbEffectsAfterMove(user,targets,move,realNumHits)
end
# End effect of Mold Breaker
@battle.moldBreaker = false
# Gain Exp
@battle.pbGainExp
# Battle Arena only - update skills
@battle.eachBattler { |b| @battle.successStates[b.index].updateSkill }
# Shadow Pokémon triggering Hyper Mode
pbHyperMode if @battle.choices[@index][0]!=:None # Not if self is replaced
# End of move usage
pbEndTurn(choice)
# Instruct
@battle.eachBattler do |b|
next if !b.effects[PBEffects::Instruct]
b.effects[PBEffects::Instruct] = false
idxMove = -1
b.eachMoveWithIndex { |m,i| idxMove = i if m.id==b.lastMoveUsed }
next if idxMove<0
oldLastRoundMoved = b.lastRoundMoved
@battle.pbDisplay(_INTL("{1} used the move instructed by {2}!",b.pbThis,user.pbThis(true)))
PBDebug.logonerr{
b.effects[PBEffects::Instructed] = true
b.pbUseMoveSimple(b.lastMoveUsed,b.lastRegularMoveTarget,idxMove,false)
b.effects[PBEffects::Instructed] = false
}
b.lastRoundMoved = oldLastRoundMoved
@battle.pbJudge
return if @battle.decision>0
end
# Dancer
if !@effects[PBEffects::Dancer] && !user.lastMoveFailed && realNumHits>0 &&
!move.snatched && magicCoater<0 && @battle.pbCheckGlobalAbility(:DANCER)
dancers = []
@battle.pbPriority(true).each do |b|
dancers.push(b) if b.index!=user.index && b.hasActiveAbility?(:DANCER)
end
while dancers.length>0
nextUser = dancers.pop
oldLastRoundMoved = nextUser.lastRoundMoved
# NOTE: Petal Dance being used because of Dancer shouldn't lock the
# Dancer into using that move, and shouldn't contribute to its
# turn counter if it's already locked into Petal Dance.
oldOutrage = nextUser.effects[PBEffects::Outrage]
nextUser.effects[PBEffects::Outrage] += 1 if nextUser.effects[PBEffects::Outrage]>0
oldCurrentMove = nextUser.currentMove
preTarget = choice[3]
preTarget = user.index if nextUser.opposes?(user) || !nextUser.opposes?(preTarget)
@battle.pbShowAbilitySplash(nextUser,true)
@battle.pbHideAbilitySplash(nextUser)
if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} kept the dance going with {2}!",
nextUser.pbThis,nextUser.abilityName))
end
PBDebug.logonerr{
nextUser.effects[PBEffects::Dancer] = true
nextUser.pbUseMoveSimple(move.id,preTarget)
nextUser.effects[PBEffects::Dancer] = false
}
nextUser.lastRoundMoved = oldLastRoundMoved
nextUser.effects[PBEffects::Outrage] = oldOutrage
nextUser.currentMove = oldCurrentMove
@battle.pbJudge
return if @battle.decision>0
end
end
end
#=============================================================================
# Attack a single target
#=============================================================================
def pbProcessMoveHit(move,user,targets,hitNum,skipAccuracyCheck)
return false if user.fainted?
# For two-turn attacks being used in a single turn
move.pbInitialEffect(user,targets,hitNum)
numTargets = 0 # Number of targets that are affected by this hit
targets.each { |b| b.damageState.resetPerHit }
# Count a hit for Parental Bond (if it applies)
user.effects[PBEffects::ParentalBond] -= 1 if user.effects[PBEffects::ParentalBond]>0
# Accuracy check (accuracy/evasion calc)
if hitNum==0 || move.successCheckPerHit?
targets.each do |b|
next if b.damageState.unaffected
if pbSuccessCheckPerHit(move,user,b,skipAccuracyCheck)
numTargets += 1
else
b.damageState.missed = true
b.damageState.unaffected = true
end
end
# If failed against all targets
if targets.length>0 && numTargets==0 && !move.worksWithNoTargets?
targets.each do |b|
next if !b.damageState.missed || b.damageState.magicCoat
pbMissMessage(move,user,b)
end
move.pbCrashDamage(user)
user.pbItemHPHealCheck
pbCancelMoves
return false
end
end
# If we get here, this hit will happen and do something
#---------------------------------------------------------------------------
# Calculate damage to deal
if move.pbDamagingMove?
targets.each do |b|
next if b.damageState.unaffected
# Check whether Substitute/Disguise will absorb the damage
move.pbCheckDamageAbsorption(user,b)
# Calculate the damage against b
# pbCalcDamage shows the "eat berry" animation for SE-weakening
# berries, although the message about it comes after the additional
# effect below
move.pbCalcDamage(user,b,targets.length) # Stored in damageState.calcDamage
# Lessen damage dealt because of False Swipe/Endure/etc.
move.pbReduceDamage(user,b) # Stored in damageState.hpLost
end
end
# Show move animation (for this hit)
move.pbShowAnimation(move.id,user,targets,hitNum)
# Type-boosting Gem consume animation/message
if user.effects[PBEffects::GemConsumed]>0 && hitNum==0
# NOTE: The consume animation and message for Gems are shown now, but the
# actual removal of the item happens in def pbEffectsAfterMove.
@battle.pbCommonAnimation("UseItem",user)
@battle.pbDisplay(_INTL("The {1} strengthened {2}'s power!",
PBItems.getName(user.effects[PBEffects::GemConsumed]),move.name))
end
# Messages about missed target(s) (relevant for multi-target moves only)
targets.each do |b|
next if !b.damageState.missed
pbMissMessage(move,user,b)
end
# Deal the damage (to all allies first simultaneously, then all foes
# simultaneously)
if move.pbDamagingMove?
# This just changes the HP amounts and does nothing else
targets.each do |b|
next if b.damageState.unaffected
move.pbInflictHPDamage(b)
end
# Animate the hit flashing and HP bar changes
move.pbAnimateHitAndHPLost(user,targets)
end
# Self-Destruct/Explosion's damaging and fainting of user
move.pbSelfKO(user) if hitNum==0
user.pbFaint if user.fainted?
if move.pbDamagingMove?
targets.each do |b|
next if b.damageState.unaffected
# NOTE: This method is also used for the OKHO special message.
move.pbHitEffectivenessMessages(user,b,targets.length)
# Record data about the hit for various effects' purposes
move.pbRecordDamageLost(user,b)
end
# Close Combat/Superpower's stat-lowering, Flame Burst's splash damage,
# and Incinerate's berry destruction
targets.each do |b|
next if b.damageState.unaffected
move.pbEffectWhenDealingDamage(user,b)
end
# Ability/item effects such as Static/Rocky Helmet, and Grudge, etc.
targets.each do |b|
next if b.damageState.unaffected
pbEffectsOnMakingHit(move,user,b)
end
# Disguise/Endure/Sturdy/Focus Sash/Focus Band messages
targets.each do |b|
next if b.damageState.unaffected
move.pbEndureKOMessage(b)
end
# HP-healing held items (checks all battlers rather than just targets
# because Flame Burst's splash damage affects non-targets)
@battle.pbPriority(true).each { |b| b.pbItemHPHealCheck }
# Animate battlers fainting (checks all battlers rather than just targets
# because Flame Burst's splash damage affects non-targets)
@battle.pbPriority(true).each { |b| b.pbFaint if b && b.fainted? }
end
@battle.pbJudgeCheckpoint(user,move)
# Main effect (recoil/drain, etc.)
targets.each do |b|
next if b.damageState.unaffected
move.pbEffectAgainstTarget(user,b)
end
move.pbEffectGeneral(user)
targets.each { |b| b.pbFaint if b && b.fainted? }
user.pbFaint if user.fainted?
# Additional effect
if !user.hasActiveAbility?(:SHEERFORCE)
targets.each do |b|
next if b.damageState.calcDamage==0
chance = move.pbAdditionalEffectChance(user,b)
next if chance<=0
if @battle.pbRandom(100)<chance
move.pbAdditionalEffect(user,b)
end
end
end
# Make the target flinch (because of an item/ability)
targets.each do |b|
next if b.fainted?
next if b.damageState.calcDamage==0 || b.damageState.substitute
chance = move.pbFlinchChance(user,b)
next if chance<=0
if @battle.pbRandom(100)<chance
PBDebug.log("[Item/ability triggered] #{user.pbThis}'s King's Rock/Razor Fang or Stench")
b.pbFlinch(user)
end
end
# Message for and consuming of type-weakening berries
# NOTE: The "consume held item" animation for type-weakening berries occurs
# during pbCalcDamage above (before the move's animation), but the
# message about it only shows here.
targets.each do |b|
next if b.damageState.unaffected
next if !b.damageState.berryWeakened
@battle.pbDisplay(_INTL("The {1} weakened the damage to {2}!",b.itemName,b.pbThis(true)))
b.pbConsumeItem
end
targets.each { |b| b.pbFaint if b && b.fainted? }
user.pbFaint if user.fainted?
return true
end
end

View File

@@ -0,0 +1,193 @@
class PokeBattle_Battler
#=============================================================================
# Get move's user
#=============================================================================
def pbFindUser(choice,move)
return self
end
def pbChangeUser(choice,move,user)
# Snatch
move.snatched = false
if move.canSnatch?
newUser = nil; strength = 100
@battle.eachBattler do |b|
next if b.effects[PBEffects::Snatch]==0 ||
b.effects[PBEffects::Snatch]>=strength
next if b.effects[PBEffects::SkyDrop]>=0
newUser = b
strength = b.effects[PBEffects::Snatch]
end
if newUser
user = newUser
user.effects[PBEffects::Snatch] = 0
move.snatched = true
@battle.moldBreaker = user.hasMoldBreaker?
choice[3] = -1 # Clear pre-chosen target
end
end
return user
end
#=============================================================================
# Get move's default target(s)
#=============================================================================
def pbFindTargets(choice,move,user)
preTarget = choice[3] # A target that was already chosen
targets = []
# Get list of targets
case move.pbTarget(user) # Curse can change its target type
when PBTargets::NearAlly
targetBattler = (preTarget>=0) ? @battle.battlers[preTarget] : nil
if !pbAddTarget(targets,user,targetBattler,move)
pbAddTargetRandomAlly(targets,user,move)
end
when PBTargets::UserOrNearAlly
targetBattler = (preTarget>=0) ? @battle.battlers[preTarget] : nil
if !pbAddTarget(targets,user,targetBattler,move,true,true)
pbAddTarget(targets,user,user,move,true,true)
end
when PBTargets::NearFoe, PBTargets::NearOther
targetBattler = (preTarget>=0) ? @battle.battlers[preTarget] : nil
if !pbAddTarget(targets,user,targetBattler,move)
if preTarget>=0 && !user.opposes?(preTarget)
pbAddTargetRandomAlly(targets,user,move)
else
pbAddTargetRandomFoe(targets,user,move)
end
end
when PBTargets::AllNearFoes
@battle.eachOtherSideBattler(user.index) { |b| pbAddTarget(targets,user,b,move) }
when PBTargets::RandomNearFoe
pbAddTargetRandomFoe(targets,user,move)
when PBTargets::AllNearOthers
@battle.eachBattler { |b| pbAddTarget(targets,user,b,move) }
when PBTargets::Other
targetBattler = (preTarget>=0) ? @battle.battlers[preTarget] : nil
if !pbAddTarget(targets,user,targetBattler,move,false)
if preTarget>=0 && !user.opposes?(preTarget)
pbAddTargetRandomAlly(targets,user,move,false)
else
pbAddTargetRandomFoe(targets,user,move,false)
end
end
when PBTargets::UserAndAllies
pbAddTarget(targets,user,user,move,true,true)
@battle.eachSameSideBattler(user.index) { |b| pbAddTarget(targets,user,b,move,false,true) }
when PBTargets::AllFoes
@battle.eachOtherSideBattler(user.index) { |b| pbAddTarget(targets,user,b,move,false) }
when PBTargets::AllBattlers
@battle.eachBattler { |b| pbAddTarget(targets,user,b,move,false,true) }
else
# Used by Counter/Mirror Coat/Metal Burst/Bide
move.pbAddTarget(targets,user) # Move-specific pbAddTarget, not the def below
end
return targets
end
#=============================================================================
# Redirect attack to another target
#=============================================================================
def pbChangeTargets(move,user,targets)
targetType = move.pbTarget(user)
return targets if @battle.switching # For Pursuit interrupting a switch
return targets if move.cannotRedirect?
return targets if !PBTargets.canChooseOneFoeTarget?(targetType) || targets.length!=1
priority = @battle.pbPriority(true)
nearOnly = !PBTargets.canChooseDistantTarget?(move.target)
# Spotlight (takes priority over Follow Me/Rage Powder/Lightning Rod/Storm Drain)
newTarget = nil; strength = 100 # Lower strength takes priority
priority.each do |b|
next if b.fainted? || b.effects[PBEffects::SkyDrop]>=0
next if b.effects[PBEffects::Spotlight]==0 ||
b.effects[PBEffects::Spotlight]>=strength
next if !b.opposes?(user)
next if nearOnly && !b.near?(user)
newTarget = b
strength = b.effects[PBEffects::Spotlight]
end
if newTarget
PBDebug.log("[Move target changed] #{newTarget.pbThis}'s Spotlight made it the target")
targets = []
pbAddTarget(targets,user,newTarget,move,nearOnly)
return targets
end
# Follow Me/Rage Powder (takes priority over Lightning Rod/Storm Drain)
newTarget = nil; strength = 100 # Lower strength takes priority
priority.each do |b|
next if b.fainted? || b.effects[PBEffects::SkyDrop]>=0
next if b.effects[PBEffects::RagePowder] && !user.affectedByPowder?
next if b.effects[PBEffects::FollowMe]==0 ||
b.effects[PBEffects::FollowMe]>=strength
next if !b.opposes?(user)
next if nearOnly && !b.near?(user)
newTarget = b
strength = b.effects[PBEffects::FollowMe]
end
if newTarget
PBDebug.log("[Move target changed] #{newTarget.pbThis}'s Follow Me/Rage Powder made it the target")
targets = []
pbAddTarget(targets,user,newTarget,move,nearOnly)
return targets
end
# Lightning Rod
targets = pbChangeTargetByAbility(:LIGHTNINGROD,:ELECTRIC,move,user,targets,priority,nearOnly)
# Storm Drain
targets = pbChangeTargetByAbility(:STORMDRAIN,:WATER,move,user,targets,priority,nearOnly)
return targets
end
def pbChangeTargetByAbility(drawingAbility,drawnType,move,user,targets,priority,nearOnly)
return targets if !isConst?(move.calcType,PBTypes,drawnType)
return targets if targets[0].hasActiveAbility?(drawingAbility)
priority.each do |b|
next if b.index==user.index || b.index==targets[0].index
next if !b.hasActiveAbility?(drawingAbility)
next if nearOnly && !b.near?(user)
@battle.pbShowAbilitySplash(b)
targets.clear
pbAddTarget(targets,user,b,move,nearOnly)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} took the attack!",b.pbThis))
else
@battle.pbDisplay(_INTL("{1} took the attack with its {2}!",b.pbThis,b.abilityName))
end
@battle.pbHideAbilitySplash(b)
break
end
return targets
end
#=============================================================================
# Register target
#=============================================================================
def pbAddTarget(targets,user,target,move,nearOnly=true,allowUser=false)
return false if !target || (target.fainted? && !move.cannotRedirect?)
return false if !(allowUser && user==target) && nearOnly && !user.near?(target)
targets.each { |b| return true if b.index==target.index } # Already added
targets.push(target)
return true
end
def pbAddTargetRandomAlly(targets,user,move,nearOnly=true)
choices = []
user.eachAlly do |b|
next if nearOnly && !user.near?(b)
pbAddTarget(choices,user,b,nearOnly)
end
if choices.length>0
pbAddTarget(targets,user,choices[@battle.pbRandom(choices.length)],nearOnly)
end
end
def pbAddTargetRandomFoe(targets,user,move,nearOnly=true)
choices = []
user.eachOpposing do |b|
next if nearOnly && !user.near?(b)
pbAddTarget(choices,user,b,nearOnly)
end
if choices.length>0
pbAddTarget(targets,user,choices[@battle.pbRandom(choices.length)],nearOnly)
end
end
end

View File

@@ -0,0 +1,538 @@
class PokeBattle_Battler
#=============================================================================
# Decide whether the trainer is allowed to tell the Pokémon to use the given
# move. Called when choosing a command for the round.
# Also called when processing the Pokémon's action, because these effects also
# prevent Pokémon action. Relevant because these effects can become active
# earlier in the same round (after choosing the command but before using the
# move) or an unusable move may be called by another move such as Metronome.
#=============================================================================
def pbCanChooseMove?(move,commandPhase,showMessages=true,specialUsage=false)
# Disable
if @effects[PBEffects::DisableMove]==move.id && !specialUsage
if showMessages
msg = _INTL("{1}'s {2} is disabled!",pbThis,move.name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
# Heal Block
if @effects[PBEffects::HealBlock]>0 && move.healingMove?
if showMessages
msg = _INTL("{1} can't use {2} because of Heal Block!",pbThis,move.name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
# Gravity
if @battle.field.effects[PBEffects::Gravity]>0 && move.unusableInGravity?
if showMessages
msg = _INTL("{1} can't use {2} because of gravity!",pbThis,move.name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
# Throat Chop
if @effects[PBEffects::ThroatChop]>0 && move.soundMove?
if showMessages
msg = _INTL("{1} can't use {2} because of Throat Chop!",pbThis,move.name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
# Choice Band
if @effects[PBEffects::ChoiceBand]>=0
if hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF]) &&
pbHasMove?(@effects[PBEffects::ChoiceBand])
if move.id!=@effects[PBEffects::ChoiceBand]
if showMessages
msg = _INTL("{1} allows the use of only {2}!",itemName,
PBMoves.getName(@effects[PBEffects::ChoiceBand]))
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
else
@effects[PBEffects::ChoiceBand] = -1
end
end
# Taunt
if @effects[PBEffects::Taunt]>0 && move.statusMove?
if showMessages
msg = _INTL("{1} can't use {2} after the taunt!",pbThis,move.name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
# Torment
if @effects[PBEffects::Torment] && !@effects[PBEffects::Instructed] &&
move.id==@lastMoveUsed && move.id!=@battle.struggle.id
if showMessages
msg = _INTL("{1} can't use the same move twice in a row due to the torment!",pbThis)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
# Imprison
@battle.eachOtherSideBattler(@index) do |b|
next if !b.effects[PBEffects::Imprison] || !b.pbHasMove?(move.id)
if showMessages
msg = _INTL("{1} can't use its sealed {2}!",pbThis,move.name)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
# Assault Vest (prevents choosing status moves but doesn't prevent
# executing them)
if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && commandPhase
if showMessages
msg = _INTL("The effects of the {1} prevent status moves from being used!",
itemName)
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
end
return false
end
# Belch
return false if !move.pbCanChooseMove?(self,commandPhase,showMessages)
return true
end
#=============================================================================
# Obedience check
#=============================================================================
# Return true if Pokémon continues attacking (although it may have chosen to
# use a different move in disobedience), or false if attack stops.
def pbObedienceCheck?(choice)
return true if usingMultiTurnAttack?
return true if choice[0]!=:UseMove
return true if !@battle.internalBattle
return true if !@battle.pbOwnedByPlayer?(@index)
disobedient = false
# Pokémon may be disobedient; calculate if it is
badgeLevel = 10*(@battle.pbPlayer.numbadges+1)
badgeLevel = PBExperience.maxLevel if @battle.pbPlayer.numbadges>=8
if @pokemon.foreign?(@battle.pbPlayer) && @level>badgeLevel
a = ((@level+badgeLevel)*@battle.pbRandom(256)/256).floor
disobedient |= (a>=badgeLevel)
end
disobedient |= !pbHyperModeObedience(choice[2])
return true if !disobedient
# Pokémon is disobedient; make it do something else
return pbDisobey(choice,badgeLevel)
end
def pbDisobey(choice,badgeLevel)
move = choice[2]
PBDebug.log("[Disobedience] #{pbThis} disobeyed")
@effects[PBEffects::Rage] = false
# Do nothing if using Snore/Sleep Talk
if @status==PBStatuses::SLEEP && move.usableWhenAsleep?
@battle.pbDisplay(_INTL("{1} ignored orders and kept sleeping!",pbThis))
return false
end
b = ((@level+badgeLevel)*@battle.pbRandom(256)/256).floor
# Use another move
if b<badgeLevel
@battle.pbDisplay(_INTL("{1} ignored orders!",pbThis))
return false if !@battle.pbCanShowFightMenu?(@index)
otherMoves = []
eachMoveWithIndex do |m,i|
next if i==choice[1]
otherMoves[otherMoves.length] = i if @battle.pbCanChooseMove?(@index,i,false)
end
return false if otherMoves.length==0 # No other move to use; do nothing
newChoice = otherMoves[@battle.pbRandom(otherMoves.length)]
choice[1] = newChoice
choice[2] = @moves[newChoice]
choice[3] = -1
return true
end
c = @level-badgeLevel
r = @battle.pbRandom(256)
# Fall asleep
if r<c && pbCanSleep?(self,false)
pbSleepSelf(_INTL("{1} began to nap!",pbThis))
return false
end
# Hurt self in confusion
r -= c
if r<c && @status!=PBStatuses::SLEEP
pbConfusionDamage(_INTL("{1} won't obey! It hurt itself in its confusion!",pbThis))
return false
end
# Show refusal message and do nothing
case @battle.pbRandom(4)
when 0; @battle.pbDisplay(_INTL("{1} won't obey!",pbThis))
when 1; @battle.pbDisplay(_INTL("{1} turned away!",pbThis))
when 2; @battle.pbDisplay(_INTL("{1} is loafing around!",pbThis))
when 3; @battle.pbDisplay(_INTL("{1} pretended not to notice!",pbThis))
end
return false
end
#=============================================================================
# Check whether the user (self) is able to take action at all.
# If this returns true, and if PP isn't a problem, the move will be considered
# to have been used (even if it then fails for whatever reason).
#=============================================================================
def pbTryUseMove(choice,move,specialUsage,skipAccuracyCheck)
# Check whether it's possible for self to use the given move
# NOTE: Encore has already changed the move being used, no need to have a
# check for it here.
if !pbCanChooseMove?(move,false,true,specialUsage)
@lastMoveFailed = true
return false
end
# Check whether it's possible for self to do anything at all
if @effects[PBEffects::SkyDrop]>=0 # Intentionally no message here
PBDebug.log("[Move failed] #{pbThis} can't use #{move.name} because of being Sky Dropped")
return false
end
if @effects[PBEffects::HyperBeam]>0 # Intentionally before Truant
@battle.pbDisplay(_INTL("{1} must recharge!",pbThis))
return false
end
if choice[1]==-2 # Battle Palace
@battle.pbDisplay(_INTL("{1} appears incapable of using its power!",pbThis))
return false
end
# Skip checking all applied effects that could make self fail doing something
return true if skipAccuracyCheck
# Check status problems and continue their effects/cure them
case @status
when PBStatuses::SLEEP
self.statusCount -= 1
if @statusCount<=0
pbCureStatus
else
pbContinueStatus
if !move.usableWhenAsleep? # Snore/Sleep Talk
@lastMoveFailed = true
return false
end
end
when PBStatuses::FROZEN
if !move.thawsUser?
if @battle.pbRandom(100)<20
pbCureStatus
else
pbContinueStatus
@lastMoveFailed = true
return false
end
end
end
# Obedience check
return false if !pbObedienceCheck?(choice)
# Truant
if hasActiveAbility?(:TRUANT)
@effects[PBEffects::Truant] = !@effects[PBEffects::Truant]
if !@effects[PBEffects::Truant] # True means loafing, but was just inverted
@battle.pbShowAbilitySplash(self)
@battle.pbDisplay(_INTL("{1} is loafing around!",pbThis))
@lastMoveFailed = true
@battle.pbHideAbilitySplash(self)
return false
end
end
# Flinching
if @effects[PBEffects::Flinch]
@battle.pbDisplay(_INTL("{1} flinched and couldn't move!",pbThis))
if abilityActive?
BattleHandlers.triggerAbilityOnFlinch(@ability,self,@battle)
end
@lastMoveFailed = true
return false
end
# Confusion
if @effects[PBEffects::Confusion]>0
@effects[PBEffects::Confusion] -= 1
if @effects[PBEffects::Confusion]<=0
pbCureConfusion
@battle.pbDisplay(_INTL("{1} snapped out of its confusion.",pbThis))
else
@battle.pbCommonAnimation("Confusion",self)
@battle.pbDisplay(_INTL("{1} is confused!",pbThis))
threshold = (NEWEST_BATTLE_MECHANICS) ? 33 : 50 # % chance
if @battle.pbRandom(100)<threshold
pbConfusionDamage(_INTL("It hurt itself in its confusion!"))
@lastMoveFailed = true
return false
end
end
end
# Paralysis
if @status==PBStatuses::PARALYSIS
if @battle.pbRandom(100)<25
pbContinueStatus
@lastMoveFailed = true
return false
end
end
# Infatuation
if @effects[PBEffects::Attract]>=0
@battle.pbCommonAnimation("Attract",self)
@battle.pbDisplay(_INTL("{1} is in love with {2}!",pbThis,
@battle.battlers[@effects[PBEffects::Attract]].pbThis(true)))
if @battle.pbRandom(100)<50
@battle.pbDisplay(_INTL("{1} is immobilized by love!",pbThis))
@lastMoveFailed = true
return false
end
end
return true
end
#=============================================================================
# Initial success check against the target. Done once before the first hit.
# Includes move-specific failure conditions, protections and type immunities.
#=============================================================================
def pbSuccessCheckAgainstTarget(move,user,target)
typeMod = move.pbCalcTypeMod(move.calcType,user,target)
target.damageState.typeMod = typeMod
# Two-turn attacks can't fail here in the charging turn
return true if user.effects[PBEffects::TwoTurnAttack]>0
# Move-specific failures
return false if move.pbFailsAgainstTarget?(user,target)
# Immunity to priority moves because of Psychic Terrain
if @battle.field.terrain==PBBattleTerrains::Psychic && target.affectedByTerrain? &&
target.opposes?(user) &&
@battle.choices[user.index][4]>0 # Move priority saved from pbCalculatePriority
@battle.pbDisplay(_INTL("{1} surrounds itself with psychic terrain!",target.pbThis))
return false
end
# Crafty Shield
if target.pbOwnSide.effects[PBEffects::CraftyShield] && user.index!=target.index &&
move.statusMove? && move.pbTarget(user)!=PBTargets::AllBattlers
@battle.pbCommonAnimation("CraftyShield",target)
@battle.pbDisplay(_INTL("Crafty Shield protected {1}!",target.pbThis(true)))
target.damageState.protected = true
@battle.successStates[user.index].protected = true
return false
end
# Wide Guard
if target.pbOwnSide.effects[PBEffects::WideGuard] && user.index!=target.index &&
PBTargets.multipleTargets?(move.pbTarget(user)) &&
(NEWEST_BATTLE_MECHANICS || move.damagingMove?)
@battle.pbCommonAnimation("WideGuard",target)
@battle.pbDisplay(_INTL("Wide Guard protected {1}!",target.pbThis(true)))
target.damageState.protected = true
@battle.successStates[user.index].protected = true
return false
end
if move.canProtectAgainst?
# Quick Guard
if target.pbOwnSide.effects[PBEffects::QuickGuard] &&
@battle.choices[user.index][4]>0 # Move priority saved from pbCalculatePriority
@battle.pbCommonAnimation("QuickGuard",target)
@battle.pbDisplay(_INTL("Quick Guard protected {1}!",target.pbThis(true)))
target.damageState.protected = true
@battle.successStates[user.index].protected = true
return false
end
# Protect
if target.effects[PBEffects::Protect]
@battle.pbCommonAnimation("Protect",target)
@battle.pbDisplay(_INTL("{1} protected itself!",target.pbThis))
target.damageState.protected = true
@battle.successStates[user.index].protected = true
return false
end
# King's Shield
if target.effects[PBEffects::KingsShield] && move.damagingMove?
@battle.pbCommonAnimation("KingsShield",target)
@battle.pbDisplay(_INTL("{1} protected itself!",target.pbThis))
target.damageState.protected = true
@battle.successStates[user.index].protected = true
if move.pbContactMove?(user) && user.affectedByContactEffect?
if user.pbCanLowerStatStage?(PBStats::ATTACK)
user.pbLowerStatStage(PBStats::ATTACK,2,nil)
end
end
return false
end
# Spiky Shield
if target.effects[PBEffects::SpikyShield]
@battle.pbCommonAnimation("SpikyShield",target)
@battle.pbDisplay(_INTL("{1} protected itself!",target.pbThis))
target.damageState.protected = true
@battle.successStates[user.index].protected = true
if move.pbContactMove?(user) && user.affectedByContactEffect?
@battle.scene.pbDamageAnimation(user)
user.pbReduceHP(user.totalhp/8,false)
@battle.pbDisplay(_INTL("{1} was hurt!",user.pbThis))
user.pbItemHPHealCheck
end
return false
end
# Baneful Bunker
if target.effects[PBEffects::BanefulBunker]
@battle.pbCommonAnimation("BanefulBunker",target)
@battle.pbDisplay(_INTL("{1} protected itself!",target.pbThis))
target.damageState.protected = true
@battle.successStates[user.index].protected = true
if move.pbContactMove?(user) && user.affectedByContactEffect?
user.pbPoison(target) if user.pbCanPoison?(target,false)
end
return false
end
# Mat Block
if target.pbOwnSide.effects[PBEffects::MatBlock] && move.damagingMove?
# NOTE: Confirmed no common animation for this effect.
@battle.pbDisplay(_INTL("{1} was blocked by the kicked-up mat!",move.name))
target.damageState.protected = true
@battle.successStates[user.index].protected = true
return false
end
end
# Magic Coat/Magic Bounce
if move.canMagicCoat? && !target.semiInvulnerable? && target.opposes?(user)
if target.effects[PBEffects::MagicCoat]
target.damageState.magicCoat = true
target.effects[PBEffects::MagicCoat] = false
return false
end
if target.hasActiveAbility?(:MAGICBOUNCE) && !@battle.moldBreaker &&
!target.effects[PBEffects::MagicBounce]
target.damageState.magicBounce = true
target.effects[PBEffects::MagicBounce] = true
return false
end
end
# Immunity because of ability (intentionally before type immunity check)
return false if move.pbImmunityByAbility(user,target)
# Type immunity
if move.pbDamagingMove? && PBTypes.ineffective?(typeMod)
PBDebug.log("[Target immune] #{target.pbThis}'s type immunity")
@battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true)))
return false
end
# Dark-type immunity to moves made faster by Prankster
if NEWEST_BATTLE_MECHANICS && user.effects[PBEffects::Prankster] &&
target.pbHasType?(:DARK) && target.opposes?(user)
PBDebug.log("[Target immune] #{target.pbThis} is Dark-type and immune to Prankster-boosted moves")
@battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true)))
return false
end
# Airborne-based immunity to Ground moves
if move.damagingMove? && isConst?(move.calcType,PBTypes,:GROUND) &&
target.airborne? && !move.hitsFlyingTargets?
if target.hasActiveAbility?(:LEVITATE) && !@battle.moldBreaker
@battle.pbShowAbilitySplash(target)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} avoided the attack!",target.pbThis))
else
@battle.pbDisplay(_INTL("{1} avoided the attack with {2}!",target.pbThis,target.abilityName))
end
@battle.pbHideAbilitySplash(target)
return false
end
if target.hasActiveItem?(:AIRBALLOON)
@battle.pbDisplay(_INTL("{1}'s {2} makes Ground moves miss!",target.pbThis,target.itemName))
return false
end
if target.effects[PBEffects::MagnetRise]>0
@battle.pbDisplay(_INTL("{1} makes Ground moves miss with Magnet Rise!",target.pbThis))
return false
end
if target.effects[PBEffects::Telekinesis]>0
@battle.pbDisplay(_INTL("{1} makes Ground moves miss with Telekinesis!",target.pbThis))
return false
end
end
# Immunity to powder-based moves
if NEWEST_BATTLE_MECHANICS && move.powderMove?
if target.pbHasType?(:GRASS)
PBDebug.log("[Target immune] #{target.pbThis} is Grass-type and immune to powder-based moves")
@battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true)))
return false
end
if target.hasActiveAbility?(:OVERCOAT) && !@battle.moldBreaker
@battle.pbShowAbilitySplash(target)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true)))
else
@battle.pbDisplay(_INTL("It doesn't affect {1} because of its {2}.",target.pbThis(true),target.abilityName))
end
@battle.pbHideAbilitySplash(target)
return false
end
if target.hasActiveItem?(:SAFETYGOGGLES)
PBDebug.log("[Item triggered] #{target.pbThis} has Safety Goggles and is immune to powder-based moves")
@battle.pbDisplay(_INTL("It doesn't affect {1}...",target.pbThis(true)))
return false
end
end
# Substitute
if target.effects[PBEffects::Substitute]>0 && move.statusMove? &&
!move.ignoresSubstitute?(user) && user.index!=target.index
PBDebug.log("[Target immune] #{target.pbThis} is protected by its Substitute")
@battle.pbDisplay(_INTL("{1} avoided the attack!",target.pbThis(true)))
return false
end
return true
end
#=============================================================================
# Per-hit success check against the target.
# Includes semi-invulnerable move use and accuracy calculation.
#=============================================================================
def pbSuccessCheckPerHit(move,user,target,skipAccuracyCheck)
# Two-turn attacks can't fail here in the charging turn
return true if user.effects[PBEffects::TwoTurnAttack]>0
# Lock-On
return true if user.effects[PBEffects::LockOn]>0 &&
user.effects[PBEffects::LockOnPos]==target.index
# Toxic
return true if move.pbOverrideSuccessCheckPerHit(user,target)
miss = false; hitsInvul = false
# No Guard
hitsInvul = true if user.hasActiveAbility?(:NOGUARD) ||
target.hasActiveAbility?(:NOGUARD)
# Future Sight
hitsInvul = true if @battle.futureSight
# Helping Hand
hitsInvul = true if move.function=="09C"
if !hitsInvul
# Semi-invulnerable moves
if target.effects[PBEffects::TwoTurnAttack]>0
if target.inTwoTurnAttack?("0C9","0CC","0CE") # Fly, Bounce, Sky Drop
miss = true if !move.hitsFlyingTargets?
elsif target.inTwoTurnAttack?("0CA") # Dig
miss = true if !move.hitsDiggingTargets?
elsif target.inTwoTurnAttack?("0CB") # Dive
miss = true if !move.hitsDivingTargets?
elsif target.inTwoTurnAttack?("0CD","14D") # Shadow Force, Phantom Force
miss = true
end
end
if target.effects[PBEffects::SkyDrop]>=0 &&
target.effects[PBEffects::SkyDrop]!=user.index
miss = true if !move.hitsFlyingTargets?
end
end
if !miss
# Called by another move
return true if skipAccuracyCheck
# Accuracy check
return true if move.pbAccuracyCheck(user,target) # Includes Counter/Mirror Coat
end
# Missed
PBDebug.log("[Move failed] Failed pbAccuracyCheck or target is semi-invulnerable")
return false
end
#=============================================================================
# Message shown when a move fails the per-hit success check above.
#=============================================================================
def pbMissMessage(move,user,target)
tar = move.pbTarget(user)
if PBTargets.multipleTargets?(tar)
@battle.pbDisplay(_INTL("{1} avoided the attack!",target.pbThis))
elsif target.effects[PBEffects::TwoTurnAttack]>0
@battle.pbDisplay(_INTL("{1} avoided the attack!",target.pbThis))
elsif !move.pbMissMessage(user,target)
@battle.pbDisplay(_INTL("{1}'s attack missed!",user.pbThis))
end
end
end

View File

@@ -0,0 +1,188 @@
class PokeBattle_Battler
#=============================================================================
# Effect per hit
#=============================================================================
def pbEffectsOnMakingHit(move,user,target)
if target.damageState.calcDamage>0 && !target.damageState.substitute
# Target's ability
if target.abilityActive?(true)
oldHP = user.hp
BattleHandlers.triggerTargetAbilityOnHit(target.ability,user,target,move,@battle)
user.pbItemHPHealCheck if user.hp<oldHP
end
# User's ability
if user.abilityActive?(true)
BattleHandlers.triggerUserAbilityOnHit(user.ability,user,target,move,@battle)
user.pbItemHPHealCheck
end
# Target's item
if target.itemActive?(true)
oldHP = user.hp
BattleHandlers.triggerTargetItemOnHit(target.item,user,target,move,@battle)
user.pbItemHPHealCheck if user.hp<oldHP
end
end
if target.opposes?(user)
# Rage
if target.effects[PBEffects::Rage] && !target.fainted?
if target.pbCanRaiseStatStage?(PBStats::ATTACK,target)
@battle.pbDisplay(_INTL("{1}'s rage is building!",target.pbThis))
target.pbRaiseStatStage(PBStats::ATTACK,1,target)
end
end
# Beak Blast
if target.effects[PBEffects::BeakBlast]
PBDebug.log("[Lingering effect] #{target.pbThis}'s Beak Blast")
if move.pbContactMove?(user) && user.affectedByContactEffect?
target.pbBurn(user) if target.pbCanBurn?(user,false,self)
end
end
# Shell Trap (make the trapper move next if the trap was triggered)
if target.effects[PBEffects::ShellTrap] &&
@battle.choices[target.index][0]==:UseMove && !target.movedThisRound?
if target.damageState.hpLost>0 && !target.damageState.substitute && move.physicalMove?
target.tookPhysicalHit = true
target.effects[PBEffects::MoveNext] = true
target.effects[PBEffects::Quash] = 0
end
end
# Grudge
if target.effects[PBEffects::Grudge] && target.fainted?
move.pp = 0
@battle.pbDisplay(_INTL("{1}'s {2} lost all of its PP due to the grudge!",
user.pbThis,move.name))
end
# Destiny Bond (recording that it should apply)
if target.effects[PBEffects::DestinyBond] && target.fainted?
if user.effects[PBEffects::DestinyBondTarget]<0
user.effects[PBEffects::DestinyBondTarget] = target.index
end
end
end
end
#=============================================================================
# Effects after all hits (i.e. at end of move usage)
#=============================================================================
def pbEffectsAfterMove(user,targets,move,numHits)
# Defrost
if move.damagingMove?
targets.each do |b|
next if b.damageState.unaffected || b.damageState.substitute
next if b.status!=PBStatuses::FROZEN
# NOTE: Non-Fire-type moves that thaw the user will also thaw the
# target (in Gen 6+).
if isConst?(move.calcType,PBTypes,:FIRE) ||
(NEWEST_BATTLE_MECHANICS && move.thawsUser?)
b.pbCureStatus
end
end
end
# Destiny Bond
# NOTE: Although Destiny Bond is similar to Grudge, they don't apply at
# the same time (although Destiny Bond does check whether it's going
# to trigger at the same time as Grudge).
if user.effects[PBEffects::DestinyBondTarget]>=0 && !user.fainted?
dbName = @battle.battlers[user.effects[PBEffects::DestinyBondTarget]].pbThis
@battle.pbDisplay(_INTL("{1} took its attacker down with it!",dbName))
user.pbReduceHP(user.hp,false)
user.pbItemHPHealCheck
user.pbFaint
@battle.pbJudgeCheckpoint(user)
end
# User's ability
if user.abilityActive?
BattleHandlers.triggerUserAbilityEndOfMove(user.ability,user,targets,move,@battle)
end
# Greninja - Battle Bond
if !user.fainted? && !user.effects[PBEffects::Transform] &&
isConst?(user.species,PBSpecies,:GRENINJA) &&
isConst?(user.ability,PBAbilities,:BATTLEBOND)
if !@battle.pbAllFainted?(user.idxOpposingSide) &&
!@battle.battleBond[user.index&1][user.pokemonIndex]
numFainted = 0
targets.each { |b| numFainted += 1 if b.damageState.fainted }
if numFainted>0 && user.form!=1
@battle.battleBond[user.index&1][user.pokemonIndex] = true
@battle.pbDisplay(_INTL("{1} became fully charged due to its bond with its Trainer!",user.pbThis))
@battle.pbShowAbilitySplash(user,true)
@battle.pbHideAbilitySplash(user)
user.pbChangeForm(1,_INTL("{1} became Ash-Greninja!",user.pbThis))
end
end
end
# Consume user's Gem
if user.effects[PBEffects::GemConsumed]>0
# NOTE: The consume animation and message for Gems are shown immediately
# after the move's animation, but the item is only consumed now.
user.pbConsumeItem
end
# Pokémon switching caused by Roar, Whirlwind, Circle Throw, Dragon Tail
switchedBattlers = []
move.pbSwitchOutTargetsEffect(user,targets,numHits,switchedBattlers)
# Target's item, user's item, target's ability (all negated by Sheer Force)
if move.addlEffect==0 || !user.hasActiveAbility?(:SHEERFORCE)
pbEffectsAfterMove2(user,targets,move,numHits,switchedBattlers)
end
# Some move effects that need to happen here, i.e. U-turn/Volt Switch
# switching, Baton Pass switching, Parting Shot switching, Relic Song's form
# changing, Fling/Natural Gift consuming item.
if !switchedBattlers.include?(user.index)
move.pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers)
end
if numHits>0
@battle.eachBattler { |b| b.pbItemEndOfMoveCheck }
end
end
# Everything in this method is negated by Sheer Force.
def pbEffectsAfterMove2(user,targets,move,numHits,switchedBattlers)
hpNow = user.hp # Intentionally determined now, before Shell Bell
# Target's held item (Eject Button, Red Card)
switchByItem = []
@battle.pbPriority(true).each do |b|
next if !targets.any? { |targetB| targetB.index==b.index }
next if b.damageState.unaffected || b.damageState.calcDamage==0 ||
switchedBattlers.include?(b.index)
next if !b.itemActive?
BattleHandlers.triggerTargetItemAfterMoveUse(b.item,b,user,move,switchByItem,@battle)
end
@battle.moldBreaker = false if switchByItem.include?(user.index)
@battle.pbPriority(true).each do |b|
b.pbEffectsOnSwitchIn(true) if switchByItem.include?(b.index)
end
switchByItem.each { |idxB| switchedBattlers.push(idxB) }
# User's held item (Life Orb, Shell Bell)
if !switchedBattlers.include?(user.index) && user.itemActive?
BattleHandlers.triggerUserItemAfterMoveUse(user.item,user,targets,move,numHits,@battle)
end
# Target's ability (Berserk, Color Change, Emergency Exit, Pickpocket, Wimp Out)
switchWimpOut = []
@battle.pbPriority(true).each do |b|
next if !targets.any? { |targetB| targetB.index==b.index }
next if b.damageState.unaffected || switchedBattlers.include?(b.index)
next if !b.abilityActive?
BattleHandlers.triggerTargetAbilityAfterMoveUse(b.ability,b,user,move,switchedBattlers,@battle)
if !switchedBattlers.include?(b.index) && move.damagingMove?
if b.pbAbilitiesOnDamageTaken(b.damageState.initialHP) # Emergency Exit, Wimp Out
switchWimpOut.push(b.index)
end
end
end
@battle.moldBreaker = false if switchWimpOut.include?(user.index)
@battle.pbPriority(true).each do |b|
next if b.index==user.index
b.pbEffectsOnSwitchIn(true) if switchWimpOut.include?(b.index)
end
switchWimpOut.each { |idxB| switchedBattlers.push(idxB) }
# User's ability (Emergency Exit, Wimp Out)
if !switchedBattlers.include?(user.index) && move.damagingMove?
hpNow = user.hp if user.hp<hpNow # In case HP was lost because of Life Orb
if user.pbAbilitiesOnDamageTaken(user.initialHP,hpNow)
@battle.moldBreaker = false
user.pbEffectsOnSwitchIn(true)
switchedBattlers.push(user.index)
end
end
end
end

View File

@@ -0,0 +1,30 @@
begin
module PBEnvironment
None = 0
Grass = 1
TallGrass = 2
MovingWater = 3
StillWater = 4
Puddle = 5
Underwater = 6
Cave = 7
Rock = 8
Sand = 9
Forest = 10
ForestGrass = 11
Snow = 12
Ice = 13
Volcano = 14
Graveyard = 15
Sky = 16
Space = 17
UltraSpace = 18
def self.maxValue; return 18; end
end
rescue Exception
if $!.is_a?(SystemExit) || "#{$!.class}"=="Reset"
raise $!
end
end

View File

@@ -0,0 +1,140 @@
class PokeBattle_Move
attr_reader :battle
attr_reader :realMove
attr_accessor :id
attr_reader :name
attr_reader :function
attr_reader :baseDamage
attr_reader :type
attr_reader :category
attr_reader :accuracy
attr_accessor :pp
attr_writer :totalpp
attr_reader :addlEffect
attr_reader :target
attr_reader :priority
attr_reader :flags
attr_accessor :calcType
attr_accessor :powerBoost
attr_accessor :snatched
def to_int; return @id; end
#=============================================================================
# Creating a move
#=============================================================================
def initialize(battle,move)
@battle = battle
@realMove = move
@id = move.id
@name = PBMoves.getName(@id) # Get the move's name
# Get data on the move
moveData = pbGetMoveData(@id)
@function = moveData[MOVE_FUNCTION_CODE]
@baseDamage = moveData[MOVE_BASE_DAMAGE]
@type = moveData[MOVE_TYPE]
@category = moveData[MOVE_CATEGORY]
@accuracy = moveData[MOVE_ACCURACY]
@pp = move.pp # Can be changed with Mimic/Transform
@addlEffect = moveData[MOVE_EFFECT_CHANCE]
@target = moveData[MOVE_TARGET]
@priority = moveData[MOVE_PRIORITY]
@flags = moveData[MOVE_FLAGS]
@calcType = -1
@powerBoost = false # For Aerilate, Pixilate, Refrigerate, Galvanize
@snatched = false
end
# This is the code actually used to generate a PokeBattle_Move object. The
# object generated is a subclass of this one which depends on the move's
# function code (found in the script section PokeBattle_MoveEffect).
def PokeBattle_Move.pbFromPBMove(battle,move)
move = PBMove.new(0) if !move
moveFunction = pbGetMoveData(move.id,MOVE_FUNCTION_CODE) || "000"
className = sprintf("PokeBattle_Move_%s",moveFunction)
if Object.const_defined?(className)
return Object.const_get(className).new(battle,move)
end
return PokeBattle_UnimplementedMove.new(battle,move)
end
#=============================================================================
# About the move
#=============================================================================
def pbTarget(user); return @target; end
def totalpp
return @totalpp if @totalpp && @totalpp>0 # Usually undefined
return @realMove.totalpp if @realMove
return 0
end
# NOTE: This method is only ever called while using a move (and also by the
# AI), so using @calcType here is acceptable.
def physicalMove?(thisType=nil)
return (@category==0) if MOVE_CATEGORY_PER_MOVE
thisType ||= @calcType if @calcType>=0
thisType = @type if !thisType
return !PBTypes.isSpecialType?(thisType)
end
# NOTE: This method is only ever called while using a move (and also by the
# AI), so using @calcType here is acceptable.
def specialMove?(thisType=nil)
return (@category==1) if MOVE_CATEGORY_PER_MOVE
thisType ||= @calcType if @calcType>=0
thisType = @type if !thisType
return PBTypes.isSpecialType?(thisType)
end
def damagingMove?; return @category!=2; end
def statusMove?; return @category==2; end
def usableWhenAsleep?; return false; end
def unusableInGravity?; return false; end
def healingMove?; return false; end
def recoilMove?; return false; end
def flinchingMove?; return false; end
def callsAnotherMove?; return false; end
# Whether the move can/will hit more than once in the same turn (including
# Beat Up which may instead hit just once). Not the same as pbNumHits>1.
def multiHitMove?; return false; end
def chargingTurnMove?; return false; end
def successCheckPerHit?; return false; end
def hitsFlyingTargets?; return false; end
def hitsDiggingTargets?; return false; end
def hitsDivingTargets?; return false; end
def ignoresReflect?; return false; end # For Brick Break
def cannotRedirect?; return false; end # For Future Sight/Doom Desire
def worksWithNoTargets?; return false; end # For Explosion
def damageReducedByBurn?; return true; end # For Facade
def triggersHyperMode?; return false; end
def contactMove?; return @flags[/a/]; end
def canProtectAgainst?; return @flags[/b/]; end
def canMagicCoat?; return @flags[/c/]; end
def canSnatch?; return @flags[/d/]; end
def canMirrorMove?; return @flags[/e/]; end
def canKingsRock?; return @flags[/f/]; end
def thawsUser?; return @flags[/g/]; end
def highCriticalRate?; return @flags[/h/]; end
def bitingMove?; return @flags[/i/]; end
def punchingMove?; return @flags[/j/]; end
def soundMove?; return @flags[/k/]; end
def powderMove?; return @flags[/l/]; end
def pulseMove?; return @flags[/m/]; end
def bombMove?; return @flags[/n/]; end
def danceMove?; return @flags[/o/]; end
# Causes perfect accuracy (param=1) and double damage (param=2).
def tramplesMinimize?(param=1); return false; end
def nonLethal?(user,target); return false; end # For False Swipe
def ignoresSubstitute?(user) # user is the Pokémon using this move
if NEWEST_BATTLE_MECHANICS
return true if soundMove?
return true if user && user.hasActiveAbility?(:INFILTRATOR)
end
return false
end
end

View File

@@ -0,0 +1,350 @@
class PokeBattle_Move
#=============================================================================
# Effect methods per move usage
#=============================================================================
def pbCanChooseMove?(user,commandPhase,showMessages); return true; end # For Belch
def pbDisplayChargeMessage(user); end # For Focus Punch/shell Trap/Beak Blast
def pbOnStartUse(user,targets); end
def pbAddTarget(targets,user); end # For Counter, etc. and Bide
# Reset move usage counters (child classes can increment them).
def pbChangeUsageCounters(user,specialUsage)
user.effects[PBEffects::FuryCutter] = 0
user.effects[PBEffects::ParentalBond] = 0
user.effects[PBEffects::ProtectRate] = 1
@battle.field.effects[PBEffects::FusionBolt] = false
@battle.field.effects[PBEffects::FusionFlare] = false
end
def pbDisplayUseMessage(user)
@battle.pbDisplayBrief(_INTL("{1} used {2}!",user.pbThis,@name))
end
def pbMissMessage(user,target); return false; end
#=============================================================================
#
#=============================================================================
# Whether the move is currently in the "charging" turn of a two turn attack.
# Is false if Power Herb or another effect lets a two turn move charge and
# attack in the same turn.
# user.effects[PBEffects::TwoTurnAttack] is set to the move's ID during the
# charging turn, and is 0 during the attack turn.
def pbIsChargingTurn?(user); return false; end
def pbDamagingMove?; return damagingMove?; end
def pbContactMove?(user)
return false if user.hasActiveAbility?(:LONGREACH)
return contactMove?
end
# The maximum number of hits in a round this move will actually perform. This
# can be 1 for Beat Up, and can be 2 for any moves affected by Parental Bond.
def pbNumHits(user,targets)
if user.hasActiveAbility?(:PARENTALBOND) && pbDamagingMove? &&
!chargingTurnMove? && targets.length==1
# Record that Parental Bond applies, to weaken the second attack
user.effects[PBEffects::ParentalBond] = 3
return 2
end
return 1
end
#=============================================================================
# Effect methods per hit
#=============================================================================
def pbOverrideSuccessCheckPerHit(user,target); return false; end
def pbCrashDamage(user); end
def pbInitialEffect(user,targets,hitNum); end
def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true)
return if !showAnimation
if user.effects[PBEffects::ParentalBond]==1
@battle.pbCommonAnimation("ParentalBond",user,targets)
else
@battle.pbAnimation(id,user,targets,hitNum)
end
end
def pbSelfKO(user); end
def pbEffectWhenDealingDamage(user,target); end
def pbEffectAgainstTarget(user,target); end
def pbEffectGeneral(user); end
def pbAdditionalEffect(user,target); end
def pbEffectAfterAllHits(user,target); end # Move effects that occur after all hits
def pbSwitchOutTargetsEffect(user,targets,numHits,switchedBattlers); end
def pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers); end
#=============================================================================
# Check if target is immune to the move because of its ability
#=============================================================================
def pbImmunityByAbility(user,target)
return false if @battle.moldBreaker
ret = false
if target.abilityActive?
ret = BattleHandlers.triggerMoveImmunityTargetAbility(target.ability,
user,target,self,@calcType,@battle)
end
return ret
end
#=============================================================================
# Move failure checks
#=============================================================================
# Check whether the move fails completely due to move-specific requirements.
def pbMoveFailed?(user,targets); return false; end
# Checks whether the move will be ineffective against the target.
def pbFailsAgainstTarget?(user,target); return false; end
def pbMoveFailedLastInRound?(user)
unmoved = false
@battle.eachBattler do |b|
next if b.index==user.index
next if @battle.choices[b.index][0]!=:UseMove && @battle.choices[b.index][0]!=:Shift
next if b.movedThisRound?
unmoved = true
break
end
if !unmoved
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
return false
end
def pbMoveFailedTargetAlreadyMoved?(target)
if (@battle.choices[target.index][0]!=:UseMove &&
@battle.choices[target.index][0]!=:Shift) || target.movedThisRound?
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
return false
end
def pbMoveFailedAromaVeil?(user,target,showMessage=true)
return false if @battle.moldBreaker
if target.hasActiveAbility?(:AROMAVEIL)
if showMessage
@battle.pbShowAbilitySplash(target)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis))
else
@battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",
target.pbThis,target.abilityName))
end
@battle.pbHideAbilitySplash(target)
end
return true
end
target.eachAlly do |b|
next if !b.hasActiveAbility?(:AROMAVEIL)
if showMessage
@battle.pbShowAbilitySplash(target)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis))
else
@battle.pbDisplay(_INTL("{1} is unaffected because of {2}'s {3}!",
target.pbThis,b.pbThis(true),b.abilityName))
end
@battle.pbHideAbilitySplash(target)
end
return true
end
return false
end
#=============================================================================
# Weaken the damage dealt (doesn't actually change a battler's HP)
#=============================================================================
def pbCheckDamageAbsorption(user,target)
# Substitute will take the damage
if target.effects[PBEffects::Substitute]>0 && !ignoresSubstitute?(user) &&
(!user || user.index!=target.index)
target.damageState.substitute = true
return
end
# Disguise will take the damage
if !@battle.moldBreaker && isConst?(target.species,PBSpecies,:MIMIKYU) &&
target.form==0 && isConst?(target.ability,PBAbilities,:DISGUISE)
target.damageState.disguise = true
return
end
end
def pbReduceDamage(user,target)
damage = target.damageState.calcDamage
# Substitute takes the damage
if target.damageState.substitute
damage = target.effects[PBEffects::Substitute] if damage>target.effects[PBEffects::Substitute]
target.damageState.hpLost = damage
target.damageState.totalHPLost += damage
return
end
# Disguise takes the damage
return if target.damageState.disguise
# Target takes the damage
if damage>=target.hp
damage = target.hp
# Survive a lethal hit with 1 HP effects
if nonLethal?(user,target)
damage -= 1
elsif target.effects[PBEffects::Endure]
target.damageState.endured = true
damage -= 1
elsif damage==target.totalhp
if target.hasActiveAbility?(:STURDY) && !@battle.moldBreaker
target.damageState.sturdy = true
damage -= 1
elsif target.hasActiveItem?(:FOCUSSASH) && target.hp==target.totalhp
target.damageState.focusSash = true
damage -= 1
elsif target.hasActiveItem?(:FOCUSBAND) && @battle.pbRandom(100)<10
target.damageState.focusBand = true
damage -= 1
end
end
end
damage = 0 if damage<0
target.damageState.hpLost = damage
target.damageState.totalHPLost += damage
end
#=============================================================================
# Change the target's HP by the amount calculated above
#=============================================================================
def pbInflictHPDamage(target)
if target.damageState.substitute
target.effects[PBEffects::Substitute] -= target.damageState.hpLost
else
target.hp -= target.damageState.hpLost
end
end
#=============================================================================
# Animate the damage dealt, including lowering the HP
#=============================================================================
# Animate being damaged and losing HP (by a move)
def pbAnimateHitAndHPLost(user,targets)
# Animate allies first, then foes
animArray = []
for side in 0...2 # side here means "allies first, then foes"
targets.each do |b|
next if b.damageState.unaffected || b.damageState.hpLost==0
next if (side==0 && b.opposes?(user)) || (side==1 && !b.opposes?(user))
oldHP = b.hp+b.damageState.hpLost
PBDebug.log("[Move damage] #{b.pbThis} lost #{b.damageState.hpLost} HP (#{oldHP}=>#{b.hp})")
effectiveness = 0
if PBTypes.resistant?(b.damageState.typeMod); effectiveness = 1
elsif PBTypes.superEffective?(b.damageState.typeMod); effectiveness = 2
end
animArray.push([b,oldHP,effectiveness])
end
if animArray.length>0
@battle.scene.pbHitAndHPLossAnimation(animArray)
animArray.clear
end
end
end
#=============================================================================
# Messages upon being hit
#=============================================================================
def pbEffectivenessMessage(user,target,numTargets=1)
return if target.damageState.disguise
if PBTypes.superEffective?(target.damageState.typeMod)
if numTargets>1
@battle.pbDisplay(_INTL("It's super effective on {1}!",target.pbThis(true)))
else
@battle.pbDisplay(_INTL("It's super effective!"))
end
elsif PBTypes.notVeryEffective?(target.damageState.typeMod)
if numTargets>1
@battle.pbDisplay(_INTL("It's not very effective on {1}...",target.pbThis(true)))
else
@battle.pbDisplay(_INTL("It's not very effective..."))
end
end
end
def pbHitEffectivenessMessages(user,target,numTargets=1)
return if target.damageState.disguise
if target.damageState.substitute
@battle.pbDisplay(_INTL("The substitute took damage for {1}!",target.pbThis(true)))
end
if target.damageState.critical
if numTargets>1
@battle.pbDisplay(_INTL("A critical hit on {1}!",target.pbThis(true)))
else
@battle.pbDisplay(_INTL("A critical hit!"))
end
end
# Effectiveness message, for moves with 1 hit
if !multiHitMove? && user.effects[PBEffects::ParentalBond]==0
pbEffectivenessMessage(user,target,numTargets)
end
if target.damageState.substitute && target.effects[PBEffects::Substitute]==0
target.effects[PBEffects::Substitute] = 0
@battle.pbDisplay(_INTL("{1}'s substitute faded!",target.pbThis))
end
end
def pbEndureKOMessage(target)
if target.damageState.disguise
@battle.pbShowAbilitySplash(target)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("Its disguise served it as a decoy!"))
else
@battle.pbDisplay(_INTL("{1}'s disguise served it as a decoy!",target.pbThis))
end
@battle.pbHideAbilitySplash(target)
target.pbChangeForm(1,_INTL("{1}'s disguise was busted!",target.pbThis))
elsif target.damageState.endured
@battle.pbDisplay(_INTL("{1} endured the hit!",target.pbThis))
elsif target.damageState.sturdy
@battle.pbShowAbilitySplash(target)
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@battle.pbDisplay(_INTL("{1} endured the hit!",target.pbThis))
else
@battle.pbDisplay(_INTL("{1} hung on with Sturdy!",target.pbThis))
end
@battle.pbHideAbilitySplash(target)
elsif target.damageState.focusSash
@battle.pbCommonAnimation("UseItem",target)
@battle.pbDisplay(_INTL("{1} hung on using its Focus Sash!",target.pbThis))
target.pbConsumeItem
elsif target.damageState.focusBand
@battle.pbCommonAnimation("UseItem",target)
@battle.pbDisplay(_INTL("{1} hung on using its Focus Band!",target.pbThis))
end
end
# Used by Counter/Mirror Coat/Metal Burst/Revenge/Focus Punch/Bide/Assurance.
def pbRecordDamageLost(user,target)
damage = target.damageState.hpLost
# NOTE: In Gen 3 where a move's category depends on its type, Hidden Power
# is for some reason countered by Counter rather than Mirror Coat,
# regardless of its calculated type. Hence the following two lines of
# code.
moveType = nil
moveType = getID(PBTypes,:NORMAL) if @function=="090" # Hidden Power
if physicalMove?(moveType)
target.effects[PBEffects::Counter] = damage
target.effects[PBEffects::CounterTarget] = user.index
elsif specialMove?(moveType)
target.effects[PBEffects::MirrorCoat] = damage
target.effects[PBEffects::MirrorCoatTarget] = user.index
end
if target.effects[PBEffects::Bide]>0
target.effects[PBEffects::BideDamage] += damage
target.effects[PBEffects::BideTarget] = user.index
end
target.damageState.fainted = true if target.fainted?
target.lastHPLost = damage # For Focus Punch
target.tookDamage = true if damage>0 # For Assurance
target.lastAttacker.push(user.index) # For Revenge
if target.opposes?(user)
target.lastHPLostFromFoe = damage # For Metal Burst
target.lastFoeAttacker.push(user.index) # For Metal Burst
end
end
end

View File

@@ -0,0 +1,491 @@
class PokeBattle_Move
#=============================================================================
# Move's type calculation
#=============================================================================
def pbBaseType(user)
ret = @type
return ret if !ret || ret<0
if user.abilityActive?
ret = BattleHandlers.triggerMoveBaseTypeModifierAbility(user.ability,user,self,ret)
end
return ret
end
def pbCalcType(user)
@powerBoost = false
ret = pbBaseType(user)
return ret if !ret || ret<0
if hasConst?(PBTypes,:ELECTRIC)
if @battle.field.effects[PBEffects::IonDeluge] && isConst?(ret,PBTypes,:NORMAL)
ret = getConst(PBTypes,:ELECTRIC)
@powerBoost = false
end
if user.effects[PBEffects::Electrify]
ret = getConst(PBTypes,:ELECTRIC)
@powerBoost = false
end
end
return ret
end
#=============================================================================
# Type effectiveness calculation
#=============================================================================
def pbCalcTypeModSingle(moveType,defType,user,target)
ret = PBTypes.getEffectiveness(moveType,defType)
# Ring Target
if target.hasActiveItem?(:RINGTARGET)
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if PBTypes.ineffective?(moveType,defType)
end
# Foresight
if user.hasActiveAbility?(:SCRAPPY) || target.effects[PBEffects::Foresight]
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if isConst?(defType,PBTypes,:GHOST) &&
PBTypes.ineffective?(moveType,defType)
end
# Miracle Eye
if target.effects[PBEffects::MiracleEye]
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if isConst?(defType,PBTypes,:DARK) &&
PBTypes.ineffective?(moveType,defType)
end
# Delta Stream's weather
if @battle.pbWeather==PBWeather::StrongWinds
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if isConst?(defType,PBTypes,:FLYING) &&
PBTypes.superEffective?(moveType,defType)
end
# Grounded Flying-type Pokémon become susceptible to Ground moves
if !target.airborne?
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if isConst?(defType,PBTypes,:FLYING) &&
isConst?(moveType,PBTypes,:GROUND)
end
return ret
end
def pbCalcTypeMod(moveType,user,target)
return PBTypeEffectiveness::NORMAL_EFFECTIVE if moveType<0
return PBTypeEffectiveness::NORMAL_EFFECTIVE if isConst?(moveType,PBTypes,:GROUND) &&
target.pbHasType?(:FLYING) && target.hasActiveItem?(:IRONBALL)
# Determine types
tTypes = target.pbTypes(true)
# Get effectivenesses
typeMods = [PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE] * 3 # 3 types max
tTypes.each_with_index do |type,i|
typeMods[i] = pbCalcTypeModSingle(moveType,type,user,target)
end
# Multiply all effectivenesses together
ret = 1
typeMods.each { |m| ret *= m }
return ret
end
#=============================================================================
# Accuracy check
#=============================================================================
def pbBaseAccuracy(user,target); return @accuracy; end
# Accuracy calculations for one-hit KO moves and "always hit" moves are
# handled elsewhere.
def pbAccuracyCheck(user,target)
# "Always hit" effects and "always hit" accuracy
return true if target.effects[PBEffects::Telekinesis]>0
return true if target.effects[PBEffects::Minimize] && tramplesMinimize?(1)
baseAcc = pbBaseAccuracy(user,target)
return true if baseAcc==0
# Calculate all multiplier effects
modifiers = []
modifiers[BASE_ACC] = baseAcc
modifiers[ACC_STAGE] = user.stages[PBStats::ACCURACY]
modifiers[EVA_STAGE] = target.stages[PBStats::EVASION]
modifiers[ACC_MULT] = 0x1000
modifiers[EVA_MULT] = 0x1000
pbCalcAccuracyModifiers(user,target,modifiers)
# Check if move can't miss
return true if modifiers[BASE_ACC]==0
# Calculation
accStage = [[modifiers[ACC_STAGE],-6].max,6].min + 6
evaStage = [[modifiers[EVA_STAGE],-6].max,6].min + 6
stageMul = [3,3,3,3,3,3, 3, 4,5,6,7,8,9]
stageDiv = [9,8,7,6,5,4, 3, 3,3,3,3,3,3]
accuracy = 100.0 * stageMul[accStage] / stageDiv[accStage]
evasion = 100.0 * stageMul[evaStage] / stageDiv[evaStage]
accuracy = (accuracy * modifiers[ACC_MULT] / 0x1000).round
evasion = (evasion * modifiers[EVA_MULT] / 0x1000).round
evasion = 1 if evasion<1
# Calculation
return @battle.pbRandom(100) < modifiers[BASE_ACC] * accuracy / evasion
end
def pbCalcAccuracyModifiers(user,target,modifiers)
# Ability effects that alter accuracy calculation
if user.abilityActive?
BattleHandlers.triggerAccuracyCalcUserAbility(user.ability,
modifiers,user,target,self,@calcType)
end
user.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerAccuracyCalcUserAllyAbility(b.ability,
modifiers,user,target,self,@calcType)
end
if target.abilityActive? && !@battle.moldBreaker
BattleHandlers.triggerAccuracyCalcTargetAbility(target.ability,
modifiers,user,target,self,@calcType)
end
# Item effects that alter accuracy calculation
if user.itemActive?
BattleHandlers.triggerAccuracyCalcUserItem(user.item,
modifiers,user,target,self,@calcType)
end
if target.itemActive?
BattleHandlers.triggerAccuracyCalcTargetItem(target.item,
modifiers,user,target,self,@calcType)
end
# Other effects, inc. ones that set ACC_MULT or EVA_STAGE to specific values
if @battle.field.effects[PBEffects::Gravity]>0
modifiers[ACC_MULT] = (modifiers[ACC_MULT]*5/3).round
end
if user.effects[PBEffects::MicleBerry]
user.effects[PBEffects::MicleBerry] = false
modifiers[ACC_MULT] = (modifiers[ACC_MULT]*1.2).round
end
modifiers[EVA_STAGE] = 0 if target.effects[PBEffects::Foresight] && modifiers[EVA_STAGE]>0
modifiers[EVA_STAGE] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[EVA_STAGE]>0
end
#=============================================================================
# Critical hit check
#=============================================================================
# Return values:
# -1: Never a critical hit.
# 0: Calculate normally.
# 1: Always a critical hit.
def pbCritialOverride(user,target); return 0; end
# Returns whether the move will be a critical hit.
def pbIsCritical?(user,target)
return false if target.pbOwnSide.effects[PBEffects::LuckyChant]>0
# Set up the critical hit ratios
ratios = (NEWEST_BATTLE_MECHANICS) ? [24,8,2,1] : [16,8,4,3,2]
c = 0
# Ability effects that alter critical hit rate
if c>=0 && user.abilityActive?
c = BattleHandlers.triggerCriticalCalcUserAbility(user.ability,user,target,c)
end
if c>=0 && target.abilityActive? && !@battle.moldBreaker
c = BattleHandlers.triggerCriticalCalcTargetAbility(target.ability,user,target,c)
end
# Item effects that alter critical hit rate
if c>=0 && user.itemActive?
c = BattleHandlers.triggerCriticalCalcUserItem(user.item,user,target,c)
end
if c>=0 && target.itemActive?
c = BattleHandlers.triggerCriticalCalcTargetItem(target.item,user,target,c)
end
return false if c<0
# Move-specific "always/never a critical hit" effects
case pbCritialOverride(user,target)
when 1; return true
when -1; return false
end
# Other effects
return true if c>50 # Merciless
return true if user.effects[PBEffects::LaserFocus]>0
c += 1 if highCriticalRate?
c += user.effects[PBEffects::FocusEnergy]
c += 1 if user.inHyperMode? && isConst?(@type,PBTypes,:SHADOW)
c = ratios.length-1 if c>=ratios.length
# Calculation
return @battle.pbRandom(ratios[c])==0
end
#=============================================================================
# Damage calculation
#=============================================================================
def pbBaseDamage(baseDmg,user,target); return baseDmg; end
def pbBaseDamageMultiplier(damageMult,user,target); return damageMult; end
def pbModifyDamage(damageMult,user,target); return damageMult; end
def pbGetAttackStats(user,target)
if specialMove?
return user.spatk, user.stages[PBStats::SPATK]+6
end
return user.attack, user.stages[PBStats::ATTACK]+6
end
def pbGetDefenseStats(user,target)
if specialMove?
return target.spdef, target.stages[PBStats::SPDEF]+6
end
return target.defense, target.stages[PBStats::DEFENSE]+6
end
def pbCalcDamage(user,target,numTargets=1)
return if statusMove?
if target.damageState.disguise
target.damageState.calcDamage = 1
return
end
stageMul = [2,2,2,2,2,2, 2, 3,4,5,6,7,8]
stageDiv = [8,7,6,5,4,3, 2, 2,2,2,2,2,2]
# Get the move's type
type = @calcType # -1 is treated as physical
# Calculate whether this hit deals critical damage
target.damageState.critical = pbIsCritical?(user,target)
# Calcuate base power of move
baseDmg = pbBaseDamage(@baseDamage,user,target)
# Calculate user's attack stat
atk, atkStage = pbGetAttackStats(user,target)
if !target.hasActiveAbility?(:UNAWARE) || @battle.moldBreaker
atkStage = 6 if target.damageState.critical && atkStage<6
atk = (atk.to_f*stageMul[atkStage]/stageDiv[atkStage]).floor
end
# Calculate target's defense stat
defense, defStage = pbGetDefenseStats(user,target)
if !user.hasActiveAbility?(:UNAWARE)
defStage = 6 if target.damageState.critical && defStage>6
defense = (defense.to_f*stageMul[defStage]/stageDiv[defStage]).floor
end
# Calculate all multiplier effects
multipliers = [0x1000,0x1000,0x1000,0x1000]
pbCalcDamageMultipliers(user,target,numTargets,type,baseDmg,multipliers)
# Main damage calculation
baseDmg = [(baseDmg * multipliers[BASE_DMG_MULT] / 0x1000).round,1].max
atk = [(atk * multipliers[ATK_MULT] / 0x1000).round,1].max
defense = [(defense * multipliers[DEF_MULT] / 0x1000).round,1].max
damage = (((2.0*user.level/5+2).floor*baseDmg*atk/defense).floor/50).floor+2
damage = [(damage * multipliers[FINAL_DMG_MULT] / 0x1000).round,1].max
target.damageState.calcDamage = damage
end
def pbCalcDamageMultipliers(user,target,numTargets,baseDmg,type,multipliers)
# Global abilities
if (@battle.pbCheckGlobalAbility(:DARKAURA) && isConst?(type,PBTypes,:DARK)) ||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && isConst?(type,PBTypes,:FAIRY))
if @battle.pbCheckGlobalAbility(:AURABREAK)
multipliers[BASE_DMG_MULT] *= 2/3.0
else
multipliers[BASE_DMG_MULT] *= 4/3.0
end
end
# Ability effects that alter damage
if user.abilityActive?
BattleHandlers.triggerDamageCalcUserAbility(user.ability,
user,target,self,multipliers,baseDmg,type)
end
if !@battle.moldBreaker
# NOTE: It's odd that the user's Mold Breaker prevents its partner's
# beneficial abilities (i.e. Flower Gift boosting Atk), but that's
# how it works.
user.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerDamageCalcUserAllyAbility(b.ability,
user,target,self,multipliers,baseDmg,type)
end
if target.abilityActive?
BattleHandlers.triggerDamageCalcTargetAbility(target.ability,
user,target,self,multipliers,baseDmg,type) if !@battle.moldBreaker
BattleHandlers.triggerDamageCalcTargetAbilityNonIgnorable(target.ability,
user,target,self,multipliers,baseDmg,type)
end
target.eachAlly do |b|
next if !b.abilityActive?
BattleHandlers.triggerDamageCalcTargetAllyAbility(b.ability,
user,target,self,multipliers,baseDmg,type)
end
end
# Item effects that alter damage
if user.itemActive?
BattleHandlers.triggerDamageCalcUserItem(user.item,
user,target,self,multipliers,baseDmg,type)
end
if target.itemActive?
BattleHandlers.triggerDamageCalcTargetItem(target.item,
user,target,self,multipliers,baseDmg,type)
end
# Parental Bond's second attack
if user.effects[PBEffects::ParentalBond]==1
multipliers[BASE_DMG_MULT] /= 4
end
# Other
if user.effects[PBEffects::MeFirst]
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
end
if user.effects[PBEffects::HelpingHand] && !self.is_a?(PokeBattle_Confusion)
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
end
if user.effects[PBEffects::Charge]>0 && isConst?(type,PBTypes,:ELECTRIC)
multipliers[BASE_DMG_MULT] *= 2
end
# Mud Sport
if isConst?(type,PBTypes,:ELECTRIC)
@battle.eachBattler do |b|
next if !b.effects[PBEffects::MudSport]
multipliers[BASE_DMG_MULT] /= 3
break
end
if @battle.field.effects[PBEffects::MudSportField]>0
multipliers[BASE_DMG_MULT] /= 3
end
end
# Water Sport
if isConst?(type,PBTypes,:FIRE)
@battle.eachBattler do |b|
next if !b.effects[PBEffects::WaterSport]
multipliers[BASE_DMG_MULT] /= 3
break
end
if @battle.field.effects[PBEffects::WaterSportField]>0
multipliers[BASE_DMG_MULT] /= 3
end
end
# Terrain moves
if user.affectedByTerrain?
case @battle.field.terrain
when PBBattleTerrains::Electric
if isConst?(type,PBTypes,:ELECTRIC)
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
end
when PBBattleTerrains::Grassy
if isConst?(type,PBTypes,:GRASS)
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
end
when PBBattleTerrains::Psychic
if isConst?(type,PBTypes,:PSYCHIC)
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
end
end
end
if @battle.field.terrain==PBBattleTerrains::Misty && target.affectedByTerrain? &&
isConst?(type,PBTypes,:DRAGON)
multipliers[BASE_DMG_MULT] /= 2
end
# Badge multipliers
if @battle.internalBattle
if user.pbOwnedByPlayer?
if physicalMove? && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_ATTACK
multipliers[ATK_MULT] = (multipliers[ATK_MULT]*1.1).round
elsif specialMove? && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_SPATK
multipliers[ATK_MULT] = (multipliers[ATK_MULT]*1.1).round
end
end
if target.pbOwnedByPlayer?
if physicalMove? && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_DEFENSE
multipliers[DEF_MULT] = (multipliers[DEF_MULT]*1.1).round
elsif specialMove? && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_SPDEF
multipliers[DEF_MULT] = (multipliers[DEF_MULT]*1.1).round
end
end
end
# Multi-targeting attacks
if numTargets>1
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*0.75).round
end
# Weather
case @battle.pbWeather
when PBWeather::Sun, PBWeather::HarshSun
if isConst?(type,PBTypes,:FIRE)
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
elsif isConst?(type,PBTypes,:WATER)
multipliers[FINAL_DMG_MULT] /= 2
end
when PBWeather::Rain, PBWeather::HeavyRain
if isConst?(type,PBTypes,:FIRE)
multipliers[FINAL_DMG_MULT] /= 2
elsif isConst?(type,PBTypes,:WATER)
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
end
when PBWeather::Sandstorm
if target.pbHasType?(:ROCK) && specialMove? && @function!="122" # Psyshock
multipliers[DEF_MULT] = (multipliers[DEF_MULT]*1.5).round
end
end
# Critical hits
if target.damageState.critical
if NEWEST_BATTLE_MECHANICS
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
else
multipliers[FINAL_DMG_MULT] *= 2
end
end
# Random variance
if !self.is_a?(PokeBattle_Confusion)
random = 85+@battle.pbRandom(16)
multipliers[FINAL_DMG_MULT] *= random/100.0
end
# STAB
if type>=0 && user.pbHasType?(type)
if user.hasActiveAbility?(:ADAPTABILITY)
multipliers[FINAL_DMG_MULT] *= 2
else
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
end
end
# Type effectiveness
multipliers[FINAL_DMG_MULT] *= target.damageState.typeMod.to_f/PBTypeEffectiveness::NORMAL_EFFECTIVE
multipliers[FINAL_DMG_MULT] = multipliers[FINAL_DMG_MULT].round
# Burn
if user.status==PBStatuses::BURN && physicalMove? && damageReducedByBurn? &&
!user.hasActiveAbility?(:GUTS)
multipliers[FINAL_DMG_MULT] /= 2
end
# Aurora Veil, Reflect, Light Screen
if !ignoresReflect? && !target.damageState.critical &&
!user.hasActiveAbility?(:INFILTRATOR)
if target.pbOwnSide.effects[PBEffects::AuroraVeil]>0
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*2/3).round
else
multipliers[FINAL_DMG_MULT] /= 2
end
elsif target.pbOwnSide.effects[PBEffects::Reflect]>0 && physicalMove?
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*2/3).round
else
multipliers[FINAL_DMG_MULT] /= 2
end
elsif target.pbOwnSide.effects[PBEffects::LightScreen]>0 && specialMove?
if @battle.pbSideBattlerCount(target)>1
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*2/3).round
else
multipliers[FINAL_DMG_MULT] /= 2
end
end
end
# Minimize
if target.effects[PBEffects::Minimize] && tramplesMinimize?(2)
multipliers[FINAL_DMG_MULT] *= 2
end
# Move-specific base damage modifiers
multipliers[BASE_DMG_MULT] = pbBaseDamageMultiplier(multipliers[BASE_DMG_MULT],user,target)
# Move-specific final damage modifiers
multipliers[FINAL_DMG_MULT] = pbModifyDamage(multipliers[FINAL_DMG_MULT],user,target)
end
#=============================================================================
# Additional effect chance
#=============================================================================
def pbAdditionalEffectChance(user,target,effectChance=0)
return 0 if target.hasActiveAbility?(:SHIELDDUST) && !@battle.moldBreaker
ret = (effectChance>0) ? effectChance : @addlEffect
if NEWEST_BATTLE_MECHANICS || @function!="0A4" # Secret Power
ret *= 2 if user.hasActiveAbility?(:SERENEGRACE) ||
user.pbOwnSide.effects[PBEffects::Rainbow]>0
end
ret = 100 if $DEBUG && Input.press?(Input::CTRL)
return ret
end
# NOTE: Flinching caused by a move's effect is applied in that move's code,
# not here.
def pbFlinchChance(user,target)
return 0 if flinchingMove?
return 0 if target.hasActiveAbility?(:SHIELDDUST) && !@battle.moldBreaker
ret = 0
if user.hasActiveAbility?(:STENCH,true)
ret = 10
elsif user.hasActiveItem?([:KINGSROCK,:RAZORFANG],true)
ret = 10
end
ret *= 2 if user.hasActiveAbility?(:SERENEGRACE) ||
user.pbOwnSide.effects[PBEffects::Rainbow]>0
return ret
end
end

View File

@@ -0,0 +1,714 @@
#===============================================================================
# Superclass that handles moves using a non-existent function code.
# Damaging moves just do damage with no additional effect.
# Status moves always fail.
#===============================================================================
class PokeBattle_UnimplementedMove < PokeBattle_Move
def pbMoveFailed?(user,targets)
if statusMove?
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
return false
end
end
#===============================================================================
# Pseudomove for confusion damage.
#===============================================================================
class PokeBattle_Confusion < PokeBattle_Move
def initialize(battle,move)
@battle = battle
@realMove = move
@id = 0
@name = ""
@function = "000"
@baseDamage = 40
@type = -1
@category = 0
@accuracy = 100
@pp = -1
@target = 0
@priority = 0
@flags = ""
@addlEffect = 0
@calcType = -1
@powerBoost = false
@snatched = false
end
def physicalMove?(thisType=nil); return true; end
def specialMove?(thisType=nil); return false; end
def pbCritialOverride(user,target); return -1; end
end
#===============================================================================
# Implements the move Struggle.
# For cases where the real move named Struggle is not defined.
#===============================================================================
class PokeBattle_Struggle < PokeBattle_Move
def initialize(battle,move)
@battle = battle
@realMove = nil # Not associated with a move
@id = (move) ? move.id : -1 # Doesn't work if 0
@name = (move) ? PBMoves.getName(@id) : _INTL("Struggle")
@function = "002"
@baseDamage = 50
@type = -1
@category = 0
@accuracy = 0
@pp = -1
@target = 0
@priority = 0
@flags = ""
@addlEffect = 0
@calcType = -1
@powerBoost = false
@snatched = false
end
def physicalMove?(thisType=nil); return true; end
def specialMove?(thisType=nil); return false; end
def pbEffectAfterAllHits(user,target)
return if target.damageState.unaffected
user.pbReduceHP((user.totalhp/4.0).round,false)
@battle.pbDisplay(_INTL("{1} is damaged by recoil!",user.pbThis))
user.pbItemHPHealCheck
end
end
#===============================================================================
# Generic status problem-inflicting classes.
#===============================================================================
class PokeBattle_SleepMove < PokeBattle_Move
def pbFailsAgainstTarget?(user,target)
return false if damagingMove?
return !target.pbCanSleep?(user,true,self)
end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
target.pbSleep
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
target.pbSleep if target.pbCanSleep?(user,false,self)
end
end
class PokeBattle_PoisonMove < PokeBattle_Move
def initialize(battle,move)
super
@toxic = false
end
def pbFailsAgainstTarget?(user,target)
return false if damagingMove?
return !target.pbCanPoison?(user,true,self)
end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
target.pbPoison(user,nil,@toxic)
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
target.pbPoison(user,nil,@toxic) if target.pbCanPoison?(user,false,self)
end
end
class PokeBattle_ParalysisMove < PokeBattle_Move
def pbFailsAgainstTarget?(user,target)
return false if damagingMove?
return !target.pbCanParalyze?(user,true,self)
end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
target.pbParalyze(user)
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
target.pbParalyze(user) if target.pbCanParalyze?(user,false,self)
end
end
class PokeBattle_BurnMove < PokeBattle_Move
def pbFailsAgainstTarget?(user,target)
return false if damagingMove?
return !target.pbCanBurn?(user,true,self)
end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
target.pbBurn(user)
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
target.pbBurn(user) if target.pbCanBurn?(user,false,self)
end
end
class PokeBattle_FreezeMove < PokeBattle_Move
def pbFailsAgainstTarget?(user,target)
return false if damagingMove?
return !target.pbCanFreeze?(user,true,self)
end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
target.pbFreeze
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
target.pbFreeze if target.pbCanFreeze?(user,false,self)
end
end
#===============================================================================
# Other problem-causing classes.
#===============================================================================
class PokeBattle_FlinchMove < PokeBattle_Move
def flinchingMove?; return true; end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
target.pbFlinch(user)
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
target.pbFlinch(user)
end
end
class PokeBattle_ConfuseMove < PokeBattle_Move
def pbFailsAgainstTarget?(user,target)
return false if damagingMove?
return !target.pbCanConfuse?(user,true,self)
end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
target.pbConfuse
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
return if !target.pbCanConfuse?(user,false,self)
target.pbConfuse
end
end
#===============================================================================
# Generic user's stat increase/decrease classes.
#===============================================================================
class PokeBattle_StatUpMove < PokeBattle_Move
def pbMoveFailed?(user,targets)
return false if damagingMove?
return !user.pbCanRaiseStatStage?(@statUp[0],user,self,true)
end
def pbEffectGeneral(user)
return if damagingMove?
user.pbRaiseStatStage(@statUp[0],@statUp[1],user)
end
def pbAdditionalEffect(user,target)
if user.pbCanRaiseStatStage?(@statUp[0],user,self)
user.pbRaiseStatStage(@statUp[0],@statUp[1],user)
end
end
end
class PokeBattle_MultiStatUpMove < PokeBattle_Move
def pbMoveFailed?(user,targets)
return false if damagingMove?
failed = true
for i in 0...@statUp.length/2
next if !user.pbCanRaiseStatStage?(@statUp[i*2],user,self)
failed = false
break
end
if failed
@battle.pbDisplay(_INTL("{1}'s stats won't go any higher!",user.pbThis))
return true
end
return false
end
def pbEffectGeneral(user)
return if damagingMove?
showAnim = true
for i in 0...@statUp.length/2
next if !user.pbCanRaiseStatStage?(@statUp[i*2],user,self)
if user.pbRaiseStatStage(@statUp[i*2],@statUp[i*2+1],user,showAnim)
showAnim = false
end
end
end
def pbAdditionalEffect(user,target)
showAnim = true
for i in 0...@statUp.length/2
next if !user.pbCanRaiseStatStage?(@statUp[i*2],user,self)
if user.pbRaiseStatStage(@statUp[i*2],@statUp[i*2+1],user,showAnim)
showAnim = false
end
end
end
end
class PokeBattle_StatDownMove < PokeBattle_Move
def pbEffectWhenDealingDamage(user,target)
return if @battle.pbAllFainted?(target.idxOwnSide)
showAnim = true
for i in 0...@statDown.length/2
next if !user.pbCanLowerStatStage?(@statDown[i*2],user,self)
if user.pbLowerStatStage(@statDown[i*2],@statDown[i*2+1],user,showAnim)
showAnim = false
end
end
end
end
#===============================================================================
# Generic target's stat increase/decrease classes.
#===============================================================================
class PokeBattle_TargetStatDownMove < PokeBattle_Move
def pbFailsAgainstTarget?(user,target)
return false if damagingMove?
return !target.pbCanLowerStatStage?(@statDown[0],user,self,true)
end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
target.pbLowerStatStage(@statDown[0],@statDown[1],user)
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
return if !target.pbCanLowerStatStage?(@statDown[0],user,self)
target.pbLowerStatStage(@statDown[0],@statDown[1],user)
end
end
class PokeBattle_TargetMultiStatDownMove < PokeBattle_Move
def pbFailsAgainstTarget?(user,target)
return false if damagingMove?
failed = true
for i in 0...@statDown.length/2
next if !target.pbCanLowerStatStage?(@statDown[i*2],user,self)
failed = false
break
end
if failed
# NOTE: It's a bit of a faff to make sure the appropriate failure message
# is shown here, I know.
canLower = false
if target.hasActiveAbility?(:CONTRARY) && !@battle.moldBreaker
for i in 0...@statDown.length/2
next if target.statStageAtMax?(@statDown[i*2])
canLower = true
break
end
@battle.pbDisplay(_INTL("{1}'s stats won't go any higher!",user.pbThis)) if !canLower
else
for i in 0...@statDown.length/2
next if target.statStageAtMin?(@statDown[i*2])
canLower = true
break
end
@battle.pbDisplay(_INTL("{1}'s stats won't go any lower!",user.pbThis)) if !canLower
end
if canLower
target.pbCanLowerStatStage?(@statDown[0],user,self,true)
end
return true
end
return false
end
def pbEffectAgainstTarget(user,target)
return if damagingMove?
showAnim = true
for i in 0...@statDown.length/2
next if !target.pbCanLowerStatStage?(@statDown[i*2],user,self)
if target.pbLowerStatStage(@statDown[i*2],@statDown[i*2+1],user,showAnim)
showAnim = false
end
end
end
def pbAdditionalEffect(user,target)
return if target.damageState.substitute
showAnim = true
for i in 0...@statDown.length/2
next if !target.pbCanLowerStatStage?(@statDown[i*2],user,self)
if target.pbLowerStatStage(@statDown[i*2],@statDown[i*2+1],user,showAnim)
showAnim = false
end
end
end
end
#===============================================================================
# Fixed damage-inflicting move.
#===============================================================================
class PokeBattle_FixedDamageMove < PokeBattle_Move
def pbFixedDamage(user,target); return 1; end
def pbCalcDamage(user,target,numTargets=1)
target.damageState.critical = false
target.damageState.calcDamage = pbFixedDamage(user,target)
target.damageState.calcDamage = 1 if target.damageState.calcDamage<1
end
end
#===============================================================================
# Two turn move.
#===============================================================================
class PokeBattle_TwoTurnMove < PokeBattle_Move
def chargingTurnMove?; return true; end
# user.effects[PBEffects::TwoTurnAttack] is set to the move's ID if this
# method returns true, or 0 if false.
# Non-zero means the charging turn. 0 means the attacking turn.
def pbIsChargingTurn?(user)
@powerHerb = false
@chargingTurn = false # Assume damaging turn by default
@damagingTurn = true
# 0 at start of charging turn, move's ID at start of damaging turn
if user.effects[PBEffects::TwoTurnAttack]==0
@powerHerb = user.hasActiveItem?(:POWERHERB)
@chargingTurn = true
@damagingTurn = @powerHerb
end
return !@damagingTurn # Deliberately not "return @chargingTurn"
end
def pbDamagingMove? # Stops damage being dealt in the first (charging) turn
return false if !@damagingTurn
return super
end
def pbAccuracyCheck(user,target)
return true if !@damagingTurn
return super
end
def pbInitialEffect(user,targets,hitNum)
pbChargingTurnMessage(user,targets) if @chargingTurn
if @chargingTurn && @damagingTurn # Move only takes one turn to use
pbShowAnimation(@id,user,targets,1) # Charging anim
targets.each { |b| pbChargingTurnEffect(user,b) }
if @powerHerb
# Moves that would make the user semi-invulnerable will hide the user
# after the charging animation, so the "UseItem" animation shouldn't show
# for it
if !["0C9","0CA","0CB","0CC","0CD","0CE","14D"].include?(@function)
@battle.pbCommonAnimation("UseItem",user)
end
@battle.pbDisplay(_INTL("{1} became fully charged due to its Power Herb!",user.pbThis))
user.pbConsumeItem
end
end
pbAttackingTurnMessage(user,targets) if @damagingTurn
end
def pbChargingTurnMessage(user,targets)
@battle.pbDisplay(_INTL("{1} began charging up!",user.pbThis))
end
def pbAttackingTurnMessage(user,targets)
end
def pbChargingTurnEffect(user,target)
# Skull Bash/Sky Drop are the only two-turn moves with an effect here, and
# the latter just records the target is being Sky Dropped
end
def pbAttackingTurnEffect(user,target)
end
def pbEffectAgainstTarget(user,target)
if @damagingTurn; pbAttackingTurnEffect(user,target)
elsif @chargingTurn; pbChargingTurnEffect(user,target)
end
end
def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true)
hitNum = 1 if @chargingTurn && !@damagingTurn # Charging anim
super
end
end
#===============================================================================
# Healing move.
#===============================================================================
class PokeBattle_HealingMove < PokeBattle_Move
def healingMove?; return true; end
def pbHealAmount(user); return 1; end
def pbMoveFailed?(user,targets)
if user.hp==user.totalhp
@battle.pbDisplay(_INTL("{1}'s HP is full!",user.pbThis))
return true
end
return false
end
def pbEffectGeneral(user)
amt = pbHealAmount(user)
user.pbRecoverHP(amt)
@battle.pbDisplay(_INTL("{1}'s HP was restored.",user.pbThis))
end
end
#===============================================================================
# Recoil move.
#===============================================================================
class PokeBattle_RecoilMove < PokeBattle_Move
def recoilMove?; return true; end
def pbRecoilDamage(user,target); return 1; end
def pbEffectAfterAllHits(user,target)
return if target.damageState.unaffected
return if !user.takesIndirectDamage?
return if user.hasActiveAbility?(:ROCKHEAD)
amt = pbRecoilDamage(user,target)
amt = 1 if amt<1
user.pbReduceHP(amt,false)
@battle.pbDisplay(_INTL("{1} is damaged by recoil!",user.pbThis))
user.pbItemHPHealCheck
end
end
#===============================================================================
# Protect move.
#===============================================================================
class PokeBattle_ProtectMove < PokeBattle_Move
def initialize(battle,move)
super
@sidedEffect = false
end
def pbChangeUsageCounters(user,specialUsage)
oldVal = user.effects[PBEffects::ProtectRate]
super
user.effects[PBEffects::ProtectRate] = oldVal
end
def pbMoveFailed?(user,targets)
if @sidedEffect
if user.pbOwnSide.effects[@effect]
user.effects[PBEffects::ProtectRate] = 1
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
elsif user.effects[@effect]
user.effects[PBEffects::ProtectRate] = 1
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
if !(@sidedEffect && NEWEST_BATTLE_MECHANICS) &&
user.effects[PBEffects::ProtectRate]>1 &&
@battle.pbRandom(user.effects[PBEffects::ProtectRate])!=0
user.effects[PBEffects::ProtectRate] = 1
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
if pbMoveFailedLastInRound?(user)
user.effects[PBEffects::ProtectRate] = 1
return true
end
return false
end
def pbEffectGeneral(user)
if @sidedEffect
user.pbOwnSide.effects[@effect] = true
else
user.effects[@effect] = true
end
user.effects[PBEffects::ProtectRate] *= (NEWEST_BATTLE_MECHANICS) ? 3 : 2
pbProtectMessage(user)
end
def pbProtectMessage(user)
if @sidedEffect
@battle.pbDisplay(_INTL("{1} protected {2}!",@name,user.pbTeam(true)))
else
@battle.pbDisplay(_INTL("{1} protected itself!",user.pbThis))
end
end
end
#===============================================================================
# Weather-inducing move.
#===============================================================================
class PokeBattle_WeatherMove < PokeBattle_Move
def initialize(battle,move)
super
@weatherType = PBWeather::None
end
def pbMoveFailed?(user,targets)
case @battle.field.weather
when PBWeather::HarshSun
@battle.pbDisplay(_INTL("The extremely harsh sunlight was not lessened at all!"))
return true
when PBWeather::HeavyRain
@battle.pbDisplay(_INTL("There is no relief from this heavy rain!"))
return true
when PBWeather::StrongWinds
@battle.pbDisplay(_INTL("The mysterious air current blows on regardless!"))
return true
when @weatherType
@battle.pbDisplay(_INTL("But it failed!"))
return true
end
return false
end
def pbEffectGeneral(user)
@battle.pbStartWeather(user,@weatherType,true,false)
end
end
#===============================================================================
# Pledge move.
#===============================================================================
class PokeBattle_PledgeMove < PokeBattle_Move
def pbOnStartUse(user,targets)
@pledgeSetup = false; @pledgeCombo = false; @pledgeOtherUser = nil
@comboEffect = nil; @overrideType = nil; @overrideAnim = nil
# Check whether this is the use of a combo move
@combos.each do |i|
next if i[0]!=user.effects[PBEffects::FirstPledge]
@battle.pbDisplay(_INTL("The two moves have become one! It's a combined move!"))
@pledgeCombo = true
@comboEffect = i[1]; @overrideType = i[2]; @overrideAnim = i[3]
break
end
return if @pledgeCombo
# Check whether this is the setup of a combo move
user.eachAlly do |b|
next if @battle.choices[b.index][0]!=:UseMove || b.movedThisRound?
move = @battle.choices[b.index][2]
next if !move || move.id<=0
@combos.each do |i|
next if i[0]!=move.function
@pledgeSetup = true
@pledgeOtherUser = b
break
end
break if @pledgeSetup
end
end
def pbDamagingMove?
return false if @pledgeSetup
return super
end
def pbBaseType(user)
return @overrideType if @overrideType!=nil
return super
end
def pbBaseDamage(baseDmg,user,target)
baseDmg *= 2 if @pledgeCombo
return baseDmg
end
def pbEffectGeneral(user)
user.effects[PBEffects::FirstPledge] = 0
return if !@pledgeSetup
@battle.pbDisplay(_INTL("{1} is waiting for {2}'s move...",
user.pbThis,@pledgeOtherUser.pbThis(true)))
@pledgeOtherUser.effects[PBEffects::FirstPledge] = @function
@pledgeOtherUser.effects[PBEffects::MoveNext] = true
user.lastMoveFailed = true # Treated as a failure for Stomping Tantrum
end
def pbEffectAfterAllHits(user,target)
return if !@pledgeCombo
msg = nil; animName = nil
case @comboEffect
when :SeaOfFire # Grass + Fire
if user.pbOpposingSide.effects[PBEffects::SeaOfFire]==0
user.pbOpposingSide.effects[PBEffects::SeaOfFire] = 4
msg = _INTL("A sea of fire enveloped {1}!",user.pbOpposingTeam(true))
animName = (user.opposes?) ? "SeaOfFire" : "SeaOfFireOpp"
end
when :Rainbow # Fire + Water
if user.pbOwnSide.effects[PBEffects::Rainbow]==0
user.pbOwnSide.effects[PBEffects::Rainbow] = 4
msg = _INTL("A rainbow appeared in the sky on {1}'s side!",user.pbTeam(true))
animName = (user.opposes?) ? "RainbowOpp" : "Rainbow"
end
when :Swamp # Water + Grass
if user.pbOpposingSide.effects[PBEffects::Swamp]==0
user.pbOpposingSide.effects[PBEffects::Swamp] = 4
msg = _INTL("A swamp enveloped {1}!",user.pbOpposingTeam(true))
animName = (user.opposes?) ? "Swamp" : "SwampOpp"
end
end
@battle.pbDisplay(msg) if msg
@battle.pbCommonAnimation(animName) if animName
end
def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true)
return if @pledgeSetup # No animation for setting up
id = @overrideAnim if @overrideAnim!=nil
return super
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
begin
module PBWeather
None = 0
Sun = 1
Rain = 2
Sandstorm = 3
Hail = 4
HarshSun = 5
HeavyRain = 6
StrongWinds = 7
ShadowSky = 8
def self.animationName(weather)
case weather
when Sun; return "Sun"
when Rain; return "Rain"
when Sandstorm; return "Sandstorm"
when Hail; return "Hail"
when HarshSun; return "HarshSun"
when HeavyRain; return "HeavyRain"
when StrongWinds; return "StrongWinds"
when ShadowSky; return "ShadowSky"
end
return nil
end
end
rescue Exception
if $!.is_a?(SystemExit) || "#{$!.class}"=="Reset"
raise $!
end
end

View File

@@ -0,0 +1,230 @@
module PokeBattle_BattleCommon
#=============================================================================
# Store caught Pokémon
#=============================================================================
def pbStorePokemon(pkmn)
# Nickname the Pokémon (unless it's a Shadow Pokémon)
if !pkmn.shadowPokemon?
if pbDisplayConfirm(_INTL("Would you like to give a nickname to {1}?",pkmn.name))
nickname = @scene.pbNameEntry(_INTL("{1}'s nickname?",pkmn.speciesName),pkmn)
pkmn.name = nickname if nickname!=""
end
end
# Store the Pokémon
currentBox = @peer.pbCurrentBox
storedBox = @peer.pbStorePokemon(pbPlayer,pkmn)
if storedBox<0
pbDisplayPaused(_INTL("{1} has been added to your party.",pkmn.name))
@initialItems[0][pbPlayer.party.length-1] = pkmn.item if @initialItems
return
end
# Messages saying the Pokémon was stored in a PC box
creator = @peer.pbGetStorageCreatorName
curBoxName = @peer.pbBoxName(currentBox)
boxName = @peer.pbBoxName(storedBox)
if storedBox!=currentBox
if creator
pbDisplayPaused(_INTL("Box \"{1}\" on {2}'s PC was full.",curBoxName,creator))
else
pbDisplayPaused(_INTL("Box \"{1}\" on someone's PC was full.",curBoxName))
end
pbDisplayPaused(_INTL("{1} was transferred to box \"{2}\".",pkmn.name,boxName))
else
if creator
pbDisplayPaused(_INTL("{1} was transferred to {2}'s PC.",pkmn.name,creator))
else
pbDisplayPaused(_INTL("{1} was transferred to someone's PC.",pkmn.name))
end
pbDisplayPaused(_INTL("It was stored in box \"{1}\".",boxName))
end
end
# Register all caught Pokémon in the Pokédex, and store them.
def pbRecordAndStoreCaughtPokemon
@caughtPokemon.each do |pkmn|
pbSeenForm(pkmn) # In case the form changed upon leaving battle
# Record the Pokémon's species as owned in the Pokédex
if !pbPlayer.hasOwned?(pkmn.species)
pbPlayer.setOwned(pkmn.species)
if $Trainer.pokedex
pbDisplayPaused(_INTL("{1}'s data was added to the Pokédex.",pkmn.name))
@scene.pbShowPokedex(pkmn.species)
end
end
# Record a Shadow Pokémon's species as having been caught
if pkmn.shadowPokemon?
pbPlayer.shadowcaught = [] if !pbPlayer.shadowcaught
pbPlayer.shadowcaught[pkmn.species] = true
end
# Store caught Pokémon
pbStorePokemon(pkmn)
end
@caughtPokemon.clear
end
#=============================================================================
# Throw a Poké Ball
#=============================================================================
def pbThrowPokeBall(idxBattler,ball,rareness=nil,showPlayer=false)
# Determine which Pokémon you're throwing the Poké Ball at
battler = nil
if opposes?(idxBattler)
battler = @battlers[idxBattler]
else
battler = @battlers[idxBattler].pbDirectOpposing(true)
end
if battler.fainted?
battler.eachAlly do |b|
battler = b
break
end
end
# Messages
itemName = PBItems.getName(ball)
if battler.fainted?
if itemName.starts_with_vowel?
pbDisplay(_INTL("{1} threw an {2}!",pbPlayer.name,itemName))
else
pbDisplay(_INTL("{1} threw a {2}!",pbPlayer.name,itemName))
end
pbDisplay(_INTL("But there was no target..."))
return
end
if itemName.starts_with_vowel?
pbDisplayBrief(_INTL("{1} threw an {2}!",pbPlayer.name,itemName))
else
pbDisplayBrief(_INTL("{1} threw a {2}!",pbPlayer.name,itemName))
end
# Animation of opposing trainer blocking Poké Balls (unless it's a Snag Ball
# at a Shadow Pokémon)
if trainerBattle? && !(pbIsSnagBall?(ball) && battler.shadowPokemon?)
@scene.pbThrowAndDeflect(ball,1)
pbDisplay(_INTL("The Trainer blocked your Poké Ball! Don't be a thief!"))
return
end
# Calculate the number of shakes (4=capture)
pkmn = battler.pokemon
@criticalCapture = false
numShakes = pbCaptureCalc(pkmn,battler,rareness,ball)
PBDebug.log("[Threw Poké Ball] #{itemName}, #{numShakes} shakes (4=capture)")
# Animation of Ball throw, absorb, shake and capture/burst out
@scene.pbThrow(ball,numShakes,@criticalCapture,battler.index,showPlayer)
# Outcome message
case numShakes
when 0
pbDisplay(_INTL("Oh no! The Pokémon broke free!"))
BallHandlers.onFailCatch(ball,self,battler)
when 1
pbDisplay(_INTL("Aww! It appeared to be caught!"))
BallHandlers.onFailCatch(ball,self,battler)
when 2
pbDisplay(_INTL("Aargh! Almost had it!"))
BallHandlers.onFailCatch(ball,self,battler)
when 3
pbDisplay(_INTL("Gah! It was so close, too!"))
BallHandlers.onFailCatch(ball,self,battler)
when 4
pbDisplayBrief(_INTL("Gotcha! {1} was caught!",pkmn.name))
@scene.pbThrowSuccess # Play capture success jingle
pbRemoveFromParty(battler.index,battler.pokemonIndex)
# Gain Exp
if GAIN_EXP_FOR_CAPTURE
battler.captured = true
pbGainExp
battler.captured = false
end
battler.pbReset
if trainerBattle?
@decision = 1 if pbAllFainted?(battler.index)
else
@decision = 4 if pbAllFainted?(battler.index) # Battle ended by capture
end
# Modify the Pokémon's properties because of the capture
if pbIsSnagBall?(ball)
pkmn.ot = pbPlayer.name
pkmn.trainerID = pbPlayer.id
end
BallHandlers.onCatch(ball,self,pkmn)
pkmn.ballused = pbGetBallType(ball)
pkmn.makeUnmega if pkmn.mega?
pkmn.makeUnprimal
pkmn.pbUpdateShadowMoves if pkmn.shadowPokemon?
pkmn.pbRecordFirstMoves
# Reset form
pkmn.forcedForm = nil if MultipleForms.hasFunction?(pkmn.species,"getForm")
@peer.pbOnLeavingBattle(self,pkmn,true,true)
# Make the Poké Ball and data box disappear
@scene.pbHideCaptureBall(idxBattler)
# Save the Pokémon for storage at the end of battle
@caughtPokemon.push(pkmn)
end
end
#=============================================================================
# Calculate how many shakes a thrown Poké Ball will make (4 = capture)
#=============================================================================
def pbCaptureCalc(pkmn,battler,rareness,ball)
return 4 if $DEBUG && Input.press?(Input::CTRL)
# Get a rareness if one wasn't provided
if !rareness
rareness = pbGetSpeciesData(pkmn.species,pkmn.form,SpeciesRareness)
end
# Modify rareness depending on the Poké Ball's effect
ultraBeast = (isConst?(battler.species,PBSpecies,:NIHILEGO) ||
isConst?(battler.species,PBSpecies,:BUZZWOLE) ||
isConst?(battler.species,PBSpecies,:PHEROMOSA) ||
isConst?(battler.species,PBSpecies,:XURKITREE) ||
isConst?(battler.species,PBSpecies,:CELESTEELA) ||
isConst?(battler.species,PBSpecies,:KARTANA) ||
isConst?(battler.species,PBSpecies,:GUZZLORD) ||
isConst?(battler.species,PBSpecies,:POIPOLE) ||
isConst?(battler.species,PBSpecies,:NAGANADEL) ||
isConst?(battler.species,PBSpecies,:STAKATAKA) ||
isConst?(battler.species,PBSpecies,:BLACEPHALON))
if !ultraBeast || isConst?(ball,PBItems,:BEASTBALL)
rareness = BallHandlers.modifyCatchRate(ball,rareness,self,battler,ultraBeast)
else
rareness /= 10
end
# First half of the shakes calculation
a = battler.totalhp
b = battler.hp
x = ((3*a-2*b)*rareness.to_f)/(3*a)
# Calculation modifiers
if battler.status==PBStatuses::SLEEP || battler.status==PBStatuses::FROZEN
x *= 2.5
elsif battler.status!=PBStatuses::NONE
x *= 1.5
end
x = x.floor
x = 1 if x<1
# Definite capture, no need to perform randomness checks
return 4 if x>=255 || BallHandlers.isUnconditional?(ball,self,battler)
# Second half of the shakes calculation
y = ( 65536 / ((255.0/x)**0.1875) ).floor
# Critical capture check
if ENABLE_CRITICAL_CAPTURES
c = 0
numOwned = $Trainer.pokedexOwned
if numOwned>600; c = x*5/12
elsif numOwned>450; c = x*4/12
elsif numOwned>300; c = x*3/12
elsif numOwned>150; c = x*2/12
elsif numOwned>30; c = x/12
end
# Calculate the number of shakes
if c>0 && pbRandom(256)<c
@criticalCapture = true
return 4 if pbRandom(65536)<y
return 0
end
end
# Calculate the number of shakes
numShakes = 0
for i in 0...4
break if numShakes<i
numShakes += 1 if pbRandom(65536)<y
end
return numShakes
end
end

View File

@@ -0,0 +1,781 @@
# Results of battle:
# 0 - Undecided or aborted
# 1 - Player won
# 2 - Player lost
# 3 - Player or wild Pokémon ran from battle, or player forfeited the match
# 4 - Wild Pokémon was caught
# 5 - Draw
# Possible actions a battler can take in a round:
# :None
# :UseMove
# :SwitchOut
# :UseItem
# :Call
# :Run
# :Shift
# NOTE: If you want to have more than 3 Pokémon on a side at once, you will need
# to edit some code. Mainly this is to change/add coordinates for the
# sprites, describe the relationships between Pokémon and trainers, and to
# change messages. The methods that will need editing are as follows:
# class PokeBattle_Battle
# def setBattleMode
# def pbGetOwnerIndexFromBattlerIndex
# def pbGetOpposingIndicesInOrder
# def nearBattlers?
# def pbStartBattleSendOut
# def pbEORShiftDistantBattlers
# def pbCanShift?
# def pbEndOfRoundPhase
# class TargetMenuDisplay
# def initialize
# class PokemonDataBox
# def initializeDataBoxGraphic
# module PokeBattle_SceneConstants
# def self.pbBattlerPosition
# def self.pbTrainerPosition
# class PokemonTemp
# def recordBattleRule
# (There is no guarantee that this list is complete.)
class PokeBattle_Battle
attr_reader :scene # Scene object for this battle
attr_reader :peer
attr_reader :field # Effects common to the whole of a battle
attr_reader :sides # Effects common to each side of a battle
attr_reader :positions # Effects that apply to a battler position
attr_reader :battlers # Currently active Pokémon
attr_reader :sideSizes # Array of number of battlers per side
attr_accessor :backdrop # Filename fragment used for background graphics
attr_accessor :backdropBase # Filename fragment used for base graphics
attr_accessor :time # Time of day (0=day, 1=eve, 2=night)
attr_accessor :environment # Battle surroundings (for mechanics purposes)
attr_reader :turnCount
attr_accessor :decision # Decision: 0=undecided; 1=win; 2=loss; 3=escaped; 4=caught
attr_reader :player # Player trainer (or array of trainers)
attr_reader :opponent # Opponent trainer (or array of trainers)
attr_accessor :items # Items held by opponents
attr_accessor :endSpeeches
attr_accessor :endSpeechesWin
attr_accessor :party1starts # Array of start indexes for each player-side trainer's party
attr_accessor :party2starts # Array of start indexes for each opponent-side trainer's party
attr_accessor :internalBattle # Internal battle flag
attr_accessor :debug # Debug flag
attr_accessor :canRun # True if player can run from battle
attr_accessor :canLose # True if player won't black out if they lose
attr_accessor :switchStyle # Switch/Set "battle style" option
attr_accessor :showAnims # "Battle Effects" option
attr_accessor :controlPlayer # Whether player's Pokémon are AI controlled
attr_accessor :expGain # Whether Pokémon can gain Exp/EVs
attr_accessor :moneyGain # Whether the player can gain/lose money
attr_accessor :rules
attr_accessor :choices # Choices made by each Pokémon this round
attr_accessor :megaEvolution # Battle index of each trainer's Pokémon to Mega Evolve
attr_reader :initialItems
attr_reader :recycleItems
attr_reader :belch
attr_reader :battleBond
attr_reader :usedInBattle # Whether each Pokémon was used in battle (for Burmy)
attr_reader :successStates # Success states
attr_accessor :lastMoveUsed # Last move used
attr_accessor :lastMoveUser # Last move user
attr_reader :switching # True if during the switching phase of the round
attr_reader :futureSight # True if Future Sight is hitting
attr_reader :endOfRound # True during the end of round
attr_accessor :moldBreaker # True if Mold Breaker applies
attr_reader :struggle # The Struggle move
include PokeBattle_BattleCommon
def pbRandom(x); return rand(x); end
#=============================================================================
# Creating the battle class
#=============================================================================
def initialize(scene,p1,p2,player,opponent)
if p1.length==0
raise ArgumentError.new(_INTL("Party 1 has no Pokémon."))
elsif p2.length==0
raise ArgumentError.new(_INTL("Party 2 has no Pokémon."))
end
@scene = scene
@peer = PokeBattle_BattlePeer.create
@battleAI = PokeBattle_AI.new(self)
@field = PokeBattle_ActiveField.new # Whole field (gravity/rooms)
@sides = [PokeBattle_ActiveSide.new, # Player's side
PokeBattle_ActiveSide.new] # Foe's side
@positions = [] # Battler positions
@battlers = []
@sideSizes = [1,1] # Single battle, 1v1
@backdrop = ""
@backdropBase = nil
@time = 0
@environment = PBEnvironment::None # e.g. Tall grass, cave, still water
@turnCount = 0
@decision = 0
@caughtPokemon = []
player = [player] if !player.nil? && !player.is_a?(Array)
opponent = [opponent] if !opponent.nil? && !opponent.is_a?(Array)
@player = player # Array of PokeBattle_Trainer objects, or nil
@opponent = opponent # Array of PokeBattle_Trainer objects, or nil
@items = nil
@endSpeeches = []
@endSpeechesWin = []
@party1 = p1
@party2 = p2
@party1order = Array.new(@party1.length) { |i| i }
@party2order = Array.new(@party2.length) { |i| i }
@party1starts = [0]
@party2starts = [0]
@internalBattle = true
@debug = false
@canRun = true
@canLose = false
@switchStyle = true
@showAnims = true
@controlPlayer = false
@expGain = true
@moneyGain = true
@rules = {}
@priority = []
@priorityTrickRoom = false
@choices = []
@megaEvolution = [
[-1] * (@player ? @player.length : 1),
[-1] * (@opponent ? @opponent.length : 1)
]
@initialItems = [
Array.new(@party1.length) { |i| (@party1[i]) ? @party1[i].item : 0 },
Array.new(@party2.length) { |i| (@party2[i]) ? @party2[i].item : 0 }
]
@recycleItems = [Array.new(@party1.length,0),Array.new(@party2.length,0)]
@belch = [Array.new(@party1.length,false),Array.new(@party2.length,false)]
@battleBond = [Array.new(@party1.length,false),Array.new(@party2.length,false)]
@usedInBattle = [Array.new(@party1.length,false),Array.new(@party2.length,false)]
@successStates = []
@lastMoveUsed = -1
@lastMoveUser = -1
@switching = false
@futureSight = false
@endOfRound = false
@moldBreaker = false
@runCommand = 0
@nextPickupUse = 0
if hasConst?(PBMoves,:STRUGGLE)
@struggle = PokeBattle_Move.pbFromPBMove(self,PBMove.new(getConst(PBMoves,:STRUGGLE)))
else
@struggle = PokeBattle_Struggle.new(self,nil)
end
end
#=============================================================================
# Information about the type and size of the battle
#=============================================================================
def wildBattle?; return @opponent.nil?; end
def trainerBattle?; return !@opponent.nil?; end
# Sets the number of battler slots on each side of the field independently.
# For "1v2" names, the first number is for the player's side and the second
# number is for the opposing side.
def setBattleMode(mode)
@sideSizes =
case mode
when "triple", "3v3"; [3,3]
when "3v2"; [3,2]
when "3v1"; [3,1]
when "2v3"; [2,3]
when "double", "2v2"; [2,2]
when "2v1"; [2,1]
when "1v3"; [1,3]
when "1v2"; [1,2]
else; [1,1] # Single, 1v1 (default)
end
end
def singleBattle?
return pbSideSize(0)==1 && pbSideSize(1)==1
end
def pbSideSize(index)
return @sideSizes[index%2]
end
def maxBattlerIndex
return (pbSideSize(0)>pbSideSize(1)) ? (pbSideSize(0)-1)*2 : pbSideSize(1)*2-1
end
#=============================================================================
# Trainers and owner-related methods
#=============================================================================
def pbPlayer; return @player[0]; end
# Given a battler index, returns the index within @player/@opponent of the
# trainer that controls that battler index.
# NOTE: You shouldn't ever have more trainers on a side than there are battler
# positions on that side. This method doesn't account for if you do.
def pbGetOwnerIndexFromBattlerIndex(idxBattler)
trainer = (opposes?(idxBattler)) ? @opponent : @player
return 0 if !trainer
case trainer.length
when 2
n = pbSideSize(idxBattler%2)
return [0,0,1][idxBattler/2] if n==3
return idxBattler/2 # Same as [0,1][idxBattler/2], i.e. 2 battler slots
when 3; return idxBattler/2
end
return 0
end
def pbGetOwnerFromBattlerIndex(idxBattler)
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
return (opposes?(idxBattler)) ? @opponent[idxTrainer] : @player[idxTrainer]
end
def pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
ret = -1
pbPartyStarts(idxBattler).each_with_index do |start,i|
break if start>idxParty
ret = i
end
return ret
end
# Only used for the purpose of an error message when one trainer tries to
# switch another trainer's Pokémon.
def pbGetOwnerFromPartyIndex(idxBattler,idxParty)
idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
return (opposes?(idxBattler)) ? @opponent[idxTrainer] : @player[idxTrainer]
end
def pbGetOwnerName(idxBattler)
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
return @opponent[idxTrainer].fullname if opposes?(idxBattler) # Opponent
return @player[idxTrainer].fullname if idxTrainer>0 # Ally trainer
return @player[idxTrainer].name # Player
end
def pbGetOwnerItems(idxBattler)
return [] if !@items || !opposes?(idxBattler)
return @items[pbGetOwnerIndexFromBattlerIndex(idxBattler)]
end
# Returns whether the battler in position idxBattler is owned by the same
# trainer that owns the Pokémon in party slot idxParty. This assumes that
# both the battler position and the party slot are from the same side.
def pbIsOwner?(idxBattler,idxParty)
idxTrainer1 = pbGetOwnerIndexFromBattlerIndex(idxBattler)
idxTrainer2 = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
return idxTrainer1==idxTrainer2
end
def pbOwnedByPlayer?(idxBattler)
return false if opposes?(idxBattler)
return pbGetOwnerIndexFromBattlerIndex(idxBattler)==0
end
# Returns the number of Pokémon positions controlled by the given trainerIndex
# on the given side of battle.
def pbNumPositions(side,idxTrainer)
ret = 0
for i in 0...pbSideSize(side)
t = pbGetOwnerIndexFromBattlerIndex(i*2+side)
next if t!=idxTrainer
ret += 1
end
return ret
end
#=============================================================================
# Get party information (counts all teams on the same side)
#=============================================================================
def pbParty(idxBattler)
return (opposes?(idxBattler)) ? @party2 : @party1
end
def pbOpposingParty(idxBattler)
return (opposes?(idxBattler)) ? @party1 : @party2
end
def pbPartyOrder(idxBattler)
return (opposes?(idxBattler)) ? @party2order : @party1order
end
def pbPartyStarts(idxBattler)
return (opposes?(idxBattler)) ? @party2starts : @party1starts
end
# Returns the player's team in its display order. Used when showing the party
# screen.
def pbPlayerDisplayParty(idxBattler=0)
partyOrders = pbPartyOrder(idxBattler)
idxStart, idxEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
ret = []
eachInTeamFromBattlerIndex(idxBattler) { |pkmn,i| ret[partyOrders[i]-idxStart] = pkmn }
return ret
end
def pbAbleCount(idxBattler=0)
party = pbParty(idxBattler)
count = 0
party.each { |pkmn| count += 1 if pkmn && pkmn.able? }
return count
end
def pbAbleNonActiveCount(idxBattler=0)
party = pbParty(idxBattler)
inBattleIndices = []
eachSameSideBattler(idxBattler) { |b| inBattleIndices.push(b.pokemonIndex) }
count = 0
party.each_with_index do |pkmn,idxParty|
next if !pkmn || !pkmn.able?
next if inBattleIndices.include?(idxParty)
count += 1
end
return count
end
def pbAllFainted?(idxBattler=0)
return pbAbleCount(idxBattler)==0
end
# For the given side of the field (0=player's, 1=opponent's), returns an array
# containing the number of able Pokémon in each team.
def pbAbleTeamCounts(side)
party = pbParty(side)
partyStarts = pbPartyStarts(side)
ret = []
idxTeam = -1
nextStart = 0
party.each_with_index do |pkmn,i|
if i>=nextStart
idxTeam += 1
nextStart = (idxTeam<partyStarts.length-1) ? partyStarts[idxTeam+1] : party.length
end
next if !pkmn || !pkmn.able?
ret[idxTeam] = 0 if !ret[idxTeam]
ret[idxTeam] += 1
end
return ret
end
#=============================================================================
# Get team information (a team is only the Pokémon owned by a particular
# trainer)
#=============================================================================
def pbTeamIndexRangeFromBattlerIndex(idxBattler)
partyStarts = pbPartyStarts(idxBattler)
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
idxPartyStart = partyStarts[idxTrainer]
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : pbParty(idxBattler).length
return idxPartyStart, idxPartyEnd
end
def pbTeamLengthFromBattlerIndex(idxBattler)
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
return idxPartyEnd-idxPartyStart
end
def eachInTeamFromBattlerIndex(idxBattler)
party = pbParty(idxBattler)
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
party.each_with_index { |pkmn,i| yield pkmn,i if pkmn && i>=idxPartyStart && i<idxPartyEnd }
end
def eachInTeam(side,idxTrainer)
party = pbParty(side)
partyStarts = pbPartyStarts(side)
idxPartyStart = partyStarts[idxTrainer]
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : party.length
party.each_with_index { |pkmn,i| yield pkmn,i if pkmn && i>=idxPartyStart && i<idxPartyEnd }
end
# Used for Illusion.
# NOTE: This cares about the temporary rearranged order of the team. That is,
# if you do some switching, the last Pokémon in the team could change
# and the Illusion could be a different Pokémon.
def pbLastInTeam(idxBattler)
party = pbParty(idxBattler)
partyOrders = pbPartyOrder(idxBattler)
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
ret = -1
party.each_with_index do |pkmn,i|
next if i<idxPartyStart || i>=idxPartyEnd # Check the team only
next if !pkmn || !pkmn.able? # Can't copy a non-fainted Pokémon or egg
ret = i if partyOrders[i]>partyOrders[ret]
end
return ret
end
# Used to calculate money gained/lost after winning/losing a battle.
def pbMaxLevelInTeam(side,idxTrainer)
ret = 1
eachInTeam(side,idxTrainer) do |pkmn,i|
ret = pkmn.level if pkmn.level>ret
end
return ret
end
#=============================================================================
# Iterate through battlers
#=============================================================================
def eachBattler
@battlers.each { |b| yield b if b && !b.fainted? }
end
def eachSameSideBattler(idxBattler=0)
idxBattler = idxBattler.index if idxBattler.respond_to?("index")
@battlers.each { |b| yield b if b && !b.fainted? && !b.opposes?(idxBattler) }
end
def eachOtherSideBattler(idxBattler=0)
idxBattler = idxBattler.index if idxBattler.respond_to?("index")
@battlers.each { |b| yield b if b && !b.fainted? && b.opposes?(idxBattler) }
end
def pbSideBattlerCount(idxBattler=0)
ret = 0
eachSameSideBattler(idxBattler) { |b| ret += 1 }
return ret
end
def pbOpposingBattlerCount(idxBattler=0)
ret = 0
eachOtherSideBattler(idxBattler) { |b| ret += 1 }
return ret
end
# This method only counts the player's Pokémon, not a partner trainer's.
def pbPlayerBattlerCount
ret = 0
eachSameSideBattler { |b| ret += 1 if b.pbOwnedByPlayer? }
return ret
end
def pbCheckGlobalAbility(abil)
eachBattler { |b| return b if b.hasActiveAbility?(abil) }
return nil
end
def pbCheckOpposingAbility(abil,idxBattler=0,nearOnly=false)
eachOtherSideBattler(idxBattler) do |b|
next if nearOnly && !b.near?(idxBattler)
return b if b.hasActiveAbility?(abil)
end
return nil
end
# Given a battler index, and using battle side sizes, returns an array of
# battler indices from the opposing side that are in order of most "opposite".
# Used when choosing a target and pressing up/down to move the cursor to the
# opposite side, and also when deciding which target to select first for some
# moves.
def pbGetOpposingIndicesInOrder(idxBattler)
case pbSideSize(0)
when 1
case pbSideSize(1)
when 1 # 1v1 single
return [0] if opposes?(idxBattler)
return [1]
when 2 # 1v2
return [0] if opposes?(idxBattler)
return [3,1]
when 3 # 1v3
return [0] if opposes?(idxBattler)
return [3,5,1]
end
when 2
case pbSideSize(1)
when 1 # 2v1
return [0,2] if opposes?(idxBattler)
return [1]
when 2 # 2v2 double
return [[3,1],[2,0],[1,3],[0,2]][idxBattler]
when 3 # 2v3
return [[5,3,1],[2,0],[3,1,5]][idxBattler] if idxBattler<3
return [0,2]
end
when 3
case pbSideSize(1)
when 1 # 3v1
return [2,0,4] if opposes?(idxBattler)
return [1]
when 2 # 3v2
return [[3,1],[2,4,0],[3,1],[2,0,4],[1,3]][idxBattler]
when 3 # 3v3 triple
return [[5,3,1],[4,2,0],[3,5,1],[2,0,4],[1,3,5],[0,2,4]][idxBattler]
end
end
return [idxBattler]
end
#=============================================================================
# Comparing the positions of two battlers
#=============================================================================
def opposes?(idxBattler1,idxBattler2=0)
idxBattler1 = idxBattler1.index if idxBattler1.respond_to?("index")
idxBattler2 = idxBattler2.index if idxBattler2.respond_to?("index")
return (idxBattler1&1)!=(idxBattler2&1)
end
def nearBattlers?(idxBattler1,idxBattler2)
return false if idxBattler1==idxBattler2
return true if pbSideSize(0)<=2 && pbSideSize(1)<=2
# Get all pairs of battler positions that are not close to each other
pairsArray = [[0,4],[1,5]] # Covers 3v1 and 1v3
case pbSideSize(0)
when 3
case pbSideSize(1)
when 3 # 3v3 (triple)
pairsArray.push([0,1])
pairsArray.push([4,5])
when 2 # 3v2
pairsArray.push([0,1])
pairsArray.push([3,4])
end
when 2 # 2v3
pairsArray.push([0,1])
pairsArray.push([2,5])
end
# See if any pair matches the two battlers being assessed
pairsArray.each do |pair|
return false if pair.include?(idxBattler1) && pair.include?(idxBattler2)
end
return true
end
#=============================================================================
# Altering a party or rearranging battlers
#=============================================================================
def pbRemoveFromParty(idxBattler,idxParty)
party = pbParty(idxBattler)
# Erase the Pokémon from the party
party[idxParty] = nil
# Rearrange the display order of the team to place the erased Pokémon last
# in it (to avoid gaps)
partyOrders = pbPartyOrder(idxBattler)
partyStarts = pbPartyStarts(idxBattler)
idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
idxPartyStart = partyStarts[idxTrainer]
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : party.length
origPartyPos = partyOrders[idxParty] # Position of erased Pokémon initially
partyOrders[idxParty] = idxPartyEnd # Put erased Pokémon last in the team
party.each_with_index do |pkmn,i|
next if i<idxPartyStart || i>=idxPartyEnd # Only check the team
next if partyOrders[i]<origPartyPos # Appeared before erased Pokémon
partyOrders[i] -= 1 # Appeared after erased Pokémon; bump it up by 1
end
end
def pbSwapBattlers(idxA,idxB)
return false if !@battlers[idxA] || !@battlers[idxB]
# Can't swap if battlers aren't owned by the same trainer
return false if opposes?(idxA,idxB)
return false if pbGetOwnerIndexFromBattlerIndex(idxA)!=pbGetOwnerIndexFromBattlerIndex(idxB)
@battlers[idxA], @battlers[idxB] = @battlers[idxB], @battlers[idxA]
@battlers[idxA].index, @battlers[idxB].index = @battlers[idxB].index, @battlers[idxA].index
@choices[idxA], @choices[idxB] = @choices[idxB], @choices[idxA]
@scene.pbSwapBattlerSprites(idxA,idxB)
# Swap the target of any battlers' effects that point at either of the
# swapped battlers, to ensure they still point at the correct target
# NOTE: LeechSeed is not swapped, because drained HP goes to whichever
# Pokémon is in the position that Leech Seed was used from.
# NOTE: PerishSongUser doesn't need to change, as it's only used to
# determine which side the Perish Song user was on, and a battler
# can't change sides.
effectsToSwap = [PBEffects::Attract,
PBEffects::BideTarget,
PBEffects::CounterTarget,
PBEffects::LockOnPos,
PBEffects::MeanLook,
PBEffects::MirrorCoatTarget,
PBEffects::SkyDrop,
PBEffects::TrappingUser]
eachBattler do |b|
for i in effectsToSwap
next if b.effects[i]!=idxA && b.effects[i]!=idxB
b.effects[i] = (b.effects[i]==idxA) ? idxB : idxA
end
end
return true
end
#=============================================================================
#
#=============================================================================
# Returns the battler representing the Pokémon at index idxParty in its party,
# on the same side as a battler with battler index of idxBattlerOther.
def pbFindBattler(idxParty,idxBattlerOther=0)
eachSameSideBattler(idxBattlerOther) { |b| return b if b.pokemonIndex==idxParty }
return nil
end
# Only used for Wish, as the Wishing Pokémon will no longer be in battle.
def pbThisEx(idxBattler,idxParty)
party = pbParty(idxBattler)
if opposes?(idxBattler)
return _INTL("The opposing {1}",party[idxParty].name) if trainerBattle?
return _INTL("The wild {1}",party[idxParty].name)
end
return _INTL("The ally {1}",party[idxParty].name) if !pbOwnedByPlayer?(idxBattler)
return party[idxParty].name
end
def pbSetSeen(battler)
return if !battler || !@internalBattle
pbPlayer.seen[battler.displaySpecies] = true
pbSeenForm(battler.displaySpecies,battler.displayGender,battler.displayForm)
end
def nextPickupUse
@nextPickupUse += 1
return @nextPickupUse
end
#=============================================================================
# Weather and terrain
#=============================================================================
def defaultWeather=(value)
@field.defaultWeather = value
@field.weather = value
@field.weatherDuration = -1
end
# Returns the effective weather (note that weather effects can be negated)
def pbWeather
eachBattler { |b| return PBWeather::None if b.hasActiveAbility?([:CLOUDNINE,:AIRLOCK]) }
return @field.weather
end
# Used for causing weather by a move or by an ability.
def pbStartWeather(user,newWeather,fixedDuration=false,showAnim=true)
return if @field.weather==newWeather
@field.weather = newWeather
duration = (fixedDuration) ? 5 : -1
if duration>0 && user && user.itemActive?
duration = BattleHandlers.triggerWeatherExtenderItem(user.item,
@field.weather,duration,user,self)
end
@field.weatherDuration = duration
pbCommonAnimation(PBWeather.animationName(@field.weather)) if showAnim
pbHideAbilitySplash(user) if user
case @field.weather
when PBWeather::Sun; pbDisplay(_INTL("The sunlight turned harsh!"))
when PBWeather::Rain; pbDisplay(_INTL("It started to rain!"))
when PBWeather::Sandstorm; pbDisplay(_INTL("A sandstorm brewed!"))
when PBWeather::Hail; pbDisplay(_INTL("It started to hail!"))
when PBWeather::HarshSun; pbDisplay(_INTL("The sunlight turned extremely harsh!"))
when PBWeather::HeavyRain; pbDisplay(_INTL("A heavy rain began to fall!"))
when PBWeather::StrongWinds; pbDisplay(_INTL("Mysterious strong winds are protecting Flying-type Pokémon!"))
when PBWeather::ShadowSky; pbDisplay(_INTL("A shadow sky appeared!"))
end
# Check for end of primordial weather, and weather-triggered form changes
eachBattler { |b| b.pbCheckFormOnWeatherChange }
pbEndPrimordialWeather
end
def pbEndPrimordialWeather
oldWeather = @field.weather
# End Primordial Sea, Desolate Land, Delta Stream
case @field.weather
when PBWeather::HarshSun
if !pbCheckGlobalAbility(:DESOLATELAND)
@field.weather = PBWeather::None
pbDisplay("The harsh sunlight faded!")
end
when PBWeather::HeavyRain
if !pbCheckGlobalAbility(:PRIMORDIALSEA)
@field.weather = PBWeather::None
pbDisplay("The heavy rain has lifted!")
end
when PBWeather::StrongWinds
if !pbCheckGlobalAbility(:DELTASTREAM)
@field.weather = PBWeather::None
pbDisplay("The mysterious air current has dissipated!")
end
end
# Check for form changes caused by the weather changing
if @field.weather!=oldWeather
eachBattler { |b| b.pbCheckFormOnWeatherChange }
end
end
def defaultTerrain=(value)
@field.defaultTerrain = value
@field.terrain = value
@field.terrainDuration = -1
end
def pbStartTerrain(user,newTerrain,fixedDuration=true)
return if @field.terrain==newTerrain
@field.terrain = newTerrain
duration = (fixedDuration) ? 5 : -1
if duration>0 && user && user.itemActive?
duration = BattleHandlers.triggerTerrainExtenderItem(user.item,
newTerrain,duration,user,self)
end
@field.terrainDuration = duration
pbCommonAnimation(PBBattleTerrains.animationName(@field.terrain))
pbHideAbilitySplash(user) if user
case @field.terrain
when PBBattleTerrains::Electric
pbDisplay(_INTL("An electric current runs across the battlefield!"))
when PBBattleTerrains::Grassy
pbDisplay(_INTL("Grass grew to cover the battlefield!"))
when PBBattleTerrains::Misty
pbDisplay(_INTL("Mist swirled about the battlefield!"))
when PBBattleTerrains::Psychic
pbDisplay(_INTL("The battlefield got weird!"))
end
# Check for terrain seeds that boost stats in a terrain
eachBattler { |b| b.pbItemTerrainStatBoostCheck }
end
#=============================================================================
# Messages and animations
#=============================================================================
def pbDisplay(msg,&block)
@scene.pbDisplayMessage(msg,&block)
end
def pbDisplayBrief(msg)
@scene.pbDisplayMessage(msg,true)
end
def pbDisplayPaused(msg,&block)
@scene.pbDisplayPausedMessage(msg,&block)
end
def pbDisplayConfirm(msg)
return @scene.pbDisplayConfirmMessage(msg)
end
def pbShowCommands(msg,commands,canCancel=true)
@scene.pbShowCommands(msg,commands,canCancel)
end
def pbAnimation(move,user,targets,hitNum=0)
@scene.pbAnimation(move,user,targets,hitNum) if @showAnims
end
def pbCommonAnimation(name,user=nil,targets=nil,hitNum=0)
@scene.pbCommonAnimation(name,user,targets,hitNum) if @showAnims
end
def pbShowAbilitySplash(battler,delay=false,logTrigger=true)
PBDebug.log("[Ability triggered] #{battler.pbThis}'s #{battler.abilityName}") if logTrigger
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@scene.pbShowAbilitySplash(battler)
if delay
Graphics.frame_rate.times { @scene.pbUpdate } # 1 second
end
end
def pbHideAbilitySplash(battler)
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@scene.pbHideAbilitySplash(battler)
end
def pbReplaceAbilitySplash(battler)
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
@scene.pbReplaceAbilitySplash(battler)
end
end

View File

@@ -0,0 +1,537 @@
class PokeBattle_Battle
class BattleAbortedException < Exception; end
def pbAbort
raise BattleAbortedException.new("Battle aborted")
end
#=============================================================================
# Makes sure all Pokémon exist that need to. Alter the type of battle if
# necessary. Will never try to create battler positions, only delete them
# (except for wild Pokémon whose number of positions are fixed). Reduces the
# size of each side by 1 and tries again. If the side sizes are uneven, only
# the larger side's size will be reduced by 1 each time, until both sides are
# an equal size (then both sides will be reduced equally).
#=============================================================================
def pbEnsureParticipants
# Prevent battles larger than 2v2 if both sides have multiple trainers
# NOTE: This is necessary to ensure that battlers can never become unable to
# hit each other due to being too far away. In such situations,
# battlers will move to the centre position at the end of a round, but
# because they cannot move into a position owned by a different
# trainer, it's possible that battlers will be unable to move close
# enough to hit each other if there are multiple trainers on each
# side.
if trainerBattle? && (@sideSizes[0]>2 || @sideSizes[1]>2) &&
@player.length>1 && @opponent.length>1
raise _INTL("Can't have battles larger than 2v2 where both sides have multiple trainers")
end
# Find out how many Pokémon each trainer has
side1counts = pbAbleTeamCounts(0)
side2counts = pbAbleTeamCounts(1)
# Change the size of the battle depending on how many wild Pokémon there are
if wildBattle? && side2counts[0]!=@sideSizes[1]
if @sideSizes[0]==@sideSizes[1]
# Even number of battlers per side, change both equally
@sideSizes = [side2counts[0],side2counts[0]]
else
# Uneven number of battlers per side, just change wild side's size
@sideSizes[1] = side2counts[0]
end
end
# Check if battle is possible, including changing the number of battlers per
# side if necessary
loop do
needsChanging = false
for side in 0...2 # Each side in turn
next if side==1 && wildBattle? # Wild side's size already checked above
sideCounts = (side==0) ? side1counts : side2counts
requireds = []
# Find out how many Pokémon each trainer on side needs to have
for i in 0...@sideSizes[side]
idxTrainer = pbGetOwnerIndexFromBattlerIndex(i*2+side)
requireds[idxTrainer] = 0 if requireds[idxTrainer].nil?
requireds[idxTrainer] += 1
end
# Compare the have values with the need values
if requireds.length>sideCounts.length
raise _INTL("Error: def pbGetOwnerIndexFromBattlerIndex gives invalid owner index ({1} for battle type {2}v{3}, trainers {4}v{5})",
requireds.length-1,@sideSizes[0],@sideSizes[1],side1counts.length,side2counts.length)
end
sideCounts.each_with_index do |count,i|
if !requireds[i] || requireds[i]==0
raise _INTL("Player-side trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)",
i+1,@sideSizes[0],@sideSizes[1]) if side==0
raise _INTL("Opposing trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)",
i+1,@sideSizes[0],@sideSizes[1]) if side==1
end
next if requireds[i]<=sideCounts[i] # Trainer has enough Pokémon to fill their positions
if requireds[i]==1
raise _INTL("Player-side trainer {1} has no able Pokémon",i+1) if side==0
raise _INTL("Opposing trainer {1} has no able Pokémon",i+1) if side==1
end
# Not enough Pokémon, try lowering the number of battler positions
needsChanging = true
break
end
break if needsChanging
end
break if !needsChanging
# Reduce one or both side's sizes by 1 and try again
if wildBattle?
PBDebug.log("#{@sideSizes[0]}v#{@sideSizes[1]} battle isn't possible " +
"(#{side1counts} player-side teams versus #{side2counts[0]} wild Pokémon)")
newSize = @sideSizes[0]-1
else
PBDebug.log("#{@sideSizes[0]}v#{@sideSizes[1]} battle isn't possible " +
"(#{side1counts} player-side teams versus #{side2counts} opposing teams)")
newSize = @sideSizes.max-1
end
if newSize==0
raise _INTL("Couldn't lower either side's size any further, battle isn't possible")
end
for side in 0...2
next if side==1 && wildBattle? # Wild Pokémon's side size is fixed
next if @sideSizes[side]==1 || newSize>@sideSizes[side]
@sideSizes[side] = newSize
end
PBDebug.log("Trying #{@sideSizes[0]}v#{@sideSizes[1]} battle instead")
end
end
#=============================================================================
# Set up all battlers
#=============================================================================
def pbCreateBattler(idxBattler,pkmn,idxParty)
if !@battlers[idxBattler].nil?
raise _INTL("Battler index {1} already exists",idxBattler)
end
@battlers[idxBattler] = PokeBattle_Battler.new(self,idxBattler)
@positions[idxBattler] = PokeBattle_ActivePosition.new
pbClearChoice(idxBattler)
@successStates[idxBattler] = PokeBattle_SuccessState.new
@battlers[idxBattler].pbInitialize(pkmn,idxParty)
end
def pbSetUpSides
ret = [[],[]]
for side in 0...2
# Set up wild Pokémon
if side==1 && wildBattle?
pbParty(1).each_with_index do |pkmn,idxPkmn|
pbCreateBattler(2*idxPkmn+side,pkmn,idxPkmn)
# Changes the Pokémon's form upon entering battle (if it should)
@peer.pbOnEnteringBattle(self,pkmn,true)
pbSetSeen(@battlers[2*idxPkmn+side])
@usedInBattle[side][idxPkmn] = true
end
next
end
# Set up player's Pokémon and trainers' Pokémon
trainer = (side==0) ? @player : @opponent
requireds = []
# Find out how many Pokémon each trainer on side needs to have
for i in 0...@sideSizes[side]
idxTrainer = pbGetOwnerIndexFromBattlerIndex(i*2+side)
requireds[idxTrainer] = 0 if requireds[idxTrainer].nil?
requireds[idxTrainer] += 1
end
# For each trainer in turn, find the needed number of Pokémon for them to
# send out, and initialize them
battlerNumber = 0
trainer.each_with_index do |t,idxTrainer|
ret[side][idxTrainer] = []
eachInTeam(side,idxTrainer) do |pkmn,idxPkmn|
next if !pkmn.able?
idxBattler = 2*battlerNumber+side
pbCreateBattler(idxBattler,pkmn,idxPkmn)
ret[side][idxTrainer].push(idxBattler)
battlerNumber += 1
break if ret[side][idxTrainer].length>=requireds[idxTrainer]
end
end
end
return ret
end
#=============================================================================
# Send out all battlers at the start of battle
#=============================================================================
def pbStartBattleSendOut(sendOuts)
# "Want to battle" messages
if wildBattle?
foeParty = pbParty(1)
case foeParty.length
when 1
pbDisplayPaused(_INTL("Oh! A wild {1} appeared!",foeParty[0].name))
when 2
pbDisplayPaused(_INTL("Oh! A wild {1} and {2} appeared!",foeParty[0].name,
foeParty[1].name))
when 3
pbDisplayPaused(_INTL("Oh! A wild {1}, {2} and {3} appeared!",foeParty[0].name,
foeParty[1].name,foeParty[2].name))
end
else # Trainer battle
case @opponent.length
when 1
pbDisplayPaused(_INTL("You are challenged by {1}!",@opponent[0].fullname))
when 2
pbDisplayPaused(_INTL("You are challenged by {1} and {2}!",@opponent[0].fullname,
@opponent[1].fullname))
when 3
pbDisplayPaused(_INTL("You are challenged by {1}, {2} and {3}!",
@opponent[0].fullname,@opponent[1].fullname,@opponent[2].fullname))
end
end
# Send out Pokémon (opposing trainers first)
for side in [1,0]
next if side==1 && wildBattle?
msg = ""
toSendOut = []
trainers = (side==0) ? @player : @opponent
# Opposing trainers and partner trainers's messages about sending out Pokémon
trainers.each_with_index do |t,i|
next if side==0 && i==0 # The player's message is shown last
msg += "\r\n" if msg.length>0
sent = sendOuts[side][i]
case sent.length
when 1
msg += _INTL("{1} sent out {2}!",t.fullname,@battlers[sent[0]].name)
when 2
msg += _INTL("{1} sent out {2} and {3}!",t.fullname,
@battlers[sent[0]].name,@battlers[sent[1]].name)
when 3
msg += _INTL("{1} sent out {2}, {3} and {4}!",t.fullname,
@battlers[sent[0]].name,@battlers[sent[1]].name,@battlers[sent[2]].name)
end
toSendOut.concat(sent)
end
# The player's message about sending out Pokémon
if side==0
msg += "\r\n" if msg.length>0
sent = sendOuts[side][0]
case sent.length
when 1
msg += _INTL("Go! {1}!",@battlers[sent[0]].name)
when 2
msg += _INTL("Go! {1} and {2}!",@battlers[sent[0]].name,@battlers[sent[1]].name)
when 3
msg += _INTL("Go! {1}, {2} and {3}!",@battlers[sent[0]].name,
@battlers[sent[1]].name,@battlers[sent[2]].name)
end
toSendOut.concat(sent)
end
pbDisplayBrief(msg) if msg.length>0
# The actual sending out of Pokémon
animSendOuts = []
toSendOut.each do |idxBattler|
animSendOuts.push([idxBattler,@battlers[idxBattler].pokemon])
end
pbSendOut(animSendOuts,true)
end
end
#=============================================================================
# Start a battle
#=============================================================================
def pbStartBattle
PBDebug.log("")
PBDebug.log("******************************************")
logMsg = "[Started battle] "
if @sideSizes[0]==1 && @sideSizes[1]==1
logMsg += "Single "
elsif @sideSizes[0]==2 && @sideSizes[1]==2
logMsg += "Double "
elsif @sideSizes[0]==3 && @sideSizes[1]==3
logMsg += "Triple "
else
logMsg += "#{@sideSizes[0]}v#{@sideSizes[1]} "
end
logMsg += "wild " if wildBattle?
logMsg += "trainer " if trainerBattle?
logMsg += "battle (#{@player.length} trainer(s) vs. "
logMsg += "#{pbParty(1).length} wild Pokémon)" if wildBattle?
logMsg += "#{@opponent.length} trainer(s))" if trainerBattle?
PBDebug.log(logMsg)
pbEnsureParticipants
begin
pbStartBattleCore
rescue BattleAbortedException
@decision = 0
@scene.pbEndBattle(@decision)
end
return @decision
end
def pbStartBattleCore
# Set up the battlers on each side
sendOuts = pbSetUpSides
# Create all the sprites and play the battle intro animation
@scene.pbStartBattle(self)
# Show trainers on both sides sending out Pokémon
pbStartBattleSendOut(sendOuts)
# Weather announcement
pbCommonAnimation(PBWeather.animationName(@field.weather))
case @field.weather
when PBWeather::Sun; pbDisplay(_INTL("The sunlight is strong."))
when PBWeather::Rain; pbDisplay(_INTL("It is raining."))
when PBWeather::Sandstorm; pbDisplay(_INTL("A sandstorm is raging."))
when PBWeather::Hail; pbDisplay(_INTL("Hail is falling."))
when PBWeather::HarshSun; pbDisplay(_INTL("The sunlight is extremely harsh."))
when PBWeather::HeavyRain; pbDisplay(_INTL("It is raining heavily."))
when PBWeather::StrongWinds; pbDisplay(_INTL("The wind is strong."))
when PBWeather::ShadowSky; pbDisplay(_INTL("The sky is shadowy."))
end
# Terrain announcement
pbCommonAnimation(PBBattleTerrains.animationName(@field.terrain))
case @field.terrain
when PBBattleTerrains::Electric
pbDisplay(_INTL("An electric current runs across the battlefield!"))
when PBBattleTerrains::Grassy
pbDisplay(_INTL("Grass is covering the battlefield!"))
when PBBattleTerrains::Misty
pbDisplay(_INTL("Mist swirls about the battlefield!"))
when PBBattleTerrains::Psychic
pbDisplay(_INTL("The battlefield is weird!"))
end
# Abilities upon entering battle
pbOnActiveAll
# Main battle loop
pbBattleLoop
end
#=============================================================================
# Main battle loop
#=============================================================================
def pbBattleLoop
@turnCount = 0
loop do # Now begin the battle loop
PBDebug.log("")
PBDebug.log("***Round #{@turnCount+1}***")
if @debug && @turnCount>=100
@decision = pbDecisionOnTime
PBDebug.log("")
PBDebug.log("***Undecided after 100 rounds, aborting***")
pbAbort
break
end
PBDebug.log("")
# Command phase
PBDebug.logonerr { pbCommandPhase }
break if @decision>0
# Attack phase
PBDebug.logonerr { pbAttackPhase }
break if @decision>0
# End of round phase
PBDebug.logonerr { pbEndOfRoundPhase }
break if @decision>0
@turnCount += 1
end
pbEndOfBattle
end
#=============================================================================
# End of battle
#=============================================================================
def pbGainMoney
return if !@internalBattle || !@moneyGain
# Money rewarded from opposing trainers
if trainerBattle?
tMoney = 0
@opponent.each_with_index do |t,i|
tMoney += pbMaxLevelInTeam(1,i)*t.moneyEarned
end
tMoney *= 2 if @field.effects[PBEffects::AmuletCoin]
tMoney *= 2 if @field.effects[PBEffects::HappyHour]
oldMoney = pbPlayer.money
pbPlayer.money += tMoney
moneyGained = pbPlayer.money-oldMoney
if moneyGained>0
pbDisplayPaused(_INTL("You got ${1} for winning!",moneyGained.to_s_formatted))
end
end
# Pick up money scattered by Pay Day
if @field.effects[PBEffects::PayDay]>0
@field.effects[PBEffects::PayDay] *= 2 if @field.effects[PBEffects::AmuletCoin]
@field.effects[PBEffects::PayDay] *= 2 if @field.effects[PBEffects::HappyHour]
oldMoney = pbPlayer.money
pbPlayer.money += @field.effects[PBEffects::PayDay]
moneyGained = pbPlayer.money-oldMoney
if moneyGained>0
pbDisplayPaused(_INTL("You picked up ${1}!",moneyGained.to_s_formatted))
end
end
end
def pbLoseMoney
return if !@internalBattle || !@moneyGain
return if $game_switches[NO_MONEY_LOSS]
maxLevel = pbMaxLevelInTeam(0,0) # Player's Pokémon only, not partner's
multiplier = [8,16,24,36,48,64,80,100,120]
idxMultiplier = [pbPlayer.numbadges,multiplier.length-1].min
tMoney = maxLevel*multiplier[idxMultiplier]
tMoney = pbPlayer.money if tMoney>pbPlayer.money
oldMoney = pbPlayer.money
pbPlayer.money -= tMoney
moneyLost = oldMoney-pbPlayer.money
if moneyLost>0
if trainerBattle?
pbDisplayPaused(_INTL("You gave ${1} to the winner...",moneyLost.to_s_formatted))
else
pbDisplayPaused(_INTL("You panicked and dropped ${1}...",moneyLost.to_s_formatted))
end
end
end
def pbEndOfBattle
oldDecision = @decision
@decision = 4 if @decision==1 && wildBattle? && @caughtPokemon.length>0
case oldDecision
##### WIN #####
when 1
PBDebug.log("")
PBDebug.log("***Player won***")
if trainerBattle?
@scene.pbTrainerBattleSuccess
case @opponent.length
when 1
pbDisplayPaused(_INTL("You defeated {1}!",@opponent[0].fullname))
when 2
pbDisplayPaused(_INTL("You defeated {1} and {2}!",@opponent[0].fullname,
@opponent[1].fullname))
when 3
pbDisplayPaused(_INTL("You defeated {1}, {2} and {3}!",@opponent[0].fullname,
@opponent[1].fullname,@opponent[2].fullname))
end
@opponent.each_with_index do |t,i|
@scene.pbShowOpponent(i)
msg = (@endSpeeches[i] && @endSpeeches[i]!="") ? @endSpeeches[i] : "..."
pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/,pbPlayer.name))
end
end
# Gain money from winning a trainer battle, and from Pay Day
pbGainMoney if @decision!=4
# Hide remaining trainer
@scene.pbShowOpponent(@opponent.length) if trainerBattle? && @caughtPokemon.length>0
##### LOSE, DRAW #####
when 2, 5
PBDebug.log("")
PBDebug.log("***Player lost***") if @decision==2
PBDebug.log("***Player drew with opponent***") if @decision==5
if @internalBattle
pbDisplayPaused(_INTL("You have no more Pokémon that can fight!"))
if trainerBattle?
case @opponent.length
when 1
pbDisplayPaused(_INTL("You lost against {1}!",@opponent[0].fullname))
when 2
pbDisplayPaused(_INTL("You lost against {1} and {2}!",
@opponent[0].fullname,@opponent[1].fullname))
when 3
pbDisplayPaused(_INTL("You lost against {1}, {2} and {3}!",
@opponent[0].fullname,@opponent[1].fullname,@opponent[2].fullname))
end
end
# Lose money from losing a battle
pbLoseMoney
pbDisplayPaused(_INTL("You blacked out!")) if !@canLose
elsif @decision==2
if @opponent
@opponent.each_with_index do |t,i|
@scene.pbShowOpponent(i)
msg = (@endSpeechesWin[i] && @endSpeechesWin[i]!="") ? @endSpeechesWin[i] : "..."
pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/,pbPlayer.name))
end
end
end
##### CAUGHT WILD POKÉMON #####
when 4
@scene.pbWildBattleSuccess if !GAIN_EXP_FOR_CAPTURE
end
# Register captured Pokémon in the Pokédex, and store them
pbRecordAndStoreCaughtPokemon
# Collect Pay Day money in a wild battle that ended in a capture
pbGainMoney if @decision==4
# Pass on Pokérus within the party
if @internalBattle
infected = []
$Trainer.party.each_with_index do |pkmn,i|
infected.push(i) if pkmn.pokerusStage==1
end
infected.each do |idxParty|
strain = $Trainer.party[idxParty].pokerusStrain
if idxParty>0 && $Trainer.party[idxParty-1].pokerusStage==0
$Trainer.party[idxParty-1].givePokerus(strain) if rand(3)==0 # 33%
end
if idxParty<$Trainer.party.length-1 && $Trainer.party[idxParty+1].pokerusStage==0
$Trainer.party[idxParty+1].givePokerus(strain) if rand(3)==0 # 33%
end
end
end
# Clean up battle stuff
@scene.pbEndBattle(@decision)
@battlers.each do |b|
next if !b
pbCancelChoice(b.index) # Restore unused items to Bag
BattleHandlers.triggerAbilityOnSwitchOut(b.ability,b,true) if b.abilityActive?
end
pbParty(0).each_with_index do |pkmn,i|
next if !pkmn
@peer.pbOnLeavingBattle(self,pkmn,@usedInBattle[0][i],true) # Reset form
pkmn.setItem(@initialItems[0][i] || 0)
end
return @decision
end
#=============================================================================
# Judging
#=============================================================================
def pbJudgeCheckpoint(user,move=nil); end
def pbDecisionOnTime
counts = [0,0]
hpTotals = [0,0]
for side in 0...2
pbParty(side).each do |pkmn|
next if !pkmn || !pkmn.able?
counts[side] += 1
hpTotals[side] += pkmn.hp
end
end
return 1 if counts[0]>counts[1] # Win (player has more able Pokémon)
return 2 if counts[0]<counts[1] # Loss (foe has more able Pokémon)
return 1 if hpTotals[0]>hpTotals[1] # Win (player has more HP in total)
return 2 if hpTotals[0]<hpTotals[1] # Loss (foe has more HP in total)
return 5 # Draw
end
# Unused
def pbDecisionOnTime2
counts = [0,0]
hpTotals = [0,0]
for side in 0...2
pbParty(side).each do |pkmn|
next if !pkmn || !pkmn.able?
counts[side] += 1
hpTotals[side] += 100*pkmn.hp/pkmn.totalhp
end
hpTotals[side] /= counts[side] if counts[side]>1
end
return 1 if counts[0]>counts[1] # Win (player has more able Pokémon)
return 2 if counts[0]<counts[1] # Loss (foe has more able Pokémon)
return 1 if hpTotals[0]>hpTotals[1] # Win (player has a bigger average HP %)
return 2 if hpTotals[0]<hpTotals[1] # Loss (foe has a bigger average HP %)
return 5 # Draw
end
def pbDecisionOnDraw; return 5; end # Draw
def pbJudge
fainted1 = pbAllFainted?(0)
fainted2 = pbAllFainted?(1)
if fainted1 && fainted2; @decision = pbDecisionOnDraw # Draw
elsif fainted1; @decision = 2 # Loss
elsif fainted2; @decision = 1 # Win
end
end
end

Some files were not shown because too many files have changed in this diff Show More