mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2026-06-21 09:54:12 +00:00
Initial commit
This commit is contained in:
+14
@@ -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
|
||||
Binary file not shown.
@@ -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"]
|
||||
]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 = ""
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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")
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user