mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +00:00
Initial commit
This commit is contained in:
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Audio and Graphics folders
|
||||
Audio/
|
||||
Graphics/
|
||||
|
||||
# Data folder, but not Data/Scripts folder
|
||||
Data/*
|
||||
!Data/Scripts.rxdata
|
||||
!Data/Scripts/
|
||||
|
||||
# Files in the main project folder
|
||||
Game.exe
|
||||
Game.ini
|
||||
Game.rxproj
|
||||
RGSS*.dll
|
||||
BIN
Data/Scripts.rxdata
Normal file
BIN
Data/Scripts.rxdata
Normal file
Binary file not shown.
353
Data/Scripts/001_Settings.rb
Normal file
353
Data/Scripts/001_Settings.rb
Normal file
@@ -0,0 +1,353 @@
|
||||
#==============================================================================#
|
||||
# Pokémon Essentials #
|
||||
# Version 18 #
|
||||
#==============================================================================#
|
||||
|
||||
#===============================================================================
|
||||
# * The default screen width (at a zoom of 1.0; size is half this at zoom 0.5).
|
||||
# * The default screen height (at a zoom of 1.0).
|
||||
# * The default screen zoom. (1.0 means each tile is 32x32 pixels, 0.5 means
|
||||
# each tile is 16x16 pixels, 2.0 means each tile is 64x64 pixels.)
|
||||
# * Whether full-screen display lets the border graphic go outside the edges of
|
||||
# the screen (true), or forces the border graphic to always be fully shown
|
||||
# (false).
|
||||
# * The width of each of the left and right sides of the screen border. This is
|
||||
# added on to the screen width above, only if the border is turned on.
|
||||
# * The height of each of the top and bottom sides of the screen border. This is
|
||||
# added on to the screen height above, only if the border is turned on.
|
||||
# * Map view mode (0=original, 1=custom, 2=perspective).
|
||||
#===============================================================================
|
||||
SCREEN_WIDTH = 512
|
||||
SCREEN_HEIGHT = 384
|
||||
SCREEN_ZOOM = 1.0
|
||||
BORDER_FULLY_SHOWS = false
|
||||
BORDER_WIDTH = 78
|
||||
BORDER_HEIGHT = 78
|
||||
MAP_VIEW_MODE = 1
|
||||
# To forbid the player from changing the screen size themselves, quote out or
|
||||
# delete the relevant bit of code in the PScreen_Options script section.
|
||||
|
||||
#===============================================================================
|
||||
# * The maximum level Pokémon can reach.
|
||||
# * The level of newly hatched Pokémon.
|
||||
# * The odds of a newly generated Pokémon being shiny (out of 65536).
|
||||
# * The odds of a wild Pokémon/bred egg having Pokérus (out of 65536).
|
||||
#===============================================================================
|
||||
MAXIMUM_LEVEL = 100
|
||||
EGG_LEVEL = 1
|
||||
SHINY_POKEMON_CHANCE = 8
|
||||
POKERUS_CHANCE = 3
|
||||
|
||||
#===============================================================================
|
||||
# * Whether outdoor maps should be shaded according to the time of day.
|
||||
#===============================================================================
|
||||
TIME_SHADING = true
|
||||
|
||||
#===============================================================================
|
||||
# * Whether poisoned Pokémon will lose HP while walking around in the field.
|
||||
# * Whether poisoned Pokémon will faint while walking around in the field
|
||||
# (true), or survive the poisoning with 1HP (false).
|
||||
# * Whether fishing automatically hooks the Pokémon (if false, there is a
|
||||
# reaction test first).
|
||||
# * Whether the player can surface from anywhere while diving (true), or only in
|
||||
# spots where they could dive down from above (false).
|
||||
# * Whether planted berries grow according to Gen 4 mechanics (true) or Gen 3
|
||||
# mechanics (false).
|
||||
# * Whether TMs can be used infinitely as in Gen 5 (true), or are one-use-only
|
||||
# as in older Gens (false).
|
||||
#===============================================================================
|
||||
POISON_IN_FIELD = true
|
||||
POISON_FAINT_IN_FIELD = false
|
||||
FISHING_AUTO_HOOK = false
|
||||
DIVING_SURFACE_ANYWHERE = false
|
||||
NEW_BERRY_PLANTS = true
|
||||
INFINITE_TMS = true
|
||||
|
||||
#===============================================================================
|
||||
# * The number of steps allowed before a Safari Zone game is over (0=infinite).
|
||||
# * The number of seconds a Bug Catching Contest lasts for (0=infinite).
|
||||
#===============================================================================
|
||||
SAFARI_STEPS = 600
|
||||
BUG_CONTEST_TIME = 1200
|
||||
|
||||
#===============================================================================
|
||||
# * Pairs of map IDs, where the location signpost isn't shown when moving from
|
||||
# one of the maps in a pair to the other (and vice versa). Useful for
|
||||
# single long routes/towns that are spread over multiple maps.
|
||||
# e.g. [4,5,16,17,42,43] will be map pairs 4,5 and 16,17 and 42,43.
|
||||
# Moving between two maps that have the exact same name won't show the
|
||||
# location signpost anyway, so you don't need to list those maps here.
|
||||
#===============================================================================
|
||||
NO_SIGNPOSTS = []
|
||||
|
||||
#===============================================================================
|
||||
# * The amount of money the player starts the game with.
|
||||
# * The maximum amount of money the player can have.
|
||||
# * The maximum number of Game Corner coins the player can have.
|
||||
# * The maximum length, in characters, that the player's name can be.
|
||||
#===============================================================================
|
||||
INITIAL_MONEY = 3000
|
||||
MAX_MONEY = 999999
|
||||
MAX_COINS = 99999
|
||||
MAX_PLAYER_NAME_SIZE = 10
|
||||
|
||||
#===============================================================================
|
||||
# * A set of arrays each containing a trainer type followed by a Global Variable
|
||||
# number. If the variable isn't set to 0, then all trainers with the
|
||||
# associated trainer type will be named as whatever is in that variable.
|
||||
#===============================================================================
|
||||
RIVAL_NAMES = [
|
||||
[:RIVAL1,12],
|
||||
[:RIVAL2,12],
|
||||
[:CHAMPION,12]
|
||||
]
|
||||
|
||||
#===============================================================================
|
||||
# * The minimum number of badges required to boost each stat of a player's
|
||||
# Pokémon by 1.1x, while using moves in battle only.
|
||||
# * Whether the badge restriction on using certain hidden moves is either owning
|
||||
# at least a certain number of badges (true), or owning a particular badge
|
||||
# (false).
|
||||
# * Depending on FIELD_MOVES_COUNT_BADGES, either the number of badges required
|
||||
# to use each hidden move, or the specific badge number required to use
|
||||
# each move. Remember that badge 0 is the first badge, badge 1 is the
|
||||
# second badge, etc.
|
||||
# e.g. To require the second badge, put false and 1.
|
||||
# To require at least 2 badges, put true and 2.
|
||||
#===============================================================================
|
||||
NUM_BADGES_BOOST_ATTACK = 1
|
||||
NUM_BADGES_BOOST_DEFENSE = 5
|
||||
NUM_BADGES_BOOST_SPATK = 7
|
||||
NUM_BADGES_BOOST_SPDEF = 7
|
||||
NUM_BADGES_BOOST_SPEED = 3
|
||||
FIELD_MOVES_COUNT_BADGES = true
|
||||
BADGE_FOR_CUT = 1
|
||||
BADGE_FOR_FLASH = 2
|
||||
BADGE_FOR_ROCKSMASH = 3
|
||||
BADGE_FOR_SURF = 4
|
||||
BADGE_FOR_FLY = 5
|
||||
BADGE_FOR_STRENGTH = 6
|
||||
BADGE_FOR_DIVE = 7
|
||||
BADGE_FOR_WATERFALL = 8
|
||||
|
||||
#===============================================================================
|
||||
# * Whether a move's physical/special category depends on the move itself as in
|
||||
# newer Gens (true), or on its type as in older Gens (false).
|
||||
# * Whether the battle mechanics mimic Gen 5 (false) or Gen 7 (true).
|
||||
# * Whether the Exp gained from beating a Pokémon should be scaled depending on
|
||||
# the gainer's level as in Gens 5/7 (true) or not as in other Gens (false).
|
||||
# * Whether the Exp gained from beating a Pokémon should be divided equally
|
||||
# between each participant (true), or whether each participant should gain
|
||||
# that much Exp (false). This also applies to Exp gained via the Exp Share
|
||||
# (held item version) being distributed to all Exp Share holders. This is
|
||||
# true in Gen 6 and false otherwise.
|
||||
# * Whether the critical capture mechanic applies (true) or not (false). Note
|
||||
# that it is based on a total of 600+ species (i.e. that many species need
|
||||
# to be caught to provide the greatest critical capture chance of 2.5x),
|
||||
# and there may be fewer species in your game.
|
||||
# * Whether Pokémon gain Exp for capturing a Pokémon (true) or not (false).
|
||||
# * An array of items which act as Mega Rings for the player (NPCs don't need a
|
||||
# Mega Ring item, just a Mega Stone held by their Pokémon).
|
||||
#===============================================================================
|
||||
MOVE_CATEGORY_PER_MOVE = true
|
||||
NEWEST_BATTLE_MECHANICS = true
|
||||
SCALED_EXP_FORMULA = true
|
||||
SPLIT_EXP_BETWEEN_GAINERS = false
|
||||
ENABLE_CRITICAL_CAPTURES = false
|
||||
GAIN_EXP_FOR_CAPTURE = true
|
||||
MEGA_RINGS = [:MEGARING,:MEGABRACELET,:MEGACUFF,:MEGACHARM]
|
||||
|
||||
#===============================================================================
|
||||
# * The names of each pocket of the Bag. Leave the first entry blank.
|
||||
# * The maximum number of slots per pocket (-1 means infinite number). Ignore
|
||||
# the first number (0).
|
||||
# * The maximum number of items each slot in the Bag can hold.
|
||||
# * Whether each pocket in turn auto-sorts itself by item ID number. Ignore the
|
||||
# first entry (the 0).
|
||||
#===============================================================================
|
||||
def pbPocketNames; return ["",
|
||||
_INTL("Items"),
|
||||
_INTL("Medicine"),
|
||||
_INTL("Poké Balls"),
|
||||
_INTL("TMs & HMs"),
|
||||
_INTL("Berries"),
|
||||
_INTL("Mail"),
|
||||
_INTL("Battle Items"),
|
||||
_INTL("Key Items")
|
||||
]; end
|
||||
BAG_MAX_POCKET_SIZE = [0,-1,-1,-1,-1,-1,-1,-1,-1]
|
||||
BAG_MAX_PER_SLOT = 999
|
||||
BAG_POCKET_AUTO_SORT = [0,false,false,false,true,true,false,false,false]
|
||||
|
||||
#===============================================================================
|
||||
# * A set of arrays each containing details of a graphic to be shown on the
|
||||
# region map if appropriate. The values for each array are as follows:
|
||||
# - Region number.
|
||||
# - Global Switch; the graphic is shown if this is ON (non-wall maps only).
|
||||
# - X coordinate of the graphic on the map, in squares.
|
||||
# - Y coordinate of the graphic on the map, in squares.
|
||||
# - Name of the graphic, found in the Graphics/Pictures folder.
|
||||
# - The graphic will always (true) or never (false) be shown on a wall map.
|
||||
#===============================================================================
|
||||
REGION_MAP_EXTRAS = [
|
||||
[0,51,16,15,"mapHiddenBerth",false],
|
||||
[0,52,20,14,"mapHiddenFaraday",false]
|
||||
]
|
||||
|
||||
#===============================================================================
|
||||
# * The name of the person who created the Pokémon storage system.
|
||||
# * The number of boxes in Pokémon storage.
|
||||
#===============================================================================
|
||||
def pbStorageCreator
|
||||
return _INTL("Bill")
|
||||
end
|
||||
NUM_STORAGE_BOXES = 30
|
||||
|
||||
#===============================================================================
|
||||
# * Whether the Pokédex list shown is the one for the player's current region
|
||||
# (true), or whether a menu pops up for the player to manually choose which
|
||||
# Dex list to view if more than one is available (false).
|
||||
# * The names of each Dex list in the game, in order and with National Dex at
|
||||
# the end. This is also the order that $PokemonGlobal.pokedexUnlocked is
|
||||
# in, which records which Dexes have been unlocked (first is unlocked by
|
||||
# default).
|
||||
# You can define which region a particular Dex list is linked to. This
|
||||
# means the area map shown while viewing that Dex list will ALWAYS be that
|
||||
# of the defined region, rather than whichever region the player is
|
||||
# currently in. To define this, put the Dex name and the region number in
|
||||
# an array, like the Kanto and Johto Dexes are. The National Dex isn't in
|
||||
# an array with a region number, therefore its area map is whichever region
|
||||
# the player is currently in.
|
||||
# * Whether all forms of a given species will be immediately available to view
|
||||
# in the Pokédex so long as that species has been seen at all (true), or
|
||||
# whether each form needs to be seen specifically before that form appears
|
||||
# in the Pokédex (false).
|
||||
# * An array of numbers, where each number is that of a Dex list (National Dex
|
||||
# is -1). All Dex lists included here have the species numbers in them
|
||||
# reduced by 1, thus making the first listed species have a species number
|
||||
# of 0 (e.g. Victini in Unova's Dex).
|
||||
#===============================================================================
|
||||
USE_CURRENT_REGION_DEX = false
|
||||
def pbDexNames; return [
|
||||
[_INTL("Kanto Pokédex"),0],
|
||||
[_INTL("Johto Pokédex"),1],
|
||||
_INTL("National Pokédex")
|
||||
]; end
|
||||
DEX_SHOWS_ALL_FORMS = false
|
||||
DEXES_WITH_OFFSETS = []
|
||||
|
||||
#===============================================================================
|
||||
# * A list of maps used by roaming Pokémon. Each map has an array of other maps
|
||||
# it can lead to.
|
||||
# * A set of arrays each containing the details of a roaming Pokémon. The
|
||||
# information within is as follows:
|
||||
# - Species.
|
||||
# - Level.
|
||||
# - Global Switch; the Pokémon roams while this is ON.
|
||||
# - Encounter type (0=any, 1=grass/walking in cave, 2=surfing, 3=fishing,
|
||||
# 4=surfing/fishing). See bottom of PField_RoamingPokemon for lists.
|
||||
# - Name of BGM to play for that encounter (optional).
|
||||
# - Roaming areas specifically for this Pokémon (optional).
|
||||
#===============================================================================
|
||||
RoamingAreas = {
|
||||
5 => [21,28,31,39,41,44,47,66,69],
|
||||
21 => [5,28,31,39,41,44,47,66,69],
|
||||
28 => [5,21,31,39,41,44,47,66,69],
|
||||
31 => [5,21,28,39,41,44,47,66,69],
|
||||
39 => [5,21,28,31,41,44,47,66,69],
|
||||
41 => [5,21,28,31,39,44,47,66,69],
|
||||
44 => [5,21,28,31,39,41,47,66,69],
|
||||
47 => [5,21,28,31,39,41,44,66,69],
|
||||
66 => [5,21,28,31,39,41,44,47,69],
|
||||
69 => [5,21,28,31,39,41,44,47,66]
|
||||
}
|
||||
RoamingSpecies = [
|
||||
[:LATIAS, 30, 53, 0, "Battle roaming"],
|
||||
[:LATIOS, 30, 53, 0, "Battle roaming"],
|
||||
[:KYOGRE, 40, 54, 2, nil, {
|
||||
2 => [21,31],
|
||||
21 => [2,31,69],
|
||||
31 => [2,21,69],
|
||||
69 => [21,31]
|
||||
}],
|
||||
[:ENTEI, 40, 55, 1, nil]
|
||||
]
|
||||
|
||||
#===============================================================================
|
||||
# * A set of arrays each containing details of a wild encounter that can only
|
||||
# occur via using the Poké Radar. The information within is as follows:
|
||||
# - Map ID on which this encounter can occur.
|
||||
# - Probability that this encounter will occur (as a percentage).
|
||||
# - Species.
|
||||
# - Minimum possible level.
|
||||
# - Maximum possible level (optional).
|
||||
#===============================================================================
|
||||
POKE_RADAR_ENCOUNTERS = [
|
||||
[5, 20, :STARLY, 12, 15],
|
||||
[21, 10, :STANTLER, 14],
|
||||
[28, 20, :BUTTERFREE, 15, 18],
|
||||
[28, 20, :BEEDRILL, 15, 18]
|
||||
]
|
||||
|
||||
#===============================================================================
|
||||
# * The Global Switch that is set to ON when the player whites out.
|
||||
# * The Global Switch that is set to ON when the player has seen Pokérus in the
|
||||
# Poké Center, and doesn't need to be told about it again.
|
||||
# * The Global Switch which, while ON, makes all wild Pokémon created be
|
||||
# shiny.
|
||||
# * The Global Switch which, while ON, makes all Pokémon created considered to
|
||||
# be met via a fateful encounter.
|
||||
# * The Global Switch which determines whether the player will lose money if
|
||||
# they lose a battle (they can still gain money from trainers for winning).
|
||||
# * The Global Switch which, while ON, prevents all Pokémon in battle from Mega
|
||||
# Evolving even if they otherwise could.
|
||||
#===============================================================================
|
||||
STARTING_OVER_SWITCH = 1
|
||||
SEEN_POKERUS_SWITCH = 2
|
||||
SHINY_WILD_POKEMON_SWITCH = 31
|
||||
FATEFUL_ENCOUNTER_SWITCH = 32
|
||||
NO_MONEY_LOSS = 33
|
||||
NO_MEGA_EVOLUTION = 34
|
||||
|
||||
#===============================================================================
|
||||
# * The ID of the common event that runs when the player starts fishing (runs
|
||||
# instead of showing the casting animation).
|
||||
# * The ID of the common event that runs when the player stops fishing (runs
|
||||
# instead of showing the reeling in animation).
|
||||
#===============================================================================
|
||||
FISHING_BEGIN_COMMON_EVENT = -1
|
||||
FISHING_END_COMMON_EVENT = -1
|
||||
|
||||
#===============================================================================
|
||||
# * The ID of the animation played when the player steps on grass (shows grass
|
||||
# rustling).
|
||||
# * The ID of the animation played when the player lands on the ground after
|
||||
# hopping over a ledge (shows a dust impact).
|
||||
# * The ID of the animation played when a trainer notices the player (an
|
||||
# exclamation bubble).
|
||||
# * The ID of the animation played when a patch of grass rustles due to using
|
||||
# the Poké Radar.
|
||||
# * The ID of the animation played when a patch of grass rustles vigorously due
|
||||
# to using the Poké Radar. (Rarer species)
|
||||
# * The ID of the animation played when a patch of grass rustles and shines due
|
||||
# to using the Poké Radar. (Shiny encounter)
|
||||
# * The ID of the animation played when a berry tree grows a stage while the
|
||||
# player is on the map (for new plant growth mechanics only).
|
||||
#===============================================================================
|
||||
GRASS_ANIMATION_ID = 1
|
||||
DUST_ANIMATION_ID = 2
|
||||
EXCLAMATION_ANIMATION_ID = 3
|
||||
RUSTLE_NORMAL_ANIMATION_ID = 1
|
||||
RUSTLE_VIGOROUS_ANIMATION_ID = 5
|
||||
RUSTLE_SHINY_ANIMATION_ID = 6
|
||||
PLANT_SPARKLE_ANIMATION_ID = 7
|
||||
|
||||
#===============================================================================
|
||||
# * An array of available languages in the game, and their corresponding
|
||||
# message file in the Data folder. Edit only if you have 2 or more
|
||||
# languages to choose from.
|
||||
#===============================================================================
|
||||
LANGUAGES = [
|
||||
# ["English","english.dat"],
|
||||
# ["Deutsch","deutsch.dat"]
|
||||
]
|
||||
211
Data/Scripts/001_Technical/001_Ruby Utilities.rb
Normal file
211
Data/Scripts/001_Technical/001_Ruby Utilities.rb
Normal file
@@ -0,0 +1,211 @@
|
||||
#===============================================================================
|
||||
# class Class
|
||||
#===============================================================================
|
||||
class Class
|
||||
def to_sym
|
||||
return self.to_s.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# module Comparable
|
||||
#===============================================================================
|
||||
unless Comparable.method_defined? :clamp
|
||||
module Comparable
|
||||
def clamp(min, max)
|
||||
if max-min<0
|
||||
raise ArgumentError("min argument must be smaller than max argument")
|
||||
end
|
||||
return (self>max) ? max : (self<min) ? min : self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# class Boolean
|
||||
#===============================================================================
|
||||
class Boolean
|
||||
def to_i
|
||||
return self ? 1 : 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# class String
|
||||
#===============================================================================
|
||||
class String
|
||||
def starts_with?(str)
|
||||
proc = (self[0...str.length] == str) if self.length >= str.length
|
||||
return proc ? proc : false
|
||||
end
|
||||
|
||||
def ends_with?(str)
|
||||
e = self.length - 1
|
||||
proc = (self[(e-str.length)...e] == str) if self.length >= str.length
|
||||
return proc ? proc : false
|
||||
end
|
||||
|
||||
def starts_with_vowel?
|
||||
return ['a','e','i','o','u'].include?(self[0,1].downcase)
|
||||
end
|
||||
|
||||
def first(n=1)
|
||||
return self[0...n]
|
||||
end
|
||||
|
||||
def last(n=1)
|
||||
return self[-n..-1] || self
|
||||
end
|
||||
|
||||
def bytesize
|
||||
return self.size
|
||||
end
|
||||
|
||||
def capitalize
|
||||
proc = self.scan(/./)
|
||||
proc[0] = proc[0].upcase
|
||||
string = ""
|
||||
for letter in proc
|
||||
string += letter
|
||||
end
|
||||
return string
|
||||
end
|
||||
|
||||
def capitalize!
|
||||
self.replace(self.capitalize)
|
||||
end
|
||||
|
||||
def blank?
|
||||
blank = true
|
||||
s = self.scan(/./)
|
||||
for l in s
|
||||
blank = false if l != ""
|
||||
end
|
||||
return blank
|
||||
end
|
||||
|
||||
def cut(bitmap,width)
|
||||
string = self
|
||||
width -= bitmap.text_size("...").width
|
||||
string_width = 0
|
||||
text = []
|
||||
for char in string.scan(/./)
|
||||
wdh = bitmap.text_size(char).width
|
||||
next if (wdh+string_width) > width
|
||||
string_width += wdh
|
||||
text.push(char)
|
||||
end
|
||||
text.push("...") if text.length < string.length
|
||||
new_string = ""
|
||||
for char in text
|
||||
new_string += char
|
||||
end
|
||||
return new_string
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# class Numeric
|
||||
#===============================================================================
|
||||
class Numeric
|
||||
# Turns a number into a string formatted like 12,345,678.
|
||||
def to_s_formatted
|
||||
return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# class Integer
|
||||
#===============================================================================
|
||||
class Integer
|
||||
# Returns an array containing each digit of the number in turn.
|
||||
def digits(base=10)
|
||||
quotient, remainder = divmod(base)
|
||||
(quotient==0) ? [remainder] : quotient.digits(base).push(remainder)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# class Array
|
||||
#===============================================================================
|
||||
class Array
|
||||
def first
|
||||
return self[0]
|
||||
end
|
||||
|
||||
def last
|
||||
return self[self.length-1]
|
||||
end
|
||||
|
||||
def ^(other) # xor of two arrays
|
||||
return (self|other)-(self&other)
|
||||
end
|
||||
|
||||
def shuffle
|
||||
dup.shuffle!
|
||||
end unless method_defined? :shuffle
|
||||
|
||||
def shuffle!
|
||||
(size-1).times do |i|
|
||||
r = i+rand(size-i)
|
||||
self[i], self[r] = self[r], self[i]
|
||||
end
|
||||
self
|
||||
end unless method_defined? :shuffle!
|
||||
end
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# module Enumerable
|
||||
#===============================================================================
|
||||
module Enumerable
|
||||
def transform
|
||||
ret = []
|
||||
self.each { |item| ret.push(yield(item)) }
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Kernel methods
|
||||
#===============================================================================
|
||||
def rand(*args)
|
||||
Kernel.rand(*args)
|
||||
end
|
||||
|
||||
class << Kernel
|
||||
alias oldRand rand unless method_defined?(:oldRand)
|
||||
def rand(a = nil, b = nil)
|
||||
if a.is_a?(Range)
|
||||
lo = a.min
|
||||
hi = a.max
|
||||
return lo + oldRand(hi - lo + 1)
|
||||
elsif a.is_a?(Numeric)
|
||||
if b.is_a?(Numeric)
|
||||
return a + oldRand(b - a + 1)
|
||||
else
|
||||
return oldRand(a)
|
||||
end
|
||||
elsif a.nil?
|
||||
return (b) ? oldRand(b) : oldRand(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def nil_or_empty?(string)
|
||||
return string.nil? || !string.is_a?(String) || string.size == 0
|
||||
end
|
||||
608
Data/Scripts/001_Technical/002_RGSS2Compatibility.rb
Normal file
608
Data/Scripts/001_Technical/002_RGSS2Compatibility.rb
Normal file
@@ -0,0 +1,608 @@
|
||||
$TEST = true if $DEBUG
|
||||
$DEBUG = true if $TEST
|
||||
$scene = nil
|
||||
Font.default_shadow = false if Font.respond_to?(:default_shadow)
|
||||
Graphics.frame_rate = 40
|
||||
|
||||
|
||||
|
||||
=begin
|
||||
class Win32API
|
||||
class << self
|
||||
unless defined?(debug_new)
|
||||
alias debug_new new
|
||||
end
|
||||
|
||||
def new(*args)
|
||||
File.open("winapi.txt","ab") { |f| f.write("new(#{args[0]},#{args[1]})\r\n") }
|
||||
b=debug_new(*args)
|
||||
b.setDllName(args[0],args[1])
|
||||
return b
|
||||
end
|
||||
end
|
||||
|
||||
unless defined?(debug_call)
|
||||
alias debug_call call
|
||||
end
|
||||
|
||||
def setDllName(a,b)
|
||||
@w32dll=a
|
||||
@w32name=b
|
||||
end
|
||||
|
||||
def call(*args)
|
||||
if @w32name!="GetAsyncKeyState"
|
||||
File.open("winapi.txt","ab") { |f|
|
||||
f.write("call(#{@w32dll},#{@w32name},#{args.inspect})\r\n")
|
||||
}
|
||||
end
|
||||
debug_call(*args)
|
||||
end
|
||||
end
|
||||
|
||||
class Bitmap
|
||||
class << self
|
||||
unless defined?(debug_new)
|
||||
alias debug_new new
|
||||
end
|
||||
|
||||
def new(*args)
|
||||
if args.length==1
|
||||
File.open("winapib.txt","ab") { |f| f.write("new(#{args[0]})\r\n") }
|
||||
end
|
||||
debug_new(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
alias debug_load_data load_data
|
||||
|
||||
def load_data(*args)
|
||||
File.open("winapif.txt","ab") { |f| f.write("load(#{args[0]})\r\n") }
|
||||
debug_load_data(*args)
|
||||
end
|
||||
=end
|
||||
|
||||
|
||||
|
||||
class Hangup < Exception; end
|
||||
|
||||
|
||||
|
||||
if false
|
||||
p (Tilemap.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
|
||||
# no changes
|
||||
p (Plane.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
|
||||
# no changes
|
||||
p (Viewport.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
|
||||
p (Bitmap.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
|
||||
# openness(=)
|
||||
p (Window.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
|
||||
p (Sprite.instance_methods-Kernel.instance_methods-Object.instance_methods).sort
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Animation
|
||||
attr_accessor :id
|
||||
attr_accessor :name
|
||||
attr_accessor :animation_name
|
||||
attr_accessor :animation_hue
|
||||
attr_accessor :position
|
||||
attr_accessor :frame_max
|
||||
attr_accessor :frames
|
||||
attr_accessor :timings
|
||||
|
||||
def initialize
|
||||
@id = 0
|
||||
@name = ""
|
||||
@animation_name = ""
|
||||
@animation_hue = 0
|
||||
@position = 1
|
||||
@frame_max = 1
|
||||
@frames = [RPG::Animation::Frame.new]
|
||||
@timings = []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Animation
|
||||
class Frame
|
||||
attr_accessor :cell_max
|
||||
attr_accessor :cell_data
|
||||
|
||||
def initialize
|
||||
@cell_max = 0
|
||||
@cell_data = Table.new(0, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Animation
|
||||
class Timing
|
||||
attr_accessor :frame
|
||||
attr_accessor :se
|
||||
attr_accessor :flash_scope
|
||||
attr_accessor :flash_color
|
||||
attr_accessor :flash_duration
|
||||
attr_accessor :condition
|
||||
|
||||
def initialize
|
||||
@frame = 0
|
||||
@se = RPG::AudioFile.new("", 80)
|
||||
@flash_scope = 0
|
||||
@flash_color = Color.new(255,255,255,255)
|
||||
@flash_duration = 5
|
||||
@condition = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class System
|
||||
attr_accessor :magic_number
|
||||
attr_accessor :party_members
|
||||
attr_accessor :elements
|
||||
attr_accessor :switches
|
||||
attr_accessor :variables
|
||||
attr_accessor :windowskin_name
|
||||
attr_accessor :title_name
|
||||
attr_accessor :gameover_name
|
||||
attr_accessor :battle_transition
|
||||
attr_accessor :title_bgm
|
||||
attr_accessor :battle_bgm
|
||||
attr_accessor :battle_end_me
|
||||
attr_accessor :gameover_me
|
||||
attr_accessor :cursor_se
|
||||
attr_accessor :decision_se
|
||||
attr_accessor :cancel_se
|
||||
attr_accessor :buzzer_se
|
||||
attr_accessor :equip_se
|
||||
attr_accessor :shop_se
|
||||
attr_accessor :save_se
|
||||
attr_accessor :load_se
|
||||
attr_accessor :battle_start_se
|
||||
attr_accessor :escape_se
|
||||
attr_accessor :actor_collapse_se
|
||||
attr_accessor :enemy_collapse_se
|
||||
attr_accessor :words
|
||||
attr_accessor :test_battlers
|
||||
attr_accessor :test_troop_id
|
||||
attr_accessor :start_map_id
|
||||
attr_accessor :start_x
|
||||
attr_accessor :start_y
|
||||
attr_accessor :battleback_name
|
||||
attr_accessor :battler_name
|
||||
attr_accessor :battler_hue
|
||||
attr_accessor :edit_map_id
|
||||
|
||||
def initialize
|
||||
@magic_number = 0
|
||||
@party_members = [1]
|
||||
@elements = [nil, ""]
|
||||
@switches = [nil, ""]
|
||||
@variables = [nil, ""]
|
||||
@windowskin_name = ""
|
||||
@title_name = ""
|
||||
@gameover_name = ""
|
||||
@battle_transition = ""
|
||||
@title_bgm = RPG::AudioFile.new
|
||||
@battle_bgm = RPG::AudioFile.new
|
||||
@battle_end_me = RPG::AudioFile.new
|
||||
@gameover_me = RPG::AudioFile.new
|
||||
@cursor_se = RPG::AudioFile.new("", 80)
|
||||
@decision_se = RPG::AudioFile.new("", 80)
|
||||
@cancel_se = RPG::AudioFile.new("", 80)
|
||||
@buzzer_se = RPG::AudioFile.new("", 80)
|
||||
@equip_se = RPG::AudioFile.new("", 80)
|
||||
@shop_se = RPG::AudioFile.new("", 80)
|
||||
@save_se = RPG::AudioFile.new("", 80)
|
||||
@load_se = RPG::AudioFile.new("", 80)
|
||||
@battle_start_se = RPG::AudioFile.new("", 80)
|
||||
@escape_se = RPG::AudioFile.new("", 80)
|
||||
@actor_collapse_se = RPG::AudioFile.new("", 80)
|
||||
@enemy_collapse_se = RPG::AudioFile.new("", 80)
|
||||
@words = RPG::System::Words.new
|
||||
@test_battlers = []
|
||||
@test_troop_id = 1
|
||||
@start_map_id = 1
|
||||
@start_x = 0
|
||||
@start_y = 0
|
||||
@battleback_name = ""
|
||||
@battler_name = ""
|
||||
@battler_hue = 0
|
||||
@edit_map_id = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Tileset
|
||||
attr_accessor :id
|
||||
attr_accessor :name
|
||||
attr_accessor :tileset_name
|
||||
attr_accessor :autotile_names
|
||||
attr_accessor :panorama_name
|
||||
attr_accessor :panorama_hue
|
||||
attr_accessor :fog_name
|
||||
attr_accessor :fog_hue
|
||||
attr_accessor :fog_opacity
|
||||
attr_accessor :fog_blend_type
|
||||
attr_accessor :fog_zoom
|
||||
attr_accessor :fog_sx
|
||||
attr_accessor :fog_sy
|
||||
attr_accessor :battleback_name
|
||||
attr_accessor :passages
|
||||
attr_accessor :priorities
|
||||
attr_accessor :terrain_tags
|
||||
|
||||
def initialize
|
||||
@id = 0
|
||||
@name = ""
|
||||
@tileset_name = ""
|
||||
@autotile_names = [""]*7
|
||||
@panorama_name = ""
|
||||
@panorama_hue = 0
|
||||
@fog_name = ""
|
||||
@fog_hue = 0
|
||||
@fog_opacity = 64
|
||||
@fog_blend_type = 0
|
||||
@fog_zoom = 200
|
||||
@fog_sx = 0
|
||||
@fog_sy = 0
|
||||
@battleback_name = ""
|
||||
@passages = Table.new(384)
|
||||
@priorities = Table.new(384)
|
||||
@priorities[0] = 5
|
||||
@terrain_tags = Table.new(384)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class CommonEvent
|
||||
attr_accessor :id
|
||||
attr_accessor :name
|
||||
attr_accessor :trigger
|
||||
attr_accessor :switch_id
|
||||
attr_accessor :list
|
||||
|
||||
def initialize
|
||||
@id = 0
|
||||
@name = ""
|
||||
@trigger = 0
|
||||
@switch_id = 1
|
||||
@list = [RPG::EventCommand.new]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Map
|
||||
attr_accessor :tileset_id
|
||||
attr_accessor :width
|
||||
attr_accessor :height
|
||||
attr_accessor :autoplay_bgm
|
||||
attr_accessor :bgm
|
||||
attr_accessor :autoplay_bgs
|
||||
attr_accessor :bgs
|
||||
attr_accessor :encounter_list
|
||||
attr_accessor :encounter_step
|
||||
attr_accessor :data
|
||||
attr_accessor :events
|
||||
|
||||
def initialize(width, height)
|
||||
@tileset_id = 1
|
||||
@width = width
|
||||
@height = height
|
||||
@autoplay_bgm = false
|
||||
@bgm = RPG::AudioFile.new
|
||||
@autoplay_bgs = false
|
||||
@bgs = RPG::AudioFile.new("", 80)
|
||||
@encounter_list = []
|
||||
@encounter_step = 30
|
||||
@data = Table.new(width, height, 3)
|
||||
@events = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class MapInfo
|
||||
attr_accessor :name
|
||||
attr_accessor :parent_id
|
||||
attr_accessor :order
|
||||
attr_accessor :expanded
|
||||
attr_accessor :scroll_x
|
||||
attr_accessor :scroll_y
|
||||
|
||||
def initialize
|
||||
@name = ""
|
||||
@parent_id = 0
|
||||
@order = 0
|
||||
@expanded = false
|
||||
@scroll_x = 0
|
||||
@scroll_y = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Event
|
||||
attr_accessor :id
|
||||
attr_accessor :name
|
||||
attr_accessor :x
|
||||
attr_accessor :y
|
||||
attr_accessor :pages
|
||||
|
||||
def initialize(x, y)
|
||||
@id = 0
|
||||
@name = ""
|
||||
@x = x
|
||||
@y = y
|
||||
@pages = [RPG::Event::Page.new]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Event
|
||||
class Page
|
||||
attr_accessor :condition
|
||||
attr_accessor :graphic
|
||||
attr_accessor :move_type
|
||||
attr_accessor :move_speed
|
||||
attr_accessor :move_frequency
|
||||
attr_accessor :move_route
|
||||
attr_accessor :walk_anime
|
||||
attr_accessor :step_anime
|
||||
attr_accessor :direction_fix
|
||||
attr_accessor :through
|
||||
attr_accessor :always_on_top
|
||||
attr_accessor :trigger
|
||||
attr_accessor :list
|
||||
|
||||
def initialize
|
||||
@condition = RPG::Event::Page::Condition.new
|
||||
@graphic = RPG::Event::Page::Graphic.new
|
||||
@move_type = 0
|
||||
@move_speed = 3
|
||||
@move_frequency = 3
|
||||
@move_route = RPG::MoveRoute.new
|
||||
@walk_anime = true
|
||||
@step_anime = false
|
||||
@direction_fix = false
|
||||
@through = false
|
||||
@always_on_top = false
|
||||
@trigger = 0
|
||||
@list = [RPG::EventCommand.new]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Event
|
||||
class Page
|
||||
class Condition
|
||||
attr_accessor :switch1_valid
|
||||
attr_accessor :switch2_valid
|
||||
attr_accessor :variable_valid
|
||||
attr_accessor :self_switch_valid
|
||||
attr_accessor :switch1_id
|
||||
attr_accessor :switch2_id
|
||||
attr_accessor :variable_id
|
||||
attr_accessor :variable_value
|
||||
attr_accessor :self_switch_ch
|
||||
|
||||
def initialize
|
||||
@switch1_valid = false
|
||||
@switch2_valid = false
|
||||
@variable_valid = false
|
||||
@self_switch_valid = false
|
||||
@switch1_id = 1
|
||||
@switch2_id = 1
|
||||
@variable_id = 1
|
||||
@variable_value = 0
|
||||
@self_switch_ch = "A"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Event
|
||||
class Page
|
||||
class Graphic
|
||||
attr_accessor :tile_id
|
||||
attr_accessor :character_name
|
||||
attr_accessor :character_hue
|
||||
attr_accessor :direction
|
||||
attr_accessor :pattern
|
||||
attr_accessor :opacity
|
||||
attr_accessor :blend_type
|
||||
|
||||
def initialize
|
||||
@tile_id = 0
|
||||
@character_name = ""
|
||||
@character_hue = 0
|
||||
@direction = 2
|
||||
@pattern = 0
|
||||
@opacity = 255
|
||||
@blend_type = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class EventCommand
|
||||
attr_accessor :code
|
||||
attr_accessor :indent
|
||||
attr_accessor :parameters
|
||||
|
||||
def initialize(code = 0, indent = 0, parameters = [])
|
||||
@code = code
|
||||
@indent = indent
|
||||
@parameters = parameters
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class MoveRoute
|
||||
attr_accessor :repeat
|
||||
attr_accessor :skippable
|
||||
attr_accessor :list
|
||||
|
||||
def initialize
|
||||
@repeat = true
|
||||
@skippable = false
|
||||
@list = [RPG::MoveCommand.new]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class MoveCommand
|
||||
attr_accessor :code
|
||||
attr_accessor :parameters
|
||||
|
||||
def initialize(code = 0, parameters = [])
|
||||
@code = code
|
||||
@parameters = parameters
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class System
|
||||
class Words
|
||||
attr_accessor :gold
|
||||
attr_accessor :hp
|
||||
attr_accessor :sp
|
||||
attr_accessor :str
|
||||
attr_accessor :dex
|
||||
attr_accessor :agi
|
||||
attr_accessor :int
|
||||
attr_accessor :atk
|
||||
attr_accessor :pdef
|
||||
attr_accessor :mdef
|
||||
attr_accessor :weapon
|
||||
attr_accessor :armor1
|
||||
attr_accessor :armor2
|
||||
attr_accessor :armor3
|
||||
attr_accessor :armor4
|
||||
attr_accessor :attack
|
||||
attr_accessor :skill
|
||||
attr_accessor :guard
|
||||
attr_accessor :item
|
||||
attr_accessor :equip
|
||||
|
||||
def initialize
|
||||
@gold = ""
|
||||
@hp = ""
|
||||
@sp = ""
|
||||
@str = ""
|
||||
@dex = ""
|
||||
@agi = ""
|
||||
@int = ""
|
||||
@atk = ""
|
||||
@pdef = ""
|
||||
@mdef = ""
|
||||
@weapon = ""
|
||||
@armor1 = ""
|
||||
@armor2 = ""
|
||||
@armor3 = ""
|
||||
@armor4 = ""
|
||||
@attack = ""
|
||||
@skill = ""
|
||||
@guard = ""
|
||||
@item = ""
|
||||
@equip = ""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class System
|
||||
class TestBattler
|
||||
attr_accessor :actor_id
|
||||
attr_accessor :level
|
||||
attr_accessor :weapon_id
|
||||
attr_accessor :armor1_id
|
||||
attr_accessor :armor2_id
|
||||
attr_accessor :armor3_id
|
||||
attr_accessor :armor4_id
|
||||
|
||||
def initialize
|
||||
@actor_id = 1
|
||||
@level = 1
|
||||
@weapon_id = 0
|
||||
@armor1_id = 0
|
||||
@armor2_id = 0
|
||||
@armor3_id = 0
|
||||
@armor4_id = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class AudioFile
|
||||
attr_accessor :name
|
||||
attr_accessor :volume
|
||||
attr_accessor :pitch
|
||||
|
||||
def initialize(name = "", volume = 100, pitch = 100)
|
||||
@name = name
|
||||
@volume = volume
|
||||
@pitch = pitch
|
||||
end
|
||||
|
||||
# def play
|
||||
# end
|
||||
end
|
||||
end
|
||||
534
Data/Scripts/001_Technical/003_RPG__Sprite.rb
Normal file
534
Data/Scripts/001_Technical/003_RPG__Sprite.rb
Normal file
@@ -0,0 +1,534 @@
|
||||
class SpriteAnimation
|
||||
@@_animations = []
|
||||
@@_reference_count = {}
|
||||
|
||||
def initialize(sprite)
|
||||
@sprite = sprite
|
||||
end
|
||||
|
||||
%w[
|
||||
x y ox oy viewport flash src_rect opacity tone
|
||||
].each_with_index do |s, i|
|
||||
eval <<-__END__
|
||||
|
||||
def #{s}(*arg)
|
||||
@sprite.#{s}(*arg)
|
||||
end
|
||||
|
||||
__END__
|
||||
end
|
||||
|
||||
def self.clear
|
||||
@@_animations.clear
|
||||
end
|
||||
|
||||
def dispose
|
||||
dispose_animation
|
||||
dispose_loop_animation
|
||||
end
|
||||
|
||||
def animation(animation, hit, height = 3)
|
||||
dispose_animation
|
||||
@_animation = animation
|
||||
return if @_animation == nil
|
||||
@_animation_hit = hit
|
||||
@_animation_height = height
|
||||
@_animation_duration = @_animation.frame_max
|
||||
fr = 20
|
||||
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
|
||||
fr = $~[1].to_i
|
||||
end
|
||||
@_animation_frame_skip = Graphics.frame_rate / fr
|
||||
animation_name = @_animation.animation_name
|
||||
animation_hue = @_animation.animation_hue
|
||||
bitmap = pbGetAnimation(animation_name, animation_hue)
|
||||
if @@_reference_count.include?(bitmap)
|
||||
@@_reference_count[bitmap] += 1
|
||||
else
|
||||
@@_reference_count[bitmap] = 1
|
||||
end
|
||||
@_animation_sprites = []
|
||||
if @_animation.position != 3 or not @@_animations.include?(animation)
|
||||
for i in 0..15
|
||||
sprite = ::Sprite.new(self.viewport)
|
||||
sprite.bitmap = bitmap
|
||||
sprite.visible = false
|
||||
@_animation_sprites.push(sprite)
|
||||
end
|
||||
unless @@_animations.include?(animation)
|
||||
@@_animations.push(animation)
|
||||
end
|
||||
end
|
||||
update_animation
|
||||
end
|
||||
|
||||
def loop_animation(animation)
|
||||
return if animation == @_loop_animation
|
||||
dispose_loop_animation
|
||||
@_loop_animation = animation
|
||||
return if @_loop_animation == nil
|
||||
@_loop_animation_index = 0
|
||||
fr = 20
|
||||
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
|
||||
fr = $~[1].to_i
|
||||
end
|
||||
@_loop_animation_frame_skip = Graphics.frame_rate / fr
|
||||
animation_name = @_loop_animation.animation_name
|
||||
animation_hue = @_loop_animation.animation_hue
|
||||
bitmap = pbGetAnimation(animation_name, animation_hue)
|
||||
if @@_reference_count.include?(bitmap)
|
||||
@@_reference_count[bitmap] += 1
|
||||
else
|
||||
@@_reference_count[bitmap] = 1
|
||||
end
|
||||
@_loop_animation_sprites = []
|
||||
for i in 0..15
|
||||
sprite = ::Sprite.new(self.viewport)
|
||||
sprite.bitmap = bitmap
|
||||
sprite.visible = false
|
||||
@_loop_animation_sprites.push(sprite)
|
||||
end
|
||||
update_loop_animation
|
||||
end
|
||||
|
||||
def dispose_animation
|
||||
return if @_animation_sprites == nil
|
||||
sprite = @_animation_sprites[0]
|
||||
if sprite != nil
|
||||
@@_reference_count[sprite.bitmap] -= 1
|
||||
if @@_reference_count[sprite.bitmap] == 0
|
||||
sprite.bitmap.dispose
|
||||
end
|
||||
end
|
||||
for sprite in @_animation_sprites
|
||||
sprite.dispose
|
||||
end
|
||||
@_animation_sprites = nil
|
||||
@_animation = nil
|
||||
end
|
||||
|
||||
def dispose_loop_animation
|
||||
return if @_loop_animation_sprites == nil
|
||||
sprite = @_loop_animation_sprites[0]
|
||||
if sprite != nil
|
||||
@@_reference_count[sprite.bitmap] -= 1
|
||||
if @@_reference_count[sprite.bitmap] == 0
|
||||
sprite.bitmap.dispose
|
||||
end
|
||||
end
|
||||
for sprite in @_loop_animation_sprites
|
||||
sprite.dispose
|
||||
end
|
||||
@_loop_animation_sprites = nil
|
||||
@_loop_animation = nil
|
||||
end
|
||||
|
||||
def active?
|
||||
return @_loop_animation_sprites != nil || @_animation_sprites != nil
|
||||
end
|
||||
|
||||
def effect?
|
||||
return @_animation_duration > 0
|
||||
end
|
||||
|
||||
def update
|
||||
if @_animation != nil
|
||||
quick_update = true
|
||||
if Graphics.frame_count % @_animation_frame_skip == 0
|
||||
@_animation_duration -= 1
|
||||
quick_update = false
|
||||
end
|
||||
update_animation(quick_update)
|
||||
end
|
||||
if @_loop_animation != nil
|
||||
quick_update = (Graphics.frame_count % @_loop_animation_frame_skip != 0)
|
||||
update_loop_animation(quick_update)
|
||||
if !quick_update
|
||||
@_loop_animation_index += 1
|
||||
@_loop_animation_index %= @_loop_animation.frame_max
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_animation(quick_update = false)
|
||||
if @_animation_duration <= 0
|
||||
dispose_animation
|
||||
return
|
||||
end
|
||||
frame_index = @_animation.frame_max - @_animation_duration
|
||||
cell_data = @_animation.frames[frame_index].cell_data
|
||||
position = @_animation.position
|
||||
animation_set_sprites(@_animation_sprites, cell_data, position, quick_update)
|
||||
return if quick_update
|
||||
for timing in @_animation.timings
|
||||
next if timing.frame != frame_index
|
||||
animation_process_timing(timing, @_animation_hit)
|
||||
end
|
||||
end
|
||||
|
||||
def update_loop_animation(quick_update = false)
|
||||
frame_index = @_loop_animation_index
|
||||
cell_data = @_loop_animation.frames[frame_index].cell_data
|
||||
position = @_loop_animation.position
|
||||
animation_set_sprites(@_loop_animation_sprites, cell_data, position, quick_update)
|
||||
return if quick_update
|
||||
for timing in @_loop_animation.timings
|
||||
next if timing.frame != frame_index
|
||||
animation_process_timing(timing, true)
|
||||
end
|
||||
end
|
||||
|
||||
def animation_set_sprites(sprites, cell_data, position, quick_update = false)
|
||||
sprite_x = 320
|
||||
sprite_y = 240
|
||||
if position == 3
|
||||
if self.viewport != nil
|
||||
sprite_x = self.viewport.rect.width / 2
|
||||
sprite_y = self.viewport.rect.height - 160
|
||||
end
|
||||
else
|
||||
sprite_x = self.x - self.ox + self.src_rect.width / 2
|
||||
sprite_y = self.y - self.oy + self.src_rect.height / 2
|
||||
sprite_y -= self.src_rect.height / 4 if position == 0
|
||||
sprite_y += self.src_rect.height / 4 if position == 2
|
||||
end
|
||||
for i in 0..15
|
||||
sprite = sprites[i]
|
||||
pattern = cell_data[i, 0]
|
||||
if sprite == nil or pattern == nil or pattern == -1
|
||||
sprite.visible = false if sprite != nil
|
||||
next
|
||||
end
|
||||
sprite.x = sprite_x + cell_data[i, 1]
|
||||
sprite.y = sprite_y + cell_data[i, 2]
|
||||
next if quick_update
|
||||
sprite.visible = true
|
||||
sprite.src_rect.set(pattern % 5 * 192, pattern / 5 * 192, 192, 192)
|
||||
case @_animation_height
|
||||
when 0; sprite.z = 1
|
||||
when 1; sprite.z = sprite.y+32+15
|
||||
when 2; sprite.z = sprite.y+32+32+17
|
||||
else; sprite.z = 2000
|
||||
end
|
||||
sprite.ox = 96
|
||||
sprite.oy = 96
|
||||
sprite.zoom_x = cell_data[i, 3] / 100.0
|
||||
sprite.zoom_y = cell_data[i, 3] / 100.0
|
||||
sprite.angle = cell_data[i, 4]
|
||||
sprite.mirror = (cell_data[i, 5] == 1)
|
||||
sprite.tone = self.tone
|
||||
sprite.opacity = cell_data[i, 6] * self.opacity / 255.0
|
||||
sprite.blend_type = cell_data[i, 7]
|
||||
end
|
||||
end
|
||||
|
||||
def animation_process_timing(timing, hit)
|
||||
if (timing.condition == 0) or
|
||||
(timing.condition == 1 and hit == true) or
|
||||
(timing.condition == 2 and hit == false)
|
||||
if timing.se.name != ""
|
||||
se = timing.se
|
||||
pbSEPlay(se)
|
||||
end
|
||||
case timing.flash_scope
|
||||
when 1
|
||||
self.flash(timing.flash_color, timing.flash_duration * 2)
|
||||
when 2
|
||||
if self.viewport != nil
|
||||
self.viewport.flash(timing.flash_color, timing.flash_duration * 2)
|
||||
end
|
||||
when 3
|
||||
self.flash(nil, timing.flash_duration * 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def x=(x)
|
||||
sx = x - self.x
|
||||
return if sx == 0
|
||||
if @_animation_sprites != nil
|
||||
for i in 0..15
|
||||
@_animation_sprites[i].x += sx
|
||||
end
|
||||
end
|
||||
if @_loop_animation_sprites != nil
|
||||
for i in 0..15
|
||||
@_loop_animation_sprites[i].x += sx
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def y=(y)
|
||||
sy = y - self.y
|
||||
return if sy == 0
|
||||
if @_animation_sprites != nil
|
||||
for i in 0..15
|
||||
@_animation_sprites[i].y += sy
|
||||
end
|
||||
end
|
||||
if @_loop_animation_sprites != nil
|
||||
for i in 0..15
|
||||
@_loop_animation_sprites[i].y += sy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RPG
|
||||
class Sprite < ::Sprite
|
||||
def initialize(viewport = nil)
|
||||
super(viewport)
|
||||
@_whiten_duration = 0
|
||||
@_appear_duration = 0
|
||||
@_escape_duration = 0
|
||||
@_collapse_duration = 0
|
||||
@_damage_duration = 0
|
||||
@_animation_duration = 0
|
||||
@_blink = false
|
||||
@animations = []
|
||||
@loopAnimations = []
|
||||
end
|
||||
|
||||
def dispose
|
||||
dispose_damage
|
||||
dispose_animation
|
||||
dispose_loop_animation
|
||||
super
|
||||
end
|
||||
|
||||
def whiten
|
||||
self.blend_type = 0
|
||||
self.color.set(255, 255, 255, 128)
|
||||
self.opacity = 255
|
||||
@_whiten_duration = 16
|
||||
@_appear_duration = 0
|
||||
@_escape_duration = 0
|
||||
@_collapse_duration = 0
|
||||
end
|
||||
|
||||
def appear
|
||||
self.blend_type = 0
|
||||
self.color.set(0, 0, 0, 0)
|
||||
self.opacity = 0
|
||||
@_appear_duration = 16
|
||||
@_whiten_duration = 0
|
||||
@_escape_duration = 0
|
||||
@_collapse_duration = 0
|
||||
end
|
||||
|
||||
def escape
|
||||
self.blend_type = 0
|
||||
self.color.set(0, 0, 0, 0)
|
||||
self.opacity = 255
|
||||
@_escape_duration = 32
|
||||
@_whiten_duration = 0
|
||||
@_appear_duration = 0
|
||||
@_collapse_duration = 0
|
||||
end
|
||||
|
||||
def collapse
|
||||
self.blend_type = 1
|
||||
self.color.set(255, 64, 64, 255)
|
||||
self.opacity = 255
|
||||
@_collapse_duration = 48
|
||||
@_whiten_duration = 0
|
||||
@_appear_duration = 0
|
||||
@_escape_duration = 0
|
||||
end
|
||||
|
||||
def damage(value, critical)
|
||||
dispose_damage
|
||||
damage_string = (value.is_a?(Numeric)) ? value.abs.to_s : value.to_s
|
||||
bitmap = Bitmap.new(160, 48)
|
||||
bitmap.font.name = "Arial Black"
|
||||
bitmap.font.size = 32
|
||||
bitmap.font.color.set(0, 0, 0)
|
||||
bitmap.draw_text(-1, 12-1, 160, 36, damage_string, 1)
|
||||
bitmap.draw_text(+1, 12-1, 160, 36, damage_string, 1)
|
||||
bitmap.draw_text(-1, 12+1, 160, 36, damage_string, 1)
|
||||
bitmap.draw_text(+1, 12+1, 160, 36, damage_string, 1)
|
||||
if value.is_a?(Numeric) and value < 0
|
||||
bitmap.font.color.set(176, 255, 144)
|
||||
else
|
||||
bitmap.font.color.set(255, 255, 255)
|
||||
end
|
||||
bitmap.draw_text(0, 12, 160, 36, damage_string, 1)
|
||||
if critical
|
||||
bitmap.font.size = 20
|
||||
bitmap.font.color.set(0, 0, 0)
|
||||
bitmap.draw_text(-1, -1, 160, 20, "CRITICAL", 1)
|
||||
bitmap.draw_text(+1, -1, 160, 20, "CRITICAL", 1)
|
||||
bitmap.draw_text(-1, +1, 160, 20, "CRITICAL", 1)
|
||||
bitmap.draw_text(+1, +1, 160, 20, "CRITICAL", 1)
|
||||
bitmap.font.color.set(255, 255, 255)
|
||||
bitmap.draw_text(0, 0, 160, 20, "CRITICAL", 1)
|
||||
end
|
||||
@_damage_sprite = ::Sprite.new(self.viewport)
|
||||
@_damage_sprite.bitmap = bitmap
|
||||
@_damage_sprite.ox = 80
|
||||
@_damage_sprite.oy = 20
|
||||
@_damage_sprite.x = self.x
|
||||
@_damage_sprite.y = self.y - self.oy / 2
|
||||
@_damage_sprite.z = 3000
|
||||
@_damage_duration = 40
|
||||
end
|
||||
|
||||
def pushAnimation(array, anim)
|
||||
for i in 0...array.length
|
||||
next if array[i] && array[i].active?
|
||||
array[i] = anim
|
||||
return
|
||||
end
|
||||
array.push(anim)
|
||||
end
|
||||
|
||||
def animation(animation, hit, height = 3)
|
||||
anim = SpriteAnimation.new(self)
|
||||
anim.animation(animation,hit,height)
|
||||
pushAnimation(@animations,anim)
|
||||
end
|
||||
|
||||
def loop_animation(animation)
|
||||
anim = SpriteAnimation.new(self)
|
||||
anim.loop_animation(animation)
|
||||
pushAnimation(@loopAnimations,anim)
|
||||
end
|
||||
|
||||
def dispose_damage
|
||||
return if @_damage_sprite == nil
|
||||
@_damage_sprite.bitmap.dispose
|
||||
@_damage_sprite.dispose
|
||||
@_damage_sprite = nil
|
||||
@_damage_duration = 0
|
||||
end
|
||||
|
||||
def dispose_animation
|
||||
for a in @animations
|
||||
a.dispose_animation if a
|
||||
end
|
||||
@animations.clear
|
||||
end
|
||||
|
||||
def dispose_loop_animation
|
||||
for a in @loopAnimations
|
||||
a.dispose_loop_animation if a
|
||||
end
|
||||
@loopAnimations.clear
|
||||
end
|
||||
|
||||
def blink_on
|
||||
return if @_blink
|
||||
@_blink = true
|
||||
@_blink_count = 0
|
||||
end
|
||||
|
||||
def blink_off
|
||||
return unless @_blink
|
||||
@_blink = false
|
||||
self.color.set(0, 0, 0, 0)
|
||||
end
|
||||
|
||||
def blink?
|
||||
return @_blink
|
||||
end
|
||||
|
||||
def effect?
|
||||
return true if @_whiten_duration > 0
|
||||
return true if @_appear_duration > 0
|
||||
return true if @_escape_duration > 0
|
||||
return true if @_collapse_duration > 0
|
||||
return true if @_damage_duration > 0
|
||||
for a in @animations
|
||||
return true if a.effect?
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
if @_whiten_duration > 0
|
||||
@_whiten_duration -= 1
|
||||
self.color.alpha = 128 - (16 - @_whiten_duration) * 10
|
||||
end
|
||||
if @_appear_duration > 0
|
||||
@_appear_duration -= 1
|
||||
self.opacity = (16 - @_appear_duration) * 16
|
||||
end
|
||||
if @_escape_duration > 0
|
||||
@_escape_duration -= 1
|
||||
self.opacity = 256 - (32 - @_escape_duration) * 10
|
||||
end
|
||||
if @_collapse_duration > 0
|
||||
@_collapse_duration -= 1
|
||||
self.opacity = 256 - (48 - @_collapse_duration) * 6
|
||||
end
|
||||
if @_damage_duration > 0
|
||||
@_damage_duration -= 1
|
||||
case @_damage_duration
|
||||
when 38..39
|
||||
@_damage_sprite.y -= 4
|
||||
when 36..37
|
||||
@_damage_sprite.y -= 2
|
||||
when 34..35
|
||||
@_damage_sprite.y += 2
|
||||
when 28..33
|
||||
@_damage_sprite.y += 4
|
||||
end
|
||||
@_damage_sprite.opacity = 256 - (12 - @_damage_duration) * 32
|
||||
if @_damage_duration == 0
|
||||
dispose_damage
|
||||
end
|
||||
end
|
||||
for a in @animations
|
||||
a.update
|
||||
end
|
||||
for a in @loopAnimations
|
||||
a.update
|
||||
end
|
||||
if @_blink
|
||||
@_blink_count = (@_blink_count + 1) % 32
|
||||
if @_blink_count < 16
|
||||
alpha = (16 - @_blink_count) * 6
|
||||
else
|
||||
alpha = (@_blink_count - 16) * 6
|
||||
end
|
||||
self.color.set(255, 255, 255, alpha)
|
||||
end
|
||||
SpriteAnimation.clear
|
||||
end
|
||||
|
||||
def update_animation
|
||||
for a in @animations
|
||||
a.update_animation if a && a.active?
|
||||
end
|
||||
end
|
||||
|
||||
def update_loop_animation
|
||||
for a in @loopAnimations
|
||||
a.update_loop_animation if a && a.active?
|
||||
end
|
||||
end
|
||||
|
||||
def x=(x)
|
||||
for a in @animations
|
||||
a.x = x if a
|
||||
end
|
||||
for a in @loopAnimations
|
||||
a.x = x if a
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def y=(y)
|
||||
for a in @animations
|
||||
a.y = y if a
|
||||
end
|
||||
for a in @loopAnimations
|
||||
a.y = y if a
|
||||
end
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
104
Data/Scripts/001_Technical/004_Win32API.rb
Normal file
104
Data/Scripts/001_Technical/004_Win32API.rb
Normal file
@@ -0,0 +1,104 @@
|
||||
class Win32API
|
||||
@@RGSSWINDOW = nil
|
||||
@@GetCurrentThreadId = Win32API.new('kernel32','GetCurrentThreadId', '%w()','l')
|
||||
@@GetWindowThreadProcessId = Win32API.new('user32','GetWindowThreadProcessId', '%w(l p)','l')
|
||||
@@FindWindowEx = Win32API.new('user32','FindWindowEx', '%w(l l p p)','l')
|
||||
|
||||
def Win32API.SetWindowText(text)
|
||||
hWnd = pbFindRgssWindow
|
||||
swp = Win32API.new('user32','SetWindowTextA',%(l, p),'i')
|
||||
swp.call(hWnd, text.to_s)
|
||||
end
|
||||
|
||||
# Added by Peter O. as a more reliable way to get the RGSS window
|
||||
def Win32API.pbFindRgssWindow
|
||||
return @@RGSSWINDOW if @@RGSSWINDOW
|
||||
processid = [0].pack('l')
|
||||
threadid = @@GetCurrentThreadId.call
|
||||
nextwindow = 0
|
||||
begin
|
||||
nextwindow = @@FindWindowEx.call(0,nextwindow,"RGSS Player",0)
|
||||
if nextwindow!=0
|
||||
wndthreadid = @@GetWindowThreadProcessId.call(nextwindow,processid)
|
||||
if wndthreadid==threadid
|
||||
@@RGSSWINDOW = nextwindow
|
||||
return @@RGSSWINDOW
|
||||
end
|
||||
end
|
||||
end until nextwindow==0
|
||||
raise "Can't find RGSS player window"
|
||||
return 0
|
||||
end
|
||||
|
||||
def Win32API.SetWindowPos(w, h)
|
||||
hWnd = pbFindRgssWindow
|
||||
windowrect = Win32API.GetWindowRect
|
||||
clientsize = Win32API.client_size
|
||||
xExtra = windowrect.width-clientsize[0]
|
||||
yExtra = windowrect.height-clientsize[1]
|
||||
swp = Win32API.new('user32','SetWindowPos',%(l,l,i,i,i,i,i),'i')
|
||||
win = swp.call(hWnd,0,windowrect.x,windowrect.y,w+xExtra,h+yExtra,0)
|
||||
return win
|
||||
end
|
||||
|
||||
def Win32API.client_size
|
||||
hWnd = pbFindRgssWindow
|
||||
rect = [0,0,0,0].pack('l4')
|
||||
Win32API.new('user32','GetClientRect',%w(l p),'i').call(hWnd,rect)
|
||||
width,height = rect.unpack('l4')[2..3]
|
||||
return width,height
|
||||
end
|
||||
|
||||
def Win32API.GetWindowRect
|
||||
hWnd = pbFindRgssWindow
|
||||
rect = [0,0,0,0].pack('l4')
|
||||
Win32API.new('user32','GetWindowRect',%w(l p),'i').call(hWnd,rect)
|
||||
x,y,width,height = rect.unpack('l4')
|
||||
return Rect.new(x,y,width-x,height-y)
|
||||
end
|
||||
|
||||
def Win32API.focusWindow
|
||||
window = Win32API.new('user32','ShowWindow','LL','L')
|
||||
hWnd = pbFindRgssWindow
|
||||
window.call(hWnd,9)
|
||||
end
|
||||
|
||||
def Win32API.fillScreen
|
||||
setWindowLong = Win32API.new('user32','SetWindowLong','LLL','L')
|
||||
setWindowPos = Win32API.new('user32','SetWindowPos','LLIIIII','I')
|
||||
metrics = Win32API.new('user32', 'GetSystemMetrics', 'I', 'I')
|
||||
hWnd = pbFindRgssWindow
|
||||
width = metrics.call(0)
|
||||
height = metrics.call(1)
|
||||
setWindowLong.call(hWnd,-16,0x00000000)
|
||||
setWindowPos.call(hWnd,0,0,0,width,height,0)
|
||||
Win32API.focusWindow
|
||||
return [width,height]
|
||||
end
|
||||
|
||||
def Win32API.restoreScreen
|
||||
setWindowLong = Win32API.new('user32','SetWindowLong','LLL','L')
|
||||
setWindowPos = Win32API.new('user32','SetWindowPos','LLIIIII','I')
|
||||
metrics = Win32API.new('user32','GetSystemMetrics','I','I')
|
||||
hWnd = pbFindRgssWindow
|
||||
width = SCREEN_WIDTH*$ResizeFactor
|
||||
height = SCREEN_HEIGHT*$ResizeFactor
|
||||
if $PokemonSystem && $PokemonSystem.border==1
|
||||
width += BORDER_WIDTH*2*$ResizeFactor
|
||||
height += BORDER_HEIGHT*2*$ResizeFactor
|
||||
end
|
||||
x = [(metrics.call(0)-width)/2,0].max
|
||||
y = [(metrics.call(1)-height)/2,0].max
|
||||
setWindowLong.call(hWnd,-16,0x14CA0000)
|
||||
setWindowPos.call(hWnd,0,x,y,width+6,height+29,0)
|
||||
Win32API.focusWindow
|
||||
return [width,height]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# Well done for finding this place.
|
||||
# DO NOT EDIT THESE
|
||||
ESSENTIALS_VERSION = "18"
|
||||
ERROR_TEXT = ""
|
||||
696
Data/Scripts/001_Technical/005_Sockets.rb
Normal file
696
Data/Scripts/001_Technical/005_Sockets.rb
Normal file
@@ -0,0 +1,696 @@
|
||||
module Win32
|
||||
def copymem(len)
|
||||
buf = "\0" * len
|
||||
Win32API.new("kernel32", "RtlMoveMemory", "ppl", "").call(buf, self, len)
|
||||
buf
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# Extends the numeric class.
|
||||
class Numeric
|
||||
include Win32
|
||||
end
|
||||
|
||||
|
||||
|
||||
# Extends the string class.
|
||||
class String
|
||||
include Win32
|
||||
end
|
||||
|
||||
|
||||
|
||||
module Winsock
|
||||
DLL = "ws2_32"
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Accept Connection
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.accept(*args)
|
||||
Win32API.new(DLL, "accept", "ppl", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Bind
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.bind(*args)
|
||||
Win32API.new(DLL, "bind", "ppl", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Close Socket
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.closesocket(*args)
|
||||
Win32API.new(DLL, "closesocket", "p", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Connect
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.connect(*args)
|
||||
Win32API.new(DLL, "connect", "ppl", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get host (Using Adress)
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.gethostbyaddr(*args)
|
||||
Win32API.new(DLL, "gethostbyaddr", "pll", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get host (Using Name)
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.gethostbyname(*args)
|
||||
Win32API.new(DLL, "gethostbyname", "p", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get host's Name
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.gethostname(*args)
|
||||
Win32API.new(DLL, "gethostname", "pl", "").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get Server (Using Name)
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.getservbyname(*args)
|
||||
Win32API.new(DLL, "getservbyname", "pp", "p").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Convert Host Long To Network Long
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.htonl(*args)
|
||||
Win32API.new(DLL, "htonl", "l", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Convert Host Short To Network Short
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.htons(*args)
|
||||
Win32API.new(DLL, "htons", "l", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Inet Adress
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.inet_addr(*args)
|
||||
Win32API.new(DLL, "inet_addr", "p", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Inet N To A
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.inet_ntoa(*args)
|
||||
Win32API.new(DLL, "inet_ntoa", "l", "p").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Listen
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.listen(*args)
|
||||
Win32API.new(DLL, "listen", "pl", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Recieve
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.recv(*args)
|
||||
Win32API.new(DLL, "recv", "ppll", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Select
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.select(*args)
|
||||
Win32API.new(DLL, "select", "lpppp", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Send
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.send(*args)
|
||||
Win32API.new(DLL, "send", "ppll", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Set Socket Options
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.setsockopt(*args)
|
||||
Win32API.new(DLL, "setsockopt", "pllpl", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Shutdown
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.shutdown(*args)
|
||||
Win32API.new(DLL, "shutdown", "pl", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Socket
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.socket(*args)
|
||||
Win32API.new(DLL, "socket", "lll", "l").call(*args)
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get Last Error
|
||||
#-----------------------------------------------------------------------------
|
||||
def self.WSAGetLastError(*args)
|
||||
Win32API.new(DLL, "WSAGetLastError", "", "l").call(*args)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
if !Object.const_defined?(:Socket) # for compatibility
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# ** Socket - Creates and manages sockets.
|
||||
#-------------------------------------------------------------------------------
|
||||
# Author Ruby
|
||||
# Version 1.8.1
|
||||
#===============================================================================
|
||||
class Socket
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Constants
|
||||
#-----------------------------------------------------------------------------
|
||||
AF_UNSPEC = 0
|
||||
AF_UNIX = 1
|
||||
AF_INET = 2
|
||||
AF_IPX = 6
|
||||
AF_APPLETALK = 16
|
||||
PF_UNSPEC = 0
|
||||
PF_UNIX = 1
|
||||
PF_INET = 2
|
||||
PF_IPX = 6
|
||||
PF_APPLETALK = 16
|
||||
SOCK_STREAM = 1
|
||||
SOCK_DGRAM = 2
|
||||
SOCK_RAW = 3
|
||||
SOCK_RDM = 4
|
||||
SOCK_SEQPACKET = 5
|
||||
IPPROTO_IP = 0
|
||||
IPPROTO_ICMP = 1
|
||||
IPPROTO_IGMP = 2
|
||||
IPPROTO_GGP = 3
|
||||
IPPROTO_TCP = 6
|
||||
IPPROTO_PUP = 12
|
||||
IPPROTO_UDP = 17
|
||||
IPPROTO_IDP = 22
|
||||
IPPROTO_ND = 77
|
||||
IPPROTO_RAW = 255
|
||||
IPPROTO_MAX = 256
|
||||
SOL_SOCKET = 65535
|
||||
SO_DEBUG = 1
|
||||
SO_REUSEADDR = 4
|
||||
SO_KEEPALIVE = 8
|
||||
SO_DONTROUTE = 16
|
||||
SO_BROADCAST = 32
|
||||
SO_LINGER = 128
|
||||
SO_OOBINLINE = 256
|
||||
SO_RCVLOWAT = 4100
|
||||
SO_SNDTIMEO = 4101
|
||||
SO_RCVTIMEO = 4102
|
||||
SO_ERROR = 4103
|
||||
SO_TYPE = 4104
|
||||
SO_SNDBUF = 4097
|
||||
SO_RCVBUF = 4098
|
||||
SO_SNDLOWAT = 4099
|
||||
TCP_NODELAY = 1
|
||||
MSG_OOB = 1
|
||||
MSG_PEEK = 2
|
||||
MSG_DONTROUTE = 4
|
||||
IP_OPTIONS = 1
|
||||
IP_DEFAULT_MULTICAST_LOOP = 1
|
||||
IP_DEFAULT_MULTICAST_TTL = 1
|
||||
IP_MULTICAST_IF = 2
|
||||
IP_MULTICAST_TTL = 3
|
||||
IP_MULTICAST_LOOP = 4
|
||||
IP_ADD_MEMBERSHIP = 5
|
||||
IP_DROP_MEMBERSHIP = 6
|
||||
IP_TTL = 7
|
||||
IP_TOS = 8
|
||||
IP_MAX_MEMBERSHIPS = 20
|
||||
EAI_ADDRFAMILY = 1
|
||||
EAI_AGAIN = 2
|
||||
EAI_BADFLAGS = 3
|
||||
EAI_FAIL = 4
|
||||
EAI_FAMILY = 5
|
||||
EAI_MEMORY = 6
|
||||
EAI_NODATA = 7
|
||||
EAI_NONAME = 8
|
||||
EAI_SERVICE = 9
|
||||
EAI_SOCKTYPE = 10
|
||||
EAI_SYSTEM = 11
|
||||
EAI_BADHINTS = 12
|
||||
EAI_PROTOCOL = 13
|
||||
EAI_MAX = 14
|
||||
AI_PASSIVE = 1
|
||||
AI_CANONNAME = 2
|
||||
AI_NUMERICHOST = 4
|
||||
AI_MASK = 7
|
||||
AI_ALL = 256
|
||||
AI_V4MAPPED_CFG = 512
|
||||
AI_ADDRCONFIG = 1024
|
||||
AI_DEFAULT = 1536
|
||||
AI_V4MAPPED = 2048
|
||||
#--------------------------------------------------------------------------
|
||||
# * Returns the associated IP address for the given hostname.
|
||||
#--------------------------------------------------------------------------
|
||||
def self.getaddress(host)
|
||||
gethostbyname(host)[3].unpack("C4").join(".")
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Returns the associated IP address for the given hostname.
|
||||
#--------------------------------------------------------------------------
|
||||
def self.getservice(serv)
|
||||
case serv
|
||||
when Numeric
|
||||
return serv
|
||||
when String
|
||||
return getservbyname(serv)
|
||||
else
|
||||
raise "Please use an integer or string for services."
|
||||
end
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Returns information about the given hostname.
|
||||
#--------------------------------------------------------------------------
|
||||
def self.gethostbyname(name)
|
||||
raise SocketError::ENOASSOCHOST if (ptr = Winsock.gethostbyname(name)) == 0
|
||||
host = ptr.copymem(16).unpack("iissi")
|
||||
[host[0].copymem(64).split("\0")[0], [], host[2], host[4].copymem(4).unpack("l")[0].copymem(4)]
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Returns the user's hostname.
|
||||
#--------------------------------------------------------------------------
|
||||
def self.gethostname
|
||||
buf = "\0" * 256
|
||||
Winsock.gethostname(buf, 256)
|
||||
buf.strip
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Returns information about the given service.
|
||||
#--------------------------------------------------------------------------
|
||||
def self.getservbyname(name)
|
||||
case name
|
||||
when /echo/i
|
||||
return 7
|
||||
when /daytime/i
|
||||
return 13
|
||||
when /ftp/i
|
||||
return 21
|
||||
when /telnet/i
|
||||
return 23
|
||||
when /smtp/i
|
||||
return 25
|
||||
when /time/i
|
||||
return 37
|
||||
when /http/i
|
||||
return 80
|
||||
when /pop/i
|
||||
return 110
|
||||
else
|
||||
#Network.testing? != 0 ? (Network.testresult(true)) : (raise "Service not recognized.")
|
||||
#return if Network.testing? == 2
|
||||
end
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Creates an INET-sockaddr struct.
|
||||
#--------------------------------------------------------------------------
|
||||
def self.sockaddr_in(port, host)
|
||||
begin
|
||||
[AF_INET, getservice(port)].pack("sn") + gethostbyname(host)[3] + [].pack("x8")
|
||||
rescue
|
||||
#Network.testing? != 0 ? (Network.testresult(true)): (nil)
|
||||
#return if Network.testing? == 2
|
||||
rescue Hangup
|
||||
#Network.testing? != 0 ? (Network.testresult(true)): (nil)
|
||||
#return if Network.testing? == 2
|
||||
end
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Creates a new socket and connects it to the given host and port.
|
||||
#--------------------------------------------------------------------------
|
||||
def self.open(*args)
|
||||
socket = new(*args)
|
||||
if block_given?
|
||||
begin
|
||||
yield socket
|
||||
ensure
|
||||
socket.close
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Creates a new socket.
|
||||
#--------------------------------------------------------------------------
|
||||
def initialize(domain, type, protocol)
|
||||
SocketError.check if (@fd = Winsock.socket(domain, type, protocol)) == -1
|
||||
@fd
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Accepts incoming connections.
|
||||
#--------------------------------------------------------------------------
|
||||
def accept(flags = 0)
|
||||
buf = "\0" * 16
|
||||
SocketError.check if Winsock.accept(@fd, buf, flags) == -1
|
||||
buf
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Binds a socket to the given sockaddr.
|
||||
#--------------------------------------------------------------------------
|
||||
def bind(sockaddr)
|
||||
SocketError.check if (ret = Winsock.bind(@fd, sockaddr, sockaddr.size)) == -1
|
||||
ret
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Closes a socket.
|
||||
#--------------------------------------------------------------------------
|
||||
def close
|
||||
SocketError.check if (ret = Winsock.closesocket(@fd)) == -1
|
||||
ret
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Connects a socket to the given sockaddr.
|
||||
#--------------------------------------------------------------------------
|
||||
def connect(sockaddr)
|
||||
#return if Network.testing? == 2
|
||||
SocketError.check if (ret = Winsock.connect(@fd, sockaddr, sockaddr.size)) == -1
|
||||
ret
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Listens for incoming connections.
|
||||
#--------------------------------------------------------------------------
|
||||
def listen(backlog)
|
||||
SocketError.check if (ret = Winsock.listen(@fd, backlog)) == -1
|
||||
ret
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Checks waiting data's status.
|
||||
#--------------------------------------------------------------------------
|
||||
def select(timeout) # timeout in seconds
|
||||
SocketError.check if (ret = Winsock.select(1, [1, @fd].pack("ll"), 0, 0, [timeout.to_i,
|
||||
(timeout * 1000000).to_i].pack("ll"))) == -1
|
||||
ret
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Checks if data is waiting.
|
||||
#--------------------------------------------------------------------------
|
||||
def ready?
|
||||
not select(0) == 0
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Reads data from socket.
|
||||
#--------------------------------------------------------------------------
|
||||
def read(len)
|
||||
buf = "\0" * len
|
||||
Win32API.new("msvcrt", "_read", "lpl", "l").call(@fd, buf, len)
|
||||
buf
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Returns received data.
|
||||
#--------------------------------------------------------------------------
|
||||
def recv(len, flags = 0)
|
||||
retString=""
|
||||
remainLen=len
|
||||
while remainLen > 0
|
||||
buf = "\0" * remainLen
|
||||
retval=Winsock.recv(@fd, buf, buf.size, flags)
|
||||
SocketError.check if retval == -1
|
||||
# Note: Return value may not equal requested length
|
||||
remainLen-=retval
|
||||
retString+=buf[0,retval]
|
||||
end
|
||||
return retString
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Sends data to a host.
|
||||
#--------------------------------------------------------------------------
|
||||
def send(data, flags = 0)
|
||||
SocketError.check if (ret = Winsock.send(@fd, data, data.size, flags)) == -1
|
||||
ret
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Recieves file from a socket
|
||||
# size : file size
|
||||
# scene : update scene boolean
|
||||
#--------------------------------------------------------------------------
|
||||
def recv_file(size,scene=false,file="")
|
||||
data = []
|
||||
size.times do |i|
|
||||
if scene == true
|
||||
$scene.recv_update(size,i,file) if i%((size/1000)+1)== 0
|
||||
else
|
||||
Graphics.update if i%1024 == 0
|
||||
end
|
||||
data << recv(1)
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
def recvTimeout
|
||||
if select(10)==0
|
||||
raise Hangup.new("Timeout")
|
||||
end
|
||||
return recv(1)
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Gets
|
||||
#--------------------------------------------------------------------------
|
||||
def gets
|
||||
# Create buffer
|
||||
message = ""
|
||||
# Loop Until "end of line"
|
||||
count=0
|
||||
while true
|
||||
x=select(0.05)
|
||||
if x==0
|
||||
count+=1
|
||||
Graphics.update if count%10==0
|
||||
raise Errno::ETIMEOUT if count>200
|
||||
next
|
||||
end
|
||||
ch = recv(1)
|
||||
break if ch == "\n"
|
||||
message += ch
|
||||
end
|
||||
# Return recieved data
|
||||
return message
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Writes data to socket.
|
||||
#--------------------------------------------------------------------------
|
||||
def write(data)
|
||||
Win32API.new("msvcrt", "_write", "lpl", "l").call(@fd, data, 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# ** TCPSocket - Creates and manages TCP sockets.
|
||||
#-------------------------------------------------------------------------------
|
||||
# Author Ruby
|
||||
# Version 1.8.1
|
||||
#===============================================================================
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Begin SDK Enabled Check
|
||||
#-------------------------------------------------------------------------------
|
||||
class TCPSocket < Socket
|
||||
#--------------------------------------------------------------------------
|
||||
# * Creates a new socket and connects it to the given host and port.
|
||||
#--------------------------------------------------------------------------
|
||||
def self.open(*args)
|
||||
socket = new(*args)
|
||||
if block_given?
|
||||
begin
|
||||
yield socket
|
||||
ensure
|
||||
socket.close
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
#--------------------------------------------------------------------------
|
||||
# * Creates a new socket and connects it to the given host and port.
|
||||
#--------------------------------------------------------------------------
|
||||
def initialize(host, port)
|
||||
super(AF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||
connect(Socket.sockaddr_in(port, host))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#==============================================================================
|
||||
# ** SocketError
|
||||
#------------------------------------------------------------------------------
|
||||
# Default exception class for sockets.
|
||||
#==============================================================================
|
||||
class SocketError < StandardError
|
||||
ENOASSOCHOST = "getaddrinfo: no address associated with hostname."
|
||||
|
||||
def self.check
|
||||
errno = Winsock.WSAGetLastError
|
||||
#if not Network.testing? == 1
|
||||
raise Errno.const_get(Errno.constants.detect { |c| Errno.const_get(c).new.errno == errno })
|
||||
#else
|
||||
# errno != 0 ? (Network.testresult(true)) : (Network.testresult(false))
|
||||
#end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
end # !Object.const_defined?(:Socket)
|
||||
|
||||
|
||||
#############################
|
||||
#
|
||||
# HTTP utility functions
|
||||
#
|
||||
#############################
|
||||
def pbPostData(url, postdata, filename=nil, depth=0)
|
||||
if url[/^http:\/\/([^\/]+)(.*)$/]
|
||||
host = $1
|
||||
path = $2
|
||||
path = "/" if path.length==0
|
||||
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
|
||||
body = postdata.map { |key, value|
|
||||
keyString = key.to_s
|
||||
valueString = value.to_s
|
||||
keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
|
||||
valueString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
|
||||
next "#{keyString}=#{valueString}"
|
||||
}.join('&')
|
||||
request = "POST #{path} HTTP/1.1\r\n"
|
||||
request += "Host: #{host}\r\n"
|
||||
request += "Proxy-Connection: Close\r\n"
|
||||
request += "Content-Length: #{body.length}\r\n"
|
||||
request += "Pragma: no-cache\r\n"
|
||||
request += "User-Agent: #{userAgent}\r\n"
|
||||
request += "Content-Type: application/x-www-form-urlencoded\r\n"
|
||||
request += "\r\n"
|
||||
request += body
|
||||
return pbHttpRequest(host, request, filename, depth)
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
def pbDownloadData(url, filename=nil, depth=0)
|
||||
raise "Redirection level too deep" if depth>10
|
||||
if url[/^http:\/\/([^\/]+)(.*)$/]
|
||||
host = $1
|
||||
path = $2
|
||||
path = "/" if path.length==0
|
||||
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
|
||||
request = "GET #{path} HTTP/1.1\r\n"
|
||||
request += "User-Agent: #{userAgent}\r\n"
|
||||
request += "Pragma: no-cache\r\n"
|
||||
request += "Host: #{host}\r\n"
|
||||
request += "Proxy-Connection: Close\r\n"
|
||||
request += "\r\n"
|
||||
return pbHttpRequest(host, request, filename, depth)
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
def pbHttpRequest(host, request, filename=nil, depth=0)
|
||||
raise "Redirection level too deep" if depth>10
|
||||
socket = ::TCPSocket.new(host, 80)
|
||||
time = Time.now.to_i
|
||||
begin
|
||||
socket.send(request)
|
||||
result = socket.gets
|
||||
data = ""
|
||||
# Get the HTTP result
|
||||
if result[/^HTTP\/1\.[01] (\d+).*/]
|
||||
errorcode = $1.to_i
|
||||
raise "HTTP Error #{errorcode}" if errorcode>=400 && errorcode<500
|
||||
headers = {}
|
||||
# Get the response headers
|
||||
while true
|
||||
result = socket.gets.sub(/\r$/,"")
|
||||
break if result==""
|
||||
if result[/^([^:]+):\s*(.*)/]
|
||||
headers[$1] = $2
|
||||
end
|
||||
end
|
||||
length = -1
|
||||
chunked = false
|
||||
if headers["Content-Length"]
|
||||
length = headers["Content-Length"].to_i
|
||||
end
|
||||
if headers["Transfer-Encoding"]=="chunked"
|
||||
chunked = true
|
||||
end
|
||||
if headers["Location"] && errorcode>=300 && errorcode<400
|
||||
socket.close rescue socket = nil
|
||||
return pbDownloadData(headers["Location"],filename,depth+1)
|
||||
end
|
||||
if chunked
|
||||
# Chunked content
|
||||
while true
|
||||
lengthline = socket.gets.sub(/\r$/,"")
|
||||
length = lengthline.to_i(16)
|
||||
break if length==0
|
||||
while Time.now.to_i-time>=5 || socket.select(10)==0
|
||||
time = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
data += socket.recv(length)
|
||||
socket.gets
|
||||
end
|
||||
elsif length==-1
|
||||
# No content length specified
|
||||
while true
|
||||
break if socket.select(500)==0
|
||||
while Time.now.to_i-time>=5 || socket.select(10)==0
|
||||
time = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
data += socket.recv(1)
|
||||
end
|
||||
else
|
||||
# Content length specified
|
||||
while length>0
|
||||
chunk = [length,4096].min
|
||||
while Time.now.to_i-time>=5 || socket.select(10)==0
|
||||
time = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
data += socket.recv(chunk)
|
||||
length -= chunk
|
||||
end
|
||||
end
|
||||
end
|
||||
return data if !filename
|
||||
File.open(filename,"wb") { |f| f.write(data) }
|
||||
ensure
|
||||
socket.close rescue socket = nil
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
def pbDownloadToString(url)
|
||||
begin
|
||||
data = pbDownloadData(url)
|
||||
return data
|
||||
rescue
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
def pbDownloadToFile(url, file)
|
||||
begin
|
||||
pbDownloadData(url,file)
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
def pbPostToString(url, postdata)
|
||||
begin
|
||||
data = pbPostData(url, postdata)
|
||||
return data
|
||||
rescue
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
def pbPostToFile(url, postdata, file)
|
||||
begin
|
||||
pbPostData(url, postdata,file)
|
||||
rescue
|
||||
end
|
||||
end
|
||||
158
Data/Scripts/001_Technical/006_DebugConsole.rb
Normal file
158
Data/Scripts/001_Technical/006_DebugConsole.rb
Normal file
@@ -0,0 +1,158 @@
|
||||
module Console
|
||||
attr_reader :bufferHandle
|
||||
GENERIC_READ = 0x80000000
|
||||
GENERIC_WRITE = 0x40000000
|
||||
FILE_SHARE_READ = 0x00000001
|
||||
FILE_SHARE_WRITE = 0x00000002
|
||||
CONSOLE_TEXTMODE_BUFFER = 0x00000001
|
||||
|
||||
def Console::AllocConsole
|
||||
return @apiAllocConsole.call
|
||||
end
|
||||
|
||||
def Console::CreateConsoleScreenBuffer(dwDesiredAccess,dwShareMode,dwFlags)
|
||||
return @apiCreateConsoleScreenBuffer.call(dwDesiredAccess,dwShareMode,nil,dwFlags,nil)
|
||||
end
|
||||
|
||||
def Console::WriteConsole(lpBuffer)
|
||||
hFile = @bufferHandle
|
||||
return if !hFile
|
||||
return @apiWriteConsole.call(hFile,lpBuffer,lpBuffer.size,0,0)
|
||||
end
|
||||
|
||||
def Console::ReadConsole(lpBuffer)
|
||||
hFile = @bufferHandle
|
||||
return @apiReadConsole.call(hFile,lpBuffer,lpBuffer.size,0,0)
|
||||
end
|
||||
|
||||
def Console::SetConsoleActiveScreenBuffer(hScreenBuffer)
|
||||
return @apiSetConsoleActiveScreenBuffer.call(hScreenBuffer)
|
||||
end
|
||||
|
||||
def Console::SetConsoleScreenBufferSize(hScreenBuffer,x,y)
|
||||
return @apiSetConsoleScreenBufferSize.call(hScreenBuffer,[x,y].pack("vv"))
|
||||
end
|
||||
|
||||
def Console::SetConsoleTitle(title)
|
||||
return @apiSetConsoleTitle.call(title)
|
||||
end
|
||||
|
||||
def self.setup_console
|
||||
return unless $DEBUG
|
||||
@apiAllocConsole = Win32API.new("kernel32","AllocConsole","","l")
|
||||
@apiCreateConsoleScreenBuffer = Win32API.new("kernel32","CreateConsoleScreenBuffer","nnpnp","l")
|
||||
@apiSetConsoleActiveScreenBuffer = Win32API.new("kernel32","SetConsoleActiveScreenBuffer","l","s")
|
||||
@apiWriteConsole = Win32API.new("kernel32","WriteConsole","lpnnn","S")
|
||||
@apiReadConsole = Win32API.new("kernel32","ReadConsole","lpnnn","S")
|
||||
@apiSetConsoleScreenBufferSize = Win32API.new("kernel32","SetConsoleScreenBufferSize","lp","S")
|
||||
@apiSetConsoleTitle = Win32API.new("kernel32","SetConsoleTitle","p","s")
|
||||
access = (GENERIC_READ | GENERIC_WRITE)
|
||||
sharemode = (FILE_SHARE_READ | FILE_SHARE_WRITE)
|
||||
returnCode = AllocConsole()
|
||||
@bufferHandle = CreateConsoleScreenBuffer(access,sharemode,CONSOLE_TEXTMODE_BUFFER)
|
||||
f = File.open("Game.ini")
|
||||
lines = f.readlines()
|
||||
s = lines[3]
|
||||
len = s.size
|
||||
title = (s[6,len - 7])
|
||||
SetConsoleScreenBufferSize(@bufferHandle,100,2000)
|
||||
SetConsoleTitle("Debug Console -- #{title}")
|
||||
echo "#{title} Output Window\n"
|
||||
echo "-------------------------------\n"
|
||||
echo "If you are seeing this window, you are running\n"
|
||||
echo "#{title} in Debug Mode. This means\n"
|
||||
echo "that you're either playing a Debug Version, or\n"
|
||||
echo "you are playing from within RPG Maker XP.\n"
|
||||
echo "\n"
|
||||
echo "Closing this window will close the game. If \n"
|
||||
echo "you want to get rid of this window, run the\n"
|
||||
echo "program from the Shell, or download a Release\n"
|
||||
echo "version.\n"
|
||||
echo "\n"
|
||||
echo "Gameplay will be paused while the console has\n"
|
||||
echo "focus. To resume playing, switch to the Game\n"
|
||||
echo "Window.\n"
|
||||
echo "-------------------------------\n"
|
||||
echo "Debug Output:\n"
|
||||
echo "-------------------------------\n\n"
|
||||
SetConsoleActiveScreenBuffer(@bufferHandle)
|
||||
end
|
||||
|
||||
def self.readInput
|
||||
length=20
|
||||
buffer=0.chr*length
|
||||
eventsread=0.chr*4
|
||||
done=false
|
||||
input=""
|
||||
while !done
|
||||
echo("waiting for input")
|
||||
begin
|
||||
@apiReadConsole.call(@bufferHandle,buffer,1,eventsread)
|
||||
rescue Hangup
|
||||
return
|
||||
end
|
||||
offset=0
|
||||
events=eventsread.unpack("V")
|
||||
echo("got input [eventsread #{events}")
|
||||
for i in 0...events[0]
|
||||
keyevent=buffer[offset,20]
|
||||
keyevent=keyevent.unpack("vCvvvvV")
|
||||
if keyevent[0]==1 && keyevent[1]>0
|
||||
input+=keyevent[4].chr
|
||||
if keyevent[4].chr=="\n"
|
||||
done=true
|
||||
break
|
||||
end
|
||||
end
|
||||
offset+=20
|
||||
end
|
||||
end
|
||||
return input
|
||||
end
|
||||
|
||||
def self.readInput2
|
||||
buffer=0.chr
|
||||
done=false
|
||||
input=""
|
||||
eventsread=0.chr*4
|
||||
while !done
|
||||
if ReadConsole(buffer)==0
|
||||
getlast = Win32API.new("kernel32","GetLastError","","n")
|
||||
echo(sprintf("failed (%d)\r\n",getlast.call()))
|
||||
break
|
||||
end
|
||||
offset=0
|
||||
events=eventsread.unpack("V")
|
||||
if events[0]>0
|
||||
echo("got input [eventsread #{events}][buffer #{buffer}]\r\n")
|
||||
key=buffer[0,events[0]]
|
||||
input+=key
|
||||
if key=="\n"
|
||||
break
|
||||
end
|
||||
Graphics.update
|
||||
end
|
||||
end
|
||||
return input
|
||||
end
|
||||
|
||||
def self.get_input
|
||||
echo self.readInput2
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module Kernel
|
||||
def echo(string)
|
||||
unless $DEBUG
|
||||
return
|
||||
end
|
||||
Console::WriteConsole(string.is_a?(String) ? string : string.inspect)
|
||||
end
|
||||
|
||||
def echoln(string)
|
||||
echo(string)
|
||||
echo("\r\n")
|
||||
end
|
||||
end
|
||||
757
Data/Scripts/001_Technical/007_Sprite_Resizer.rb
Normal file
757
Data/Scripts/001_Technical/007_Sprite_Resizer.rb
Normal file
@@ -0,0 +1,757 @@
|
||||
#===============================================================================
|
||||
# Overriding Sprite, Viewport, and Plane to support resizing
|
||||
# By Peter O.
|
||||
# Modified by Luka S.J. and Maruno to support fullscreen and more sizes.
|
||||
# -- This is a stand-alone RGSS script. --
|
||||
#===============================================================================
|
||||
$ResizeFactor = 1.0
|
||||
$ResizeFactorMul = 100
|
||||
$ResizeOffsetX = 0
|
||||
$ResizeOffsetY = 0
|
||||
$ResizeFactorSet = false
|
||||
$HaveResizeBorder = false
|
||||
|
||||
if true # Disables using Alt+Enter to go fullscreen
|
||||
regHotKey = Win32API.new('user32', 'RegisterHotKey', 'LIII', 'I')
|
||||
regHotKey.call(0, 1, 1, 0x0D)
|
||||
end
|
||||
|
||||
def pbSetResizeFactor(factor=1,norecalc=false)
|
||||
factor = [0.5,1.0,2.0,-1][factor] if !norecalc
|
||||
(factor<0) ? pbConfigureFullScreen : pbConfigureWindowedScreen(factor)
|
||||
end
|
||||
|
||||
def pbSetResizeFactor2(factor,force=false)
|
||||
if $ResizeFactor!=factor || force
|
||||
$ResizeFactor = factor
|
||||
$ResizeFactorMul = (factor*100).to_i
|
||||
pbRefreshResizeFactor if $ResizeFactorSet
|
||||
end
|
||||
$ResizeFactorSet = true
|
||||
$ResizeBorder.refresh if $HaveResizeBorder
|
||||
begin
|
||||
if Graphics.haveresizescreen
|
||||
Graphics.oldresizescreen(
|
||||
(Graphics.width+$ResizeOffsetX*2)*factor,
|
||||
(Graphics.height+$ResizeOffsetY*2)*factor
|
||||
)
|
||||
end
|
||||
Win32API.SetWindowPos(
|
||||
(Graphics.width+$ResizeOffsetX*2)*factor,
|
||||
(Graphics.height+$ResizeOffsetY*2)*factor
|
||||
)
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
def pbRefreshResizeFactor
|
||||
ObjectSpace.each_object(Sprite) { |o|
|
||||
next if o.disposed?
|
||||
o.x = o.x
|
||||
o.y = o.y
|
||||
o.ox = o.ox
|
||||
o.oy = o.oy
|
||||
o.zoom_x = o.zoom_x
|
||||
o.zoom_y = o.zoom_y
|
||||
}
|
||||
ObjectSpace.each_object(Viewport) { |o|
|
||||
begin
|
||||
o.rect = o.rect
|
||||
o.ox = o.ox
|
||||
o.oy = o.oy
|
||||
rescue RGSSError
|
||||
end
|
||||
}
|
||||
ObjectSpace.each_object(Plane) { |o|
|
||||
next if o.disposed?
|
||||
o.zoom_x = o.zoom_x
|
||||
o.zoom_y = o.zoom_y
|
||||
}
|
||||
end
|
||||
|
||||
def pbConfigureFullScreen
|
||||
params = Win32API.fillScreen
|
||||
fullgamew = gamew = SCREEN_WIDTH
|
||||
fullgameh = gameh = SCREEN_HEIGHT
|
||||
if !BORDER_FULLY_SHOWS && $PokemonSystem && $PokemonSystem.border==1
|
||||
fullgamew += BORDER_WIDTH * 2
|
||||
fullgameh += BORDER_HEIGHT * 2
|
||||
end
|
||||
# factor_x = ((2*params[0])/fullgamew).floor
|
||||
# factor_y = ((2*params[1])/fullgameh).floor
|
||||
# factor = [factor_x,factor_y].min/2.0
|
||||
factor_x = (params[0]/fullgamew).floor
|
||||
factor_y = (params[1]/fullgameh).floor
|
||||
factor = [factor_x,factor_y].min
|
||||
offset_x = (params[0]-gamew*factor)/(2*factor)
|
||||
offset_y = (params[1]-gameh*factor)/(2*factor)
|
||||
$ResizeOffsetX = offset_x
|
||||
$ResizeOffsetY = offset_y
|
||||
ObjectSpace.each_object(Viewport) { |o|
|
||||
begin
|
||||
next if o.rect.nil?
|
||||
ox = o.rect.x-$ResizeOffsetX
|
||||
oy = o.rect.y-$ResizeOffsetY
|
||||
o.rect.x = ox+offset_x
|
||||
o.rect.y = oy+offset_y
|
||||
rescue RGSSError
|
||||
end
|
||||
}
|
||||
pbSetResizeFactor2(factor,true)
|
||||
end
|
||||
|
||||
def pbConfigureWindowedScreen(value)
|
||||
border = $PokemonSystem ? $PokemonSystem.border : 0
|
||||
$ResizeOffsetX = [0,BORDER_WIDTH][border]
|
||||
$ResizeOffsetY = [0,BORDER_HEIGHT][border]
|
||||
pbSetResizeFactor2(value,true)
|
||||
Win32API.restoreScreen
|
||||
end
|
||||
|
||||
def setScreenBorderName(border)
|
||||
if !$HaveResizeBorder
|
||||
$ResizeBorder = ScreenBorder.new
|
||||
$HaveResizeBorder = true
|
||||
end
|
||||
$ResizeBorder.bordername = border if $ResizeBorder
|
||||
end
|
||||
|
||||
|
||||
|
||||
module Graphics
|
||||
## Nominal screen size
|
||||
@@width = SCREEN_WIDTH
|
||||
@@height = SCREEN_HEIGHT
|
||||
|
||||
def self.width
|
||||
return @@width.to_i
|
||||
end
|
||||
|
||||
def self.height
|
||||
return @@height.to_i
|
||||
end
|
||||
|
||||
@@fadeoutvp = Viewport.new(0,0,640,480)
|
||||
@@fadeoutvp.z = 0x3FFFFFFF
|
||||
@@fadeoutvp.color = Color.new(0,0,0,0)
|
||||
|
||||
def self.brightness
|
||||
return 255-@@fadeoutvp.color.alpha
|
||||
end
|
||||
|
||||
def self.brightness=(value)
|
||||
value = 0 if value<0
|
||||
value = 255 if value>255
|
||||
@@fadeoutvp.color.alpha = 255-value
|
||||
end
|
||||
|
||||
def self.fadein(frames)
|
||||
return if frames<=0
|
||||
curvalue = self.brightness
|
||||
count = (255-self.brightness)
|
||||
frames.times do |i|
|
||||
self.brightness = curvalue+(count*i/frames)
|
||||
self.update
|
||||
end
|
||||
end
|
||||
|
||||
def self.wait(frames)
|
||||
return if frames<=0
|
||||
frames.times do |i|
|
||||
self.update
|
||||
end
|
||||
end
|
||||
|
||||
def self.fadeout(frames)
|
||||
return if frames<=0
|
||||
curvalue = self.brightness
|
||||
count = self.brightness
|
||||
frames.times do |i|
|
||||
self.brightness = curvalue-(count*i/frames)
|
||||
self.update
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
begin
|
||||
x = @@haveresizescreen
|
||||
rescue NameError # If exception is caught, the class
|
||||
if !method_defined?(:oldresizescreen) # variable wasn't defined yet
|
||||
begin
|
||||
alias oldresizescreen resize_screen
|
||||
@@haveresizescreen = true
|
||||
rescue
|
||||
@@haveresizescreen = false
|
||||
end
|
||||
else
|
||||
@@haveresizescreen = false
|
||||
end
|
||||
end
|
||||
|
||||
def haveresizescreen
|
||||
@@haveresizescreen
|
||||
end
|
||||
end
|
||||
|
||||
def self.resize_screen(w,h)
|
||||
@@width = w
|
||||
@@height = h
|
||||
pbSetResizeFactor($ResizeFactor,true)
|
||||
end
|
||||
|
||||
@@deletefailed = false
|
||||
|
||||
def self.snap_to_bitmap(resize=true)
|
||||
tempPath = ENV["TEMP"]+"\\tempscreen.bmp"
|
||||
if safeExists?(tempPath) && @@deletefailed
|
||||
begin
|
||||
File.delete(tempPath)
|
||||
@@deletefailed = false
|
||||
rescue Errno::EACCES
|
||||
@@deletefailed = true
|
||||
return nil
|
||||
end
|
||||
end
|
||||
if safeExists?("./rubyscreen.dll")
|
||||
takescreen = Win32API.new("rubyscreen.dll","TakeScreenshot","p","i")
|
||||
takescreen.call(tempPath)
|
||||
end
|
||||
bm = nil
|
||||
if safeExists?(tempPath)
|
||||
bm = Bitmap.new(tempPath)
|
||||
begin
|
||||
File.delete(tempPath)
|
||||
@@deletefailed = false
|
||||
rescue Errno::EACCES
|
||||
@@deletefailed = true
|
||||
end
|
||||
end
|
||||
bm.asOpaque if bm && bm.get_pixel(0,0).alpha==0
|
||||
if resize
|
||||
if bm && $ResizeOffsetX && $ResizeOffsetY && ($ResizeOffsetX!=0 || $ResizeOffsetY!=0)
|
||||
tmpbitmap = Bitmap.new(Graphics.width*$ResizeFactor,Graphics.height*$ResizeFactor)
|
||||
tmpbitmap.blt(0,0,bm,Rect.new(
|
||||
$ResizeOffsetX*$ResizeFactor,$ResizeOffsetY*$ResizeFactor,tmpbitmap.width,tmpbitmap.height))
|
||||
bm.dispose
|
||||
bm = tmpbitmap
|
||||
end
|
||||
if bm && (bm.width!=Graphics.width || bm.height!=Graphics.height)
|
||||
newbitmap = Bitmap.new(Graphics.width,Graphics.height)
|
||||
newbitmap.stretch_blt(newbitmap.rect,bm,Rect.new(0,0,bm.width,bm.height))
|
||||
bm.dispose
|
||||
bm = newbitmap
|
||||
end
|
||||
else
|
||||
# Thise code is used only for taking screenshots with F8.
|
||||
# Doesn't crop out the screen border, doesn't normalise to 1x zoom.
|
||||
# Fixes screenshots being 1 pixel too tall.
|
||||
fullw = (Graphics.width+$ResizeOffsetX*2)*$ResizeFactor
|
||||
fullh = (Graphics.height+$ResizeOffsetY*2)*$ResizeFactor
|
||||
if bm && $ResizeOffsetX && $ResizeOffsetY && $ResizeFactor &&
|
||||
(bm.width!=fullw || bm.height!=fullh)
|
||||
tmpbitmap = Bitmap.new(fullw,fullh)
|
||||
tmpbitmap.blt(0,0,bm,Rect.new(0,0,fullw,fullh))
|
||||
bm.dispose
|
||||
bm = tmpbitmap
|
||||
end
|
||||
end
|
||||
return bm
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Sprite
|
||||
unless @SpriteResizerMethodsAliased
|
||||
alias _initialize_SpriteResizer initialize
|
||||
alias _x_SpriteResizer x
|
||||
alias _y_SpriteResizer y
|
||||
alias _ox_SpriteResizer ox
|
||||
alias _oy_SpriteResizer oy
|
||||
alias _zoomx_SpriteResizer zoom_x
|
||||
alias _zoomy_SpriteResizer zoom_y
|
||||
alias _xeq_SpriteResizer x=
|
||||
alias _yeq_SpriteResizer y=
|
||||
alias _oxeq_SpriteResizer ox=
|
||||
alias _oyeq_SpriteResizer oy=
|
||||
alias _zoomxeq_SpriteResizer zoom_x=
|
||||
alias _zoomyeq_SpriteResizer zoom_y=
|
||||
alias _bushdeptheq_SpriteResizer bush_depth=
|
||||
@SpriteResizerMethodsAliased = true
|
||||
end
|
||||
|
||||
def initialize(viewport=nil)
|
||||
_initialize_SpriteResizer(viewport)
|
||||
@resizedX=0
|
||||
@resizedY=0
|
||||
@resizedOx=0
|
||||
@resizedOy=0
|
||||
@resizedBushDepth=0
|
||||
@resizedZoomX=1.0
|
||||
@resizedZoomY=1.0
|
||||
if $ResizeOffsetX!=0 && $ResizeOffsetY!=0 && !viewport
|
||||
_xeq_SpriteResizer($ResizeOffsetX.to_f*$ResizeFactorMul/100)
|
||||
_yeq_SpriteResizer($ResizeOffsetY.to_f*$ResizeFactorMul/100)
|
||||
end
|
||||
_zoomxeq_SpriteResizer(@resizedZoomX*$ResizeFactorMul/100)
|
||||
_zoomyeq_SpriteResizer(@resizedZoomY*$ResizeFactorMul/100)
|
||||
end
|
||||
|
||||
def x
|
||||
return @resizedX
|
||||
end
|
||||
|
||||
def y
|
||||
return @resizedY
|
||||
end
|
||||
|
||||
def x=(val)
|
||||
if $ResizeFactorMul!=100
|
||||
offset=(self.viewport) ? 0 : $ResizeOffsetX
|
||||
value=(val+offset).to_f*$ResizeFactorMul/100
|
||||
_xeq_SpriteResizer(value.to_i)
|
||||
@resizedX=val.to_i
|
||||
elsif self.viewport
|
||||
_xeq_SpriteResizer(val)
|
||||
@resizedX=val
|
||||
else
|
||||
_xeq_SpriteResizer(val + $ResizeOffsetX)
|
||||
@resizedX=val
|
||||
end
|
||||
end
|
||||
|
||||
def y=(val)
|
||||
if $ResizeFactorMul!=100
|
||||
offset=(self.viewport) ? 0 : $ResizeOffsetY
|
||||
value=(val+offset).to_f*$ResizeFactorMul/100
|
||||
_yeq_SpriteResizer(value.to_i)
|
||||
@resizedY=val.to_i
|
||||
elsif self.viewport
|
||||
_yeq_SpriteResizer(val)
|
||||
@resizedY=val
|
||||
else
|
||||
_yeq_SpriteResizer(val + $ResizeOffsetY)
|
||||
@resizedY=val
|
||||
end
|
||||
end
|
||||
|
||||
def ox
|
||||
return @resizedOx
|
||||
end
|
||||
|
||||
def oy
|
||||
return @resizedOy
|
||||
end
|
||||
|
||||
def ox=(val)
|
||||
@resizedOx=val
|
||||
_oxeq_SpriteResizer(val)
|
||||
end
|
||||
|
||||
def oy=(val)
|
||||
@resizedOy=val
|
||||
_oyeq_SpriteResizer(val)
|
||||
end
|
||||
|
||||
def zoom_x
|
||||
return @resizedZoomX
|
||||
end
|
||||
|
||||
def zoom_y
|
||||
return @resizedZoomY
|
||||
end
|
||||
|
||||
def zoom_x=(val)
|
||||
value=val
|
||||
if $ResizeFactorMul!=100
|
||||
value=(val.to_f*$ResizeFactorMul/100)
|
||||
if (value-0.5).abs<=0.001
|
||||
value=0.5
|
||||
elsif (value-1.0).abs<=0.001
|
||||
value=1.0
|
||||
elsif (value-1.5).abs<=0.001
|
||||
value=1.5
|
||||
elsif (value-2.0).abs<=0.001
|
||||
value=2.0
|
||||
end
|
||||
end
|
||||
_zoomxeq_SpriteResizer(value)
|
||||
@resizedZoomX=val
|
||||
end
|
||||
|
||||
def zoom_y=(val)
|
||||
value=val
|
||||
if $ResizeFactorMul!=100
|
||||
value=(val.to_f*$ResizeFactorMul/100)
|
||||
if (value-0.5).abs<=0.001
|
||||
value=0.5
|
||||
elsif (value-1.0).abs<=0.001
|
||||
value=1.0
|
||||
elsif (value-1.5).abs<=0.001
|
||||
value=1.5
|
||||
elsif (value-2.0).abs<=0.001
|
||||
value=2.0
|
||||
end
|
||||
end
|
||||
_zoomyeq_SpriteResizer(value)
|
||||
@resizedZoomY=val
|
||||
end
|
||||
|
||||
def bush_depth
|
||||
return @resizedBushDepth
|
||||
end
|
||||
|
||||
def bush_depth=(val)
|
||||
value=((val.to_i)*$ResizeFactorMul/100)
|
||||
_bushdeptheq_SpriteResizer(value.to_i)
|
||||
@resizedBushDepth=val.to_i
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class NotifiableRect < Rect
|
||||
def setNotifyProc(proc)
|
||||
@notifyProc = proc
|
||||
end
|
||||
|
||||
def set(x,y,width,height)
|
||||
super
|
||||
@notifyProc.call(self) if @notifyProc
|
||||
end
|
||||
|
||||
def x=(value)
|
||||
super
|
||||
@notifyProc.call(self) if @notifyProc
|
||||
end
|
||||
|
||||
def y=(value)
|
||||
super
|
||||
@notifyProc.call(self) if @notifyProc
|
||||
end
|
||||
|
||||
def width=(value)
|
||||
super
|
||||
@notifyProc.call(self) if @notifyProc
|
||||
end
|
||||
|
||||
def height=(value)
|
||||
super
|
||||
@notifyProc.call(self) if @notifyProc
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Viewport
|
||||
unless @SpriteResizerMethodsAliased
|
||||
alias _initialize_SpriteResizer initialize
|
||||
alias _rect_ViewportResizer rect
|
||||
alias _recteq_SpriteResizer rect=
|
||||
alias _oxeq_SpriteResizer ox=
|
||||
alias _oyeq_SpriteResizer oy=
|
||||
@SpriteResizerMethodsAliased=true
|
||||
end
|
||||
|
||||
def initialize(*arg)
|
||||
args=arg.clone
|
||||
@oldrect=Rect.new(0,0,100,100)
|
||||
_initialize_SpriteResizer(@oldrect)
|
||||
newRect=NotifiableRect.new(0,0,0,0)
|
||||
@resizedRectProc=Proc.new { |r|
|
||||
if $ResizeFactorMul==100
|
||||
@oldrect.set(
|
||||
r.x.to_i+$ResizeOffsetX,
|
||||
r.y.to_i+$ResizeOffsetY,
|
||||
r.width.to_i,
|
||||
r.height.to_i
|
||||
)
|
||||
self._recteq_SpriteResizer(@oldrect)
|
||||
else
|
||||
@oldrect.set(
|
||||
((r.x+$ResizeOffsetX)*$ResizeFactorMul/100).to_i,
|
||||
((r.y+$ResizeOffsetY)*$ResizeFactorMul/100).to_i,
|
||||
(r.width*$ResizeFactorMul/100).to_i,
|
||||
(r.height*$ResizeFactorMul/100).to_i
|
||||
)
|
||||
self._recteq_SpriteResizer(@oldrect)
|
||||
end
|
||||
}
|
||||
newRect.setNotifyProc(@resizedRectProc)
|
||||
if arg.length==1
|
||||
newRect.set(args[0].x,args[0].y,args[0].width,args[0].height)
|
||||
else
|
||||
newRect.set(args[0],args[1],args[2],args[3])
|
||||
end
|
||||
@resizedRect=newRect
|
||||
@resizedOx=0
|
||||
@resizedOy=0
|
||||
end
|
||||
|
||||
def ox
|
||||
return @resizedOx
|
||||
end
|
||||
|
||||
def ox=(val)
|
||||
return if !val
|
||||
_oxeq_SpriteResizer(val.to_f*$ResizeFactorMul/100)
|
||||
@resizedOx=val
|
||||
end
|
||||
|
||||
def oy
|
||||
return @resizedOy
|
||||
end
|
||||
|
||||
def oy=(val)
|
||||
return if !val
|
||||
_oyeq_SpriteResizer(val.to_f*$ResizeFactorMul/100)
|
||||
@resizedOy=val
|
||||
end
|
||||
|
||||
def rect
|
||||
return @resizedRect
|
||||
end
|
||||
|
||||
def rect=(val)
|
||||
if val
|
||||
newRect=NotifiableRect.new(0,0,100,100)
|
||||
newRect.setNotifyProc(@resizedRectProc)
|
||||
newRect.set(val.x.to_i,val.y.to_i,val.width.to_i,val.height.to_i)
|
||||
@resizedRect=newRect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Plane
|
||||
unless @SpriteResizerMethodsAliased
|
||||
alias _initialize_SpriteResizer initialize
|
||||
alias _zoomxeq_SpriteResizer zoom_x=
|
||||
alias _zoomyeq_SpriteResizer zoom_y=
|
||||
alias _oxeq_SpriteResizer ox=
|
||||
alias _oyeq_SpriteResizer oy=
|
||||
@SpriteResizerMethodsAliased=true
|
||||
end
|
||||
|
||||
def initialize(viewport=nil)
|
||||
_initialize_SpriteResizer(viewport)
|
||||
@resizedZoomX=1.0
|
||||
@resizedZoomY=1.0
|
||||
@resizedOx=0
|
||||
@resizedOy=0
|
||||
_zoomxeq_SpriteResizer(@resizedZoomX*$ResizeFactorMul/100)
|
||||
_zoomyeq_SpriteResizer(@resizedZoomY*$ResizeFactorMul/100)
|
||||
end
|
||||
|
||||
def ox
|
||||
return @resizedOx
|
||||
end
|
||||
|
||||
def ox=(val)
|
||||
return if !val
|
||||
_oxeq_SpriteResizer(val.to_f*$ResizeFactorMul/100)
|
||||
@resizedOx=val
|
||||
end
|
||||
|
||||
def oy
|
||||
return @resizedOy
|
||||
end
|
||||
|
||||
def oy=(val)
|
||||
return if !val
|
||||
_oyeq_SpriteResizer(val.to_f*$ResizeFactorMul/100)
|
||||
@resizedOy=val
|
||||
end
|
||||
|
||||
def zoom_x
|
||||
return @resizedZoomX
|
||||
end
|
||||
|
||||
def zoom_x=(val)
|
||||
return if !val
|
||||
_zoomxeq_SpriteResizer(val*$ResizeFactorMul/100)
|
||||
@resizedZoomX=val
|
||||
end
|
||||
|
||||
def zoom_y
|
||||
return @resizedZoomY
|
||||
end
|
||||
|
||||
def zoom_y=(val)
|
||||
return if !val
|
||||
_zoomyeq_SpriteResizer(val*$ResizeFactorMul/100)
|
||||
@resizedZoomY=val
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
class ScreenBorder
|
||||
def initialize
|
||||
initializeInternal
|
||||
refresh
|
||||
end
|
||||
|
||||
def initializeInternal
|
||||
@maximumZ=500000
|
||||
@bordername=""
|
||||
@sprite=IconSprite.new(0,0) rescue Sprite.new
|
||||
@defaultwidth=640
|
||||
@defaultheight=480
|
||||
@defaultbitmap=Bitmap.new(@defaultwidth,@defaultheight)
|
||||
end
|
||||
|
||||
def dispose
|
||||
@borderbitmap.dispose if @borderbitmap
|
||||
@defaultbitmap.dispose
|
||||
@sprite.dispose
|
||||
end
|
||||
|
||||
def adjustZ(z)
|
||||
if z>=@maximumZ
|
||||
@maximumZ=z+1
|
||||
@sprite.z=@maximumZ
|
||||
end
|
||||
end
|
||||
|
||||
def bordername=(value)
|
||||
@bordername=value
|
||||
refresh
|
||||
end
|
||||
|
||||
def refresh
|
||||
@sprite.z=@maximumZ
|
||||
@sprite.x=-BORDER_WIDTH
|
||||
@sprite.y=-BORDER_HEIGHT
|
||||
@sprite.visible=($PokemonSystem && $PokemonSystem.border==1)
|
||||
@sprite.bitmap=nil
|
||||
if @sprite.visible
|
||||
if @bordername!=nil && @bordername!=""
|
||||
setSpriteBitmap("Graphics/Pictures/"+@bordername)
|
||||
else
|
||||
setSpriteBitmap(nil)
|
||||
@sprite.bitmap=@defaultbitmap
|
||||
end
|
||||
end
|
||||
@defaultbitmap.clear
|
||||
@defaultbitmap.fill_rect(0,0,@defaultwidth,$ResizeOffsetY,Color.new(0,0,0))
|
||||
@defaultbitmap.fill_rect(0,$ResizeOffsetY,
|
||||
$ResizeOffsetX,@defaultheight-$ResizeOffsetY,Color.new(0,0,0))
|
||||
@defaultbitmap.fill_rect(@defaultwidth-$ResizeOffsetX,$ResizeOffsetY,
|
||||
$ResizeOffsetX,@defaultheight-$ResizeOffsetY,Color.new(0,0,0))
|
||||
@defaultbitmap.fill_rect($ResizeOffsetX,@defaultheight-$ResizeOffsetY,
|
||||
@defaultwidth-$ResizeOffsetX*2,$ResizeOffsetY,Color.new(0,0,0))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setSpriteBitmap(x)
|
||||
if (@sprite.is_a?(IconSprite) rescue false)
|
||||
@sprite.setBitmap(x)
|
||||
else
|
||||
@sprite.bitmap=x ? RPG::Cache.load_bitmap("",x) : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Bitmap
|
||||
# Fast methods for retrieving bitmap data
|
||||
RtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
|
||||
RtlMoveMemory_ip = Win32API.new('kernel32', 'RtlMoveMemory', 'ipi', 'i')
|
||||
SwapRgb = Win32API.new('./rubyscreen.dll', 'SwapRgb', 'pi', '') rescue nil
|
||||
|
||||
def setData(x)
|
||||
RtlMoveMemory_ip.call(self.address, x, x.length)
|
||||
end
|
||||
|
||||
def getData
|
||||
data = "rgba" * width * height
|
||||
RtlMoveMemory_pi.call(data, self.address, data.length)
|
||||
return data
|
||||
end
|
||||
|
||||
def swap32(x)
|
||||
return ((x>>24)&0x000000FF)|
|
||||
((x>>8)&0x0000FF00)|
|
||||
((x<<8)&0x00FF0000)|
|
||||
((x<<24)&0xFF000000)
|
||||
end
|
||||
|
||||
def asOpaque
|
||||
data=getData
|
||||
j=3
|
||||
for i in 0...width*height
|
||||
data[j]=0xFF
|
||||
j+=4
|
||||
end
|
||||
setData(data)
|
||||
end
|
||||
|
||||
def saveToPng(filename)
|
||||
bytes=[
|
||||
0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A,0x00,0x00,0x00,0x0D
|
||||
].pack("CCCCCCCCCCCC")
|
||||
ihdr=[
|
||||
0x49,0x48,0x44,0x52,swap32(self.width),swap32(self.height),
|
||||
0x08,0x06,0x00,0x00,0x00
|
||||
].pack("CCCCVVCCCCC")
|
||||
crc=Zlib::crc32(ihdr)
|
||||
ihdr+=[swap32(crc)].pack("V")
|
||||
bytesPerScan=self.width*4
|
||||
row=(self.height-1)*bytesPerScan
|
||||
data=self.getData
|
||||
data2=data.clone
|
||||
width=self.width
|
||||
x=""
|
||||
len=bytesPerScan*self.height
|
||||
ttt=Time.now
|
||||
if SwapRgb
|
||||
SwapRgb.call(data2,data2.length)
|
||||
else
|
||||
# the following is considerably slower
|
||||
b=0;c=2;while b!=len
|
||||
data2[b]=data[c]
|
||||
data2[c]=data[b]
|
||||
b+=4;c+=4;
|
||||
end
|
||||
end
|
||||
#$times.push(Time.now-ttt)
|
||||
filter="\0"
|
||||
while row>=0
|
||||
thisRow=data2[row,bytesPerScan]
|
||||
x.concat(filter)
|
||||
x.concat(thisRow)
|
||||
row-=bytesPerScan
|
||||
end
|
||||
x=Zlib::Deflate.deflate(x)
|
||||
length=x.length
|
||||
x="IDAT"+x
|
||||
crc=Zlib::crc32(x)
|
||||
idat=[swap32(length)].pack("V")
|
||||
idat.concat(x)
|
||||
idat.concat([swap32(crc)].pack("V"))
|
||||
idat.concat([0,0x49,0x45,0x4E,0x44,0xAE,0x42,0x60,0x82].pack("VCCCCCCCC"))
|
||||
File.open(filename,"wb") { |f|
|
||||
f.write(bytes)
|
||||
f.write(ihdr)
|
||||
f.write(idat)
|
||||
}
|
||||
end
|
||||
|
||||
def address
|
||||
if !@address
|
||||
buffer, ad = "rgba", object_id * 2 + 16
|
||||
RtlMoveMemory_pi.call(buffer, ad, 4)
|
||||
ad = buffer.unpack("L")[0] + 8
|
||||
RtlMoveMemory_pi.call(buffer, ad, 4)
|
||||
ad = buffer.unpack("L")[0] + 16
|
||||
RtlMoveMemory_pi.call(buffer, ad, 4)
|
||||
@address=buffer.unpack("L")[0]
|
||||
end
|
||||
return @address
|
||||
end
|
||||
end
|
||||
376
Data/Scripts/001_Technical/008_Plugin_Manager.rb
Normal file
376
Data/Scripts/001_Technical/008_Plugin_Manager.rb
Normal file
@@ -0,0 +1,376 @@
|
||||
#==============================================================================#
|
||||
# Plugin Manager #
|
||||
# by Marin #
|
||||
#------------------------------------------------------------------------------#
|
||||
# Provides a simple interface that allows plugins to require dependencies #
|
||||
# at specific versions, and to specify incompatibilities between plugins. #
|
||||
#------------------------------------------------------------------------------#
|
||||
# Usage: #
|
||||
# #
|
||||
# A Pokémon Essentials plugin should register itself using the PluginManager. #
|
||||
# The simplest way to do so, for a plugin without dependencies, is as follows: #
|
||||
# #
|
||||
# PluginManager.register({ #
|
||||
# :name => "Basic Plugin", #
|
||||
# :version => "1.0", #
|
||||
# :link => "https://reliccastle.com/link-to-the-plugin/", #
|
||||
# :credits => "Marin" #
|
||||
# }) #
|
||||
# #
|
||||
# The link portion here is optional, but recommended. This will be shown in #
|
||||
# the error message if the PluginManager detects that this plugin needs to be #
|
||||
# updated. #
|
||||
# #
|
||||
# A plugin's version is typically in the format X.Y.Z, but the number of #
|
||||
# digits does not matter. You can also use Xa, Xb, Xc, Ya, etc. #
|
||||
# What matters is that you use it consistently, so that it can be compared. #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# Now let's say we create a new plugin titled "Simple Extension", which #
|
||||
# requires our previously created "Basic Plugin" to work. #
|
||||
# #
|
||||
# PluginManager.register({ #
|
||||
# :name => "Simple Extension", #
|
||||
# :version => "1.0", #
|
||||
# :link => "https://reliccastle.com/link-to-the-plugin/", #
|
||||
# :credits => ["Marin", "Maruno"], #
|
||||
# :dependencies => ["Basic Plugin"] #
|
||||
# }) #
|
||||
# #
|
||||
# This plugin has two credits as an array, instead of one string. Furthermore, #
|
||||
# this code will ensure that "Basic Plugin" is installed, ignoring its #
|
||||
# version. If you have only one dependency, you can omit the array brackets #
|
||||
# like so: #
|
||||
# #
|
||||
# :dependencies => "Basic Plugin" #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# To require a minimum version of a dependency plugin, you should turn the #
|
||||
# dependency's name into an array which contains the name and the version #
|
||||
# (both as strings). For example, to require "Basic Plugin" version 1.2 or #
|
||||
# higher, you would write: #
|
||||
# #
|
||||
# PluginManager.register({ #
|
||||
# :name => "Simple Extension", #
|
||||
# :version => "1.0", #
|
||||
# :link => "https://reliccastle.com/link-to-the-plugin/", #
|
||||
# :credits => "Marin", #
|
||||
# :dependencies => [ #
|
||||
# ["Basic Plugin", "1.2"] #
|
||||
# ] #
|
||||
# }) #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# To require a specific version (no higher and no lower) of a dependency #
|
||||
# plugin, you should add the :exact flag as the first thing in the array for #
|
||||
# that dependency: #
|
||||
# #
|
||||
# PluginManager.register({ #
|
||||
# :name => "Simple Extension", #
|
||||
# :version => "1.0", #
|
||||
# :link => "https://reliccastle.com/link-to-the-plugin/", #
|
||||
# :credits => "Marin", #
|
||||
# :dependencies => [ #
|
||||
# [:exact, "Basic Plugin", "1.2"] #
|
||||
# ] #
|
||||
# }) #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# If your plugin is known to be incompatible with another plugin, you should #
|
||||
# list that other plugin as such. Only one of the two plugins needs to list #
|
||||
# that it is incompatible with the other. #
|
||||
# #
|
||||
# PluginManager.register({ #
|
||||
# :name => "QoL Improvements", #
|
||||
# :version => "1.0", #
|
||||
# :link => "https://reliccastle.com/link-to-the-plugin/", #
|
||||
# :credits => "Marin", #
|
||||
# :incompatibilities => [ #
|
||||
# "Simple Extension" #
|
||||
# ] #
|
||||
# }) #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# If your plugin can work without another plugin, but is known to be #
|
||||
# incompatible with an old version of that other plugin, you should list it as #
|
||||
# an optional dependency. If that other plugin is present in a game, then this #
|
||||
# optional dependency will ensure it meets the minimum version required for #
|
||||
# your plugin. Write it in the same way as any other dependency as described #
|
||||
# above, but use the :optional flag instead. #
|
||||
# You do not need to list a plugin as an optional dependency at all if all #
|
||||
# versions of that other plugin are compatible with your plugin. #
|
||||
# #
|
||||
# PluginManager.register({ #
|
||||
# :name => "Other Plugin", #
|
||||
# :version => "1.0", #
|
||||
# :link => "https://reliccastle.com/link-to-the-plugin/", #
|
||||
# :credits => "Marin", #
|
||||
# :dependencies => [ #
|
||||
# [:optional, "QoL Improvements", "1.1"] #
|
||||
# ] #
|
||||
# }) #
|
||||
# #
|
||||
# The :optional_exact flag is a combination of :optional and :exact. #
|
||||
#------------------------------------------------------------------------------#
|
||||
# Please give credit when using this. #
|
||||
#==============================================================================#
|
||||
|
||||
module PluginManager
|
||||
# Win32API MessageBox function for custom errors.
|
||||
MBOX = Win32API.new('user32', 'MessageBox', ['I','P','P','I'], 'I')
|
||||
# Holds all registered plugin data.
|
||||
@@Plugins = {}
|
||||
|
||||
# Registers a plugin and tests its dependencies and incompatibilities.
|
||||
def self.register(options)
|
||||
name = nil
|
||||
version = nil
|
||||
link = nil
|
||||
dependencies = nil
|
||||
incompats = nil
|
||||
credits = []
|
||||
order = [:name, :version, :link, :dependencies, :incompatibilities, :credits]
|
||||
# Ensure it first reads the plugin's name, which is used in error reporting,
|
||||
# by sorting the keys
|
||||
keys = options.keys.sort do |a, b|
|
||||
idx_a = order.index(a)
|
||||
idx_a = order.size if idx_a == -1
|
||||
idx_b = order.index(b)
|
||||
idx_b = order.size if idx_b == -1
|
||||
next idx_a <=> idx_b
|
||||
end
|
||||
for key in keys
|
||||
value = options[key]
|
||||
case key
|
||||
when :name # Plugin name
|
||||
if nil_or_empty?(value)
|
||||
self.error("Plugin name must be a non-empty string.")
|
||||
end
|
||||
if !@@Plugins[value].nil?
|
||||
self.error("A plugin called '#{value}' already exists.")
|
||||
end
|
||||
name = value
|
||||
when :version # Plugin version
|
||||
if nil_or_empty?(value)
|
||||
self.error("Plugin version must be a string.")
|
||||
end
|
||||
version = value
|
||||
when :link # Plugin website
|
||||
if nil_or_empty?(value)
|
||||
self.error("Plugin link must be a non-empty string.")
|
||||
end
|
||||
link = value
|
||||
when :dependencies # Plugin dependencies
|
||||
dependencies = value
|
||||
dependencies = [dependencies] if !dependencies.is_a?(Array) || !dependencies[0].is_a?(Array)
|
||||
for dep in value
|
||||
if dep.is_a?(String) # "plugin name"
|
||||
if !self.installed?(dep)
|
||||
self.error("Plugin '#{name}' requires plugin '#{dep}' to be installed above it.")
|
||||
end
|
||||
elsif dep.is_a?(Array)
|
||||
case dep.size
|
||||
when 1 # ["plugin name"]
|
||||
if dep[0].is_a?(String)
|
||||
dep_name = dep[0]
|
||||
if !self.installed?(dep_name)
|
||||
self.error("Plugin '#{name}' requires plugin '#{dep_name}' to be installed above it.")
|
||||
end
|
||||
else
|
||||
self.error("Expected the plugin name as a string, but got #{dep[0].inspect}.")
|
||||
end
|
||||
when 2 # ["plugin name", "version"]
|
||||
if dep[0].is_a?(Symbol)
|
||||
self.error("A plugin version comparator symbol was given but no version was given.")
|
||||
elsif dep[0].is_a?(String) && dep[1].is_a?(String)
|
||||
dep_name = dep[0]
|
||||
dep_version = dep[1]
|
||||
next if self.installed?(dep_name, dep_version)
|
||||
if self.installed?(dep_name) # Have plugin but lower version
|
||||
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} or higher, " +
|
||||
"but the installed version is #{self.version(dep_name)}."
|
||||
if dep_link = self.link(dep_name)
|
||||
msg += "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
|
||||
end
|
||||
self.error(msg)
|
||||
else # Don't have plugin
|
||||
self.error("Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} " +
|
||||
"or higher to be installed above it.")
|
||||
end
|
||||
end
|
||||
when 3 # [:optional/:exact/:optional_exact, "plugin name", "version"]
|
||||
if !dep[0].is_a?(Symbol)
|
||||
self.error("Expected first dependency argument to be a symbol, but got #{dep[0].inspect}.")
|
||||
end
|
||||
if !dep[1].is_a?(String)
|
||||
self.error("Expected second dependency argument to be a plugin name, but got #{dep[1].inspect}.")
|
||||
end
|
||||
if !dep[2].is_a?(String)
|
||||
self.error("Expected third dependency argument to be the plugin version, but got #{dep[2].inspect}.")
|
||||
end
|
||||
dep_arg = dep[0]
|
||||
dep_name = dep[1]
|
||||
dep_version = dep[2]
|
||||
optional = false
|
||||
exact = false
|
||||
case def_arg
|
||||
when :optional; optional = true
|
||||
when :exact; exact = true
|
||||
when :optional_exact; optional = true; exact = true
|
||||
else
|
||||
self.error("Expected first dependency argument to be one of " +
|
||||
":optional, :exact or :optional_exact, but got #{dep_arg.inspect}.")
|
||||
end
|
||||
if optional
|
||||
if self.installed?(dep_name) && # Have plugin but lower version
|
||||
!self.installed?(dep_name, dep_version, exact)
|
||||
msg = "Plugin '#{name}' requires plugin '#{dep_name}', if installed, to be version #{dep_version}"
|
||||
msg << " or higher" if !exact
|
||||
msg << ", but the installed version was #{self.version(dep_name)}."
|
||||
if dep_link = self.link(dep_name)
|
||||
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
|
||||
end
|
||||
self.error(msg)
|
||||
end
|
||||
elsif !self.installed?(dep_name, dep_version, exact)
|
||||
if self.installed?(dep_name) # Have plugin but lower version
|
||||
msg = "Plugin '#{name}' requires plugin '#{dep_name}' to be version #{dep_version}"
|
||||
msg << " or later" if !exact
|
||||
msg << ", but the installed version was #{self.version(dep_name)}."
|
||||
if dep_link = self.link(dep_name)
|
||||
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
|
||||
end
|
||||
self.error(msg)
|
||||
else # Don't have plugin
|
||||
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} "
|
||||
msg << "or later" if !exact
|
||||
msg << "to be installed above it."
|
||||
self.error(msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
when :incompatibilities # Plugin incompatibilities
|
||||
incompats = value
|
||||
incompats = [incompats] if !incompats.is_a?(Array)
|
||||
for incompat in incompats
|
||||
if self.installed?(incompat)
|
||||
self.error("Plugin '#{name}' is incompatible with '#{incompat}'. " +
|
||||
"They cannot both be used at the same time.")
|
||||
end
|
||||
end
|
||||
when :credits # Plugin credits
|
||||
value = [value] if value.is_a?(String)
|
||||
if value.is_a?(Array)
|
||||
for entry in value
|
||||
if !entry.is_a?(String)
|
||||
self.error("Plugin '#{name}'s credits array contains a non-string value.")
|
||||
else
|
||||
credits << entry
|
||||
end
|
||||
end
|
||||
else
|
||||
self.error("Plugin '#{name}'s credits field must contain a string, or a string array.")
|
||||
end
|
||||
else
|
||||
self.error("Invalid plugin registry key '#{key}'.")
|
||||
end
|
||||
end
|
||||
for plugin in @@Plugins.values
|
||||
if plugin[:incompatibilities] && plugin[:incompatibilities].include?(name)
|
||||
self.error("Plugin '#{plugin[:name]}' is incompatible with '#{name}'. " +
|
||||
"They cannot both be used at the same time.")
|
||||
end
|
||||
end
|
||||
# Add plugin to class variable
|
||||
@@Plugins[name] = {
|
||||
:name => name,
|
||||
:version => version,
|
||||
:link => link,
|
||||
:dependencies => dependencies,
|
||||
:incompatibilities => incompats,
|
||||
:credits => credits
|
||||
}
|
||||
end
|
||||
|
||||
# Throws a pure error message without stack trace or any other useless info.
|
||||
def self.error(msg)
|
||||
Graphics.update
|
||||
t = Thread.new do
|
||||
MBOX.call(Win32API.pbFindRgssWindow, msg, "Plugin Error", 0x10)
|
||||
Thread.exit
|
||||
end
|
||||
while t.status
|
||||
Graphics.update
|
||||
end
|
||||
Kernel.exit! true
|
||||
end
|
||||
|
||||
# Returns true if the specified plugin is installed.
|
||||
# If the version is specified, this version is taken into account.
|
||||
# If mustequal is true, the version must be a match with the specified version.
|
||||
def self.installed?(plugin_name, plugin_version = nil, mustequal = false)
|
||||
plugin = @@Plugins[plugin_name]
|
||||
return false if plugin.nil?
|
||||
return true if plugin_version.nil?
|
||||
comparison = compare_versions(plugin[:version], plugin_version)
|
||||
return true if !mustequal && comparison >= 0
|
||||
return true if mustequal && comparison == 0
|
||||
end
|
||||
|
||||
# Returns the string names of all installed plugins.
|
||||
def self.plugins
|
||||
return @@Plugins.keys
|
||||
end
|
||||
|
||||
# Returns the installed version of the specified plugin.
|
||||
def self.version(plugin_name)
|
||||
return if !installed?(plugin_name)
|
||||
return @@Plugins[plugin_name][:version]
|
||||
end
|
||||
|
||||
# Returns the link of the specified plugin.
|
||||
def self.link(plugin_name)
|
||||
return if !installed?(plugin_name)
|
||||
return @@Plugins[plugin_name][:link]
|
||||
end
|
||||
|
||||
# Returns the credits of the specified plugin.
|
||||
def self.credits(plugin_name)
|
||||
return if !installed?(plugin_name)
|
||||
return @@Plugins[plugin_name][:credits]
|
||||
end
|
||||
|
||||
# Compares two versions given in string form. v1 should be the plugin version
|
||||
# you actually have, and v2 should be the minimum/desired plugin version.
|
||||
# Return values:
|
||||
# 1 if v1 is higher than v2
|
||||
# 0 if v1 is equal to v2
|
||||
# -1 if v1 is lower than v2
|
||||
def self.compare_versions(v1, v2)
|
||||
d1 = v1.split("")
|
||||
d1.insert(0, "0") if d1[0] == "." # Turn ".123" into "0.123"
|
||||
while d1[-1] == "."; d1 = d1[0..-2]; end # Turn "123." into "123"
|
||||
d2 = v2.split("")
|
||||
d2.insert(0, "0") if d2[0] == "." # Turn ".123" into "0.123"
|
||||
while d2[-1] == "."; d2 = d2[0..-2]; end # Turn "123." into "123"
|
||||
for i in 0...[d1.size, d2.size].max # Compare each digit in turn
|
||||
c1 = d1[i]
|
||||
c2 = d2[i]
|
||||
if c1
|
||||
return 1 if !c2
|
||||
return 1 if c1.to_i(16) > c2.to_i(16)
|
||||
return -1 if c1.to_i(16) < c2.to_i(16)
|
||||
else
|
||||
return -1 if c2
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
end
|
||||
104
Data/Scripts/002_Switches and Variables/001_Game_Temp.rb
Normal file
104
Data/Scripts/002_Switches and Variables/001_Game_Temp.rb
Normal file
@@ -0,0 +1,104 @@
|
||||
#===============================================================================
|
||||
# ** Game_Temp
|
||||
#-------------------------------------------------------------------------------
|
||||
# This class handles temporary data that is not included with save data.
|
||||
# Refer to "$game_temp" for the instance of this class.
|
||||
#===============================================================================
|
||||
class Game_Temp
|
||||
attr_accessor :map_bgm # map music (for battle memory)
|
||||
attr_accessor :message_text # message text
|
||||
attr_accessor :message_proc # message callback (Proc)
|
||||
attr_accessor :choice_start # show choices: opening line
|
||||
attr_accessor :choice_max # show choices: number of items
|
||||
attr_accessor :choice_cancel_type # show choices: cancel
|
||||
attr_accessor :choice_proc # show choices: callback (Proc)
|
||||
attr_accessor :num_input_start # input number: opening line
|
||||
attr_accessor :num_input_variable_id # input number: variable ID
|
||||
attr_accessor :num_input_digits_max # input number: digit amount
|
||||
attr_accessor :message_window_showing # message window showing
|
||||
attr_accessor :common_event_id # common event ID
|
||||
attr_accessor :in_battle # in-battle flag
|
||||
attr_accessor :battle_calling # battle calling flag
|
||||
attr_accessor :battle_troop_id # battle troop ID
|
||||
attr_accessor :battle_can_escape # battle flag: escape possible
|
||||
attr_accessor :battle_can_lose # battle flag: losing possible
|
||||
attr_accessor :battle_proc # battle callback (Proc)
|
||||
attr_accessor :battle_turn # number of battle turns
|
||||
attr_accessor :battle_event_flags # battle event flags: completed
|
||||
attr_accessor :battle_abort # battle flag: interrupt
|
||||
attr_accessor :battle_main_phase # battle flag: main phase
|
||||
attr_accessor :battleback_name # battleback file name
|
||||
attr_accessor :forcing_battler # battler being forced into action
|
||||
attr_accessor :shop_calling # shop calling flag
|
||||
attr_accessor :shop_goods # list of shop goods
|
||||
attr_accessor :name_calling # name input: calling flag
|
||||
attr_accessor :name_actor_id # name input: actor ID
|
||||
attr_accessor :name_max_char # name input: max character count
|
||||
attr_accessor :menu_calling # menu calling flag
|
||||
attr_accessor :menu_beep # menu: play sound effect flag
|
||||
attr_accessor :in_menu # menu is open
|
||||
attr_accessor :save_calling # save calling flag
|
||||
attr_accessor :debug_calling # debug calling flag
|
||||
attr_accessor :player_transferring # player place movement flag
|
||||
attr_accessor :player_new_map_id # player destination: map ID
|
||||
attr_accessor :player_new_x # player destination: x-coordinate
|
||||
attr_accessor :player_new_y # player destination: y-coordinate
|
||||
attr_accessor :player_new_direction # player destination: direction
|
||||
attr_accessor :transition_processing # transition processing flag
|
||||
attr_accessor :transition_name # transition file name
|
||||
attr_accessor :gameover # game over flag
|
||||
attr_accessor :to_title # return to title screen flag
|
||||
attr_accessor :last_file_index # last save file no.
|
||||
attr_accessor :map_refresh # map needs redrawing
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Object Initialization
|
||||
#-----------------------------------------------------------------------------
|
||||
def initialize
|
||||
@map_bgm = nil
|
||||
@message_text = nil
|
||||
@message_proc = nil
|
||||
@choice_start = 99
|
||||
@choice_max = 0
|
||||
@choice_cancel_type = 0
|
||||
@choice_proc = nil
|
||||
@num_input_start = 99
|
||||
@num_input_variable_id = 0
|
||||
@num_input_digits_max = 0
|
||||
@message_window_showing = false
|
||||
@common_event_id = 0
|
||||
@in_battle = false
|
||||
@battle_calling = false
|
||||
@battle_troop_id = 0
|
||||
@battle_can_escape = false
|
||||
@battle_can_lose = false
|
||||
@battle_proc = nil
|
||||
@battle_turn = 0
|
||||
@battle_event_flags = {}
|
||||
@battle_abort = false
|
||||
@battle_main_phase = false
|
||||
@battleback_name = ''
|
||||
@forcing_battler = nil
|
||||
@shop_calling = false
|
||||
@shop_id = 0
|
||||
@name_calling = false
|
||||
@name_actor_id = 0
|
||||
@name_max_char = 0
|
||||
@menu_calling = false
|
||||
@menu_beep = false
|
||||
@in_menu = false
|
||||
@save_calling = false
|
||||
@debug_calling = false
|
||||
@player_transferring = false
|
||||
@player_new_map_id = 0
|
||||
@player_new_x = 0
|
||||
@player_new_y = 0
|
||||
@player_new_direction = 0
|
||||
@transition_processing = false
|
||||
@transition_name = ""
|
||||
@gameover = false
|
||||
@to_title = false
|
||||
@last_file_index = 0
|
||||
@debug_top_row = 0
|
||||
@debug_index = 0
|
||||
end
|
||||
end
|
||||
36
Data/Scripts/002_Switches and Variables/002_Game_Switches.rb
Normal file
36
Data/Scripts/002_Switches and Variables/002_Game_Switches.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
#===============================================================================
|
||||
# ** Game_Switches
|
||||
#-------------------------------------------------------------------------------
|
||||
# This class handles switches. It's a wrapper for the built-in class "Array."
|
||||
# Refer to "$game_switches" for the instance of this class.
|
||||
#===============================================================================
|
||||
|
||||
class Game_Switches
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Object Initialization
|
||||
#-----------------------------------------------------------------------------
|
||||
def initialize
|
||||
@data = []
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get Switch
|
||||
# switch_id : switch ID
|
||||
#-----------------------------------------------------------------------------
|
||||
def [](switch_id)
|
||||
if switch_id<=5000 and @data[switch_id]!=nil
|
||||
return @data[switch_id]
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Set Switch
|
||||
# switch_id : switch ID
|
||||
# value : ON (true) / OFF (false)
|
||||
#-----------------------------------------------------------------------------
|
||||
def []=(switch_id, value)
|
||||
if switch_id<=5000
|
||||
@data[switch_id] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
156
Data/Scripts/003_Game classes/001_Game_Screen.rb
Normal file
156
Data/Scripts/003_Game classes/001_Game_Screen.rb
Normal file
@@ -0,0 +1,156 @@
|
||||
#===============================================================================
|
||||
# ** Game_Screen
|
||||
#-------------------------------------------------------------------------------
|
||||
# This class handles screen maintenance data, such as change in color tone,
|
||||
# flashing, etc. Refer to "$game_screen" for the instance of this class.
|
||||
#===============================================================================
|
||||
|
||||
class Game_Screen
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Public Instance Variables
|
||||
#-----------------------------------------------------------------------------
|
||||
attr_reader :brightness # brightness
|
||||
attr_reader :tone # color tone
|
||||
attr_reader :flash_color # flash color
|
||||
attr_reader :shake # shake positioning
|
||||
attr_reader :pictures # pictures
|
||||
attr_reader :weather_type # weather type
|
||||
attr_reader :weather_max # max number of weather sprites
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Object Initialization
|
||||
#-----------------------------------------------------------------------------
|
||||
def initialize
|
||||
@brightness = 255
|
||||
@fadeout_duration = 0
|
||||
@fadein_duration = 0
|
||||
@tone = Tone.new(0, 0, 0, 0)
|
||||
@tone_target = Tone.new(0, 0, 0, 0)
|
||||
@tone_duration = 0
|
||||
@flash_color = Color.new(0, 0, 0, 0)
|
||||
@flash_duration = 0
|
||||
@shake_power = 0
|
||||
@shake_speed = 0
|
||||
@shake_duration = 0
|
||||
@shake_direction = 1
|
||||
@shake = 0
|
||||
@pictures = [nil]
|
||||
for i in 1..100
|
||||
@pictures.push(Game_Picture.new(i))
|
||||
end
|
||||
@weather_type = 0
|
||||
@weather_max = 0.0
|
||||
@weather_type_target = 0
|
||||
@weather_max_target = 0.0
|
||||
@weather_duration = 0
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Start Changing Color Tone
|
||||
# tone : color tone
|
||||
# duration : time
|
||||
#-----------------------------------------------------------------------------
|
||||
def start_tone_change(tone, duration)
|
||||
@tone_target = tone.clone
|
||||
@tone_duration = duration
|
||||
if @tone_duration == 0
|
||||
@tone = @tone_target.clone
|
||||
end
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Start Flashing
|
||||
# color : color
|
||||
# duration : time
|
||||
#-----------------------------------------------------------------------------
|
||||
def start_flash(color, duration)
|
||||
@flash_color = color.clone
|
||||
@flash_duration = duration
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Start Shaking
|
||||
# power : strength
|
||||
# speed : speed
|
||||
# duration : time
|
||||
#-----------------------------------------------------------------------------
|
||||
def start_shake(power, speed, duration)
|
||||
@shake_power = power
|
||||
@shake_speed = speed
|
||||
@shake_duration = duration
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Set Weather
|
||||
# type : type
|
||||
# power : strength
|
||||
# duration : time
|
||||
#-----------------------------------------------------------------------------
|
||||
def weather(type, power, duration)
|
||||
@weather_type_target = type
|
||||
if @weather_type_target!=0
|
||||
@weather_type = @weather_type_target
|
||||
end
|
||||
if @weather_type_target==0
|
||||
@weather_max_target = 0.0
|
||||
else
|
||||
@weather_max_target = (power + 1) * 4.0
|
||||
end
|
||||
@weather_duration = duration
|
||||
if @weather_duration==0
|
||||
@weather_type = @weather_type_target
|
||||
@weather_max = @weather_max_target
|
||||
end
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Frame Update
|
||||
#-----------------------------------------------------------------------------
|
||||
def update
|
||||
if @fadeout_duration && @fadeout_duration>=1
|
||||
d = @fadeout_duration
|
||||
@brightness = (@brightness*(d-1))/d
|
||||
@fadeout_duration -= 1
|
||||
end
|
||||
if @fadein_duration && @fadein_duration>=1
|
||||
d = @fadein_duration
|
||||
@brightness = (@brightness*(d-1)+255)/d
|
||||
@fadein_duration -= 1
|
||||
end
|
||||
if @tone_duration>=1
|
||||
d = @tone_duration
|
||||
@tone.red = (@tone.red*(d-1)+@tone_target.red)/d
|
||||
@tone.green = (@tone.green*(d-1)+@tone_target.green)/d
|
||||
@tone.blue = (@tone.blue*(d-1)+@tone_target.blue)/d
|
||||
@tone.gray = (@tone.gray*(d-1)+@tone_target.gray)/d
|
||||
@tone_duration -= 1
|
||||
end
|
||||
if @flash_duration>=1
|
||||
d = @flash_duration
|
||||
@flash_color.alpha = @flash_color.alpha*(d-1)/d
|
||||
@flash_duration -= 1
|
||||
end
|
||||
if @shake_duration>=1 or @shake!=0
|
||||
delta = (@shake_power*@shake_speed*@shake_direction)/10.0
|
||||
if @shake_duration<=1 and @shake*(@shake+delta)<0
|
||||
@shake = 0
|
||||
else
|
||||
@shake += delta
|
||||
end
|
||||
@shake_direction = -1 if @shake>@shake_power*2
|
||||
@shake_direction = 1 if @shake<-@shake_power*2
|
||||
@shake_duration -= 1 if @shake_duration>=1
|
||||
end
|
||||
if @weather_duration>=1
|
||||
d = @weather_duration
|
||||
@weather_max = (@weather_max*(d-1)+@weather_max_target)/d
|
||||
@weather_duration -= 1
|
||||
if @weather_duration==0
|
||||
@weather_type = @weather_type_target
|
||||
end
|
||||
end
|
||||
if $game_temp.in_battle
|
||||
for i in 51..100
|
||||
@pictures[i].update
|
||||
end
|
||||
else
|
||||
for i in 1..50
|
||||
@pictures[i].update
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
289
Data/Scripts/003_Game classes/002_Game_System.rb
Normal file
289
Data/Scripts/003_Game classes/002_Game_System.rb
Normal file
@@ -0,0 +1,289 @@
|
||||
#==============================================================================
|
||||
# ** Game_System
|
||||
#------------------------------------------------------------------------------
|
||||
# This class handles data surrounding the system. Backround music, etc.
|
||||
# is managed here as well. Refer to "$game_system" for the instance of
|
||||
# this class.
|
||||
#==============================================================================
|
||||
|
||||
class Game_System
|
||||
attr_reader :map_interpreter # map event interpreter
|
||||
attr_reader :battle_interpreter # battle event interpreter
|
||||
attr_accessor :timer # timer
|
||||
attr_accessor :timer_working # timer working flag
|
||||
attr_accessor :save_disabled # save forbidden
|
||||
attr_accessor :menu_disabled # menu forbidden
|
||||
attr_accessor :encounter_disabled # encounter forbidden
|
||||
attr_accessor :message_position # text option: positioning
|
||||
attr_accessor :message_frame # text option: window frame
|
||||
attr_accessor :save_count # save count
|
||||
attr_accessor :magic_number # magic number
|
||||
attr_accessor :autoscroll_x_speed
|
||||
attr_accessor :autoscroll_y_speed
|
||||
attr_accessor :bgm_position
|
||||
|
||||
def initialize
|
||||
if $RPGVX
|
||||
@map_interpreter = Game_Interpreter.new(0,true)
|
||||
@battle_interpreter = Game_Interpreter.new(0,false)
|
||||
else
|
||||
@map_interpreter = Interpreter.new(0,true)
|
||||
@battle_interpreter = Interpreter.new(0,false)
|
||||
end
|
||||
@timer = 0
|
||||
@timer_working = false
|
||||
@save_disabled = false
|
||||
@menu_disabled = false
|
||||
@encounter_disabled = false
|
||||
@message_position = 2
|
||||
@message_frame = 0
|
||||
@save_count = 0
|
||||
@magic_number = 0
|
||||
@autoscroll_x_speed = 0
|
||||
@autoscroll_y_speed = 0
|
||||
@bgm_position = 0
|
||||
@bgs_position = 0
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
def bgm_play(bgm)
|
||||
bgm_play_internal(bgm,0)
|
||||
end
|
||||
|
||||
def bgm_play_internal2(name,volume,pitch,position) # :nodoc:
|
||||
vol = volume
|
||||
vol *= $PokemonSystem.bgmvolume/100.0
|
||||
vol = vol.to_i
|
||||
begin
|
||||
Audio.bgm_play(name,vol,pitch,position)
|
||||
rescue ArgumentError
|
||||
Audio.bgm_play(name,vol,pitch)
|
||||
end
|
||||
end
|
||||
|
||||
def bgm_play_internal(bgm,position) # :nodoc:
|
||||
@bgm_position = position if !@bgm_paused
|
||||
@playing_bgm = (bgm==nil) ? nil : bgm.clone
|
||||
if bgm!=nil and bgm.name!=""
|
||||
if FileTest.audio_exist?("Audio/BGM/"+bgm.name)
|
||||
bgm_play_internal2("Audio/BGM/"+bgm.name,
|
||||
bgm.volume,bgm.pitch,@bgm_position) if !@defaultBGM
|
||||
end
|
||||
else
|
||||
@bgm_position = position if !@bgm_paused
|
||||
@playing_bgm = nil
|
||||
Audio.bgm_stop if !@defaultBGM
|
||||
end
|
||||
if @defaultBGM
|
||||
bgm_play_internal2("Audio/BGM/"+@defaultBGM.name,
|
||||
@defaultBGM.volume,@defaultBGM.pitch,@bgm_position)
|
||||
end
|
||||
Graphics.frame_reset
|
||||
end
|
||||
|
||||
def bgm_pause(fadetime=0.0) # :nodoc:
|
||||
pos = Audio.bgm_position rescue 0
|
||||
self.bgm_fade(fadetime) if fadetime>0.0
|
||||
@bgm_position = pos
|
||||
@bgm_paused = true
|
||||
end
|
||||
|
||||
def bgm_unpause # :nodoc:
|
||||
@bgm_position = 0
|
||||
@bgm_paused = false
|
||||
end
|
||||
|
||||
def bgm_resume(bgm) # :nodoc:
|
||||
if @bgm_paused
|
||||
self.bgm_play_internal(bgm,@bgm_position)
|
||||
@bgm_position = 0
|
||||
@bgm_paused = false
|
||||
end
|
||||
end
|
||||
|
||||
def bgm_stop # :nodoc:
|
||||
@bgm_position = 0 if !@bgm_paused
|
||||
@playing_bgm = nil
|
||||
Audio.bgm_stop if !@defaultBGM
|
||||
end
|
||||
|
||||
def bgm_fade(time) # :nodoc:
|
||||
@bgm_position = 0 if !@bgm_paused
|
||||
@playing_bgm = nil
|
||||
Audio.bgm_fade((time*1000).floor) if !@defaultBGM
|
||||
end
|
||||
|
||||
def playing_bgm
|
||||
return @playing_bgm
|
||||
end
|
||||
|
||||
# Saves the currently playing background music for later playback.
|
||||
def bgm_memorize
|
||||
@memorized_bgm = @playing_bgm
|
||||
end
|
||||
|
||||
# Plays the currently memorized background music
|
||||
def bgm_restore
|
||||
bgm_play(@memorized_bgm)
|
||||
end
|
||||
|
||||
# Returns an RPG::AudioFile object for the currently playing background music
|
||||
def getPlayingBGM
|
||||
return (@playing_bgm) ? @playing_bgm.clone : nil
|
||||
end
|
||||
|
||||
def setDefaultBGM(bgm,volume=80,pitch=100)
|
||||
bgm = RPG::AudioFile.new(bgm,volume,pitch) if bgm.is_a?(String)
|
||||
if bgm!=nil and bgm.name!=""
|
||||
@defaultBGM = nil
|
||||
self.bgm_play(bgm)
|
||||
@defaultBGM = bgm.clone
|
||||
else
|
||||
@defaultBGM = nil
|
||||
self.bgm_play(@playing_bgm)
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
def me_play(me)
|
||||
me = RPG::AudioFile.new(me) if me.is_a?(String)
|
||||
if me!=nil and me.name!=""
|
||||
if FileTest.audio_exist?("Audio/ME/"+me.name)
|
||||
vol = me.volume
|
||||
vol *= $PokemonSystem.bgmvolume/100.0
|
||||
vol = vol.to_i
|
||||
Audio.me_play("Audio/ME/"+me.name,vol,me.pitch)
|
||||
end
|
||||
else
|
||||
Audio.me_stop
|
||||
end
|
||||
Graphics.frame_reset
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
def bgs_play(bgs)
|
||||
@playing_bgs = (bgs==nil) ? nil : bgs.clone
|
||||
if bgs!=nil and bgs.name!=""
|
||||
if FileTest.audio_exist?("Audio/BGS/"+bgs.name)
|
||||
vol = bgs.volume
|
||||
vol *= $PokemonSystem.sevolume/100.0
|
||||
vol = vol.to_i
|
||||
Audio.bgs_play("Audio/BGS/"+bgs.name,vol,bgs.pitch)
|
||||
end
|
||||
else
|
||||
@bgs_position = 0
|
||||
@playing_bgs = nil
|
||||
Audio.bgs_stop
|
||||
end
|
||||
Graphics.frame_reset
|
||||
end
|
||||
|
||||
def bgs_pause(fadetime=0.0) # :nodoc:
|
||||
if fadetime>0.0
|
||||
self.bgs_fade(fadetime)
|
||||
else
|
||||
self.bgs_stop
|
||||
end
|
||||
@bgs_paused = true
|
||||
end
|
||||
|
||||
def bgs_unpause # :nodoc:
|
||||
@bgs_paused = false
|
||||
end
|
||||
|
||||
def bgs_resume(bgs) # :nodoc:
|
||||
if @bgs_paused
|
||||
self.bgs_play(bgs)
|
||||
@bgs_paused = false
|
||||
end
|
||||
end
|
||||
|
||||
def bgs_stop
|
||||
@bgs_position = 0
|
||||
@playing_bgs = nil
|
||||
Audio.bgs_stop
|
||||
end
|
||||
|
||||
def bgs_fade(time)
|
||||
@bgs_position = 0
|
||||
@playing_bgs = nil
|
||||
Audio.bgs_fade((time*1000).floor)
|
||||
end
|
||||
|
||||
def playing_bgs
|
||||
return @playing_bgs
|
||||
end
|
||||
|
||||
def bgs_memorize
|
||||
@memorized_bgs = @playing_bgs
|
||||
end
|
||||
|
||||
def bgs_restore
|
||||
bgs_play(@memorized_bgs)
|
||||
end
|
||||
|
||||
def getPlayingBGS
|
||||
return (@playing_bgs) ? @playing_bgs.clone : nil
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
def se_play(se)
|
||||
se = RPG::AudioFile.new(se) if se.is_a?(String)
|
||||
if se!=nil and se.name!="" && FileTest.audio_exist?("Audio/SE/"+se.name)
|
||||
vol = se.volume
|
||||
vol *= $PokemonSystem.sevolume/100.0
|
||||
vol = vol.to_i
|
||||
Audio.se_play("Audio/SE/"+se.name,vol,se.pitch)
|
||||
end
|
||||
end
|
||||
|
||||
def se_stop
|
||||
Audio.se_stop
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
def battle_bgm
|
||||
return (@battle_bgm) ? @battle_bgm : $data_system.battle_bgm
|
||||
end
|
||||
|
||||
def battle_bgm=(battle_bgm)
|
||||
@battle_bgm = battle_bgm
|
||||
end
|
||||
|
||||
def battle_end_me
|
||||
return (@battle_end_me) ? @battle_end_me : $data_system.battle_end_me
|
||||
end
|
||||
|
||||
def battle_end_me=(battle_end_me)
|
||||
@battle_end_me = battle_end_me
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
def windowskin_name
|
||||
if @windowskin_name==nil
|
||||
return $data_system.windowskin_name
|
||||
else
|
||||
return @windowskin_name
|
||||
end
|
||||
end
|
||||
|
||||
def windowskin_name=(windowskin_name)
|
||||
@windowskin_name = windowskin_name
|
||||
end
|
||||
|
||||
def update
|
||||
@timer -= 1 if @timer_working and @timer>0
|
||||
if Input.trigger?(Input::F5) && pbCurrentEventCommentInput(1,"Cut Scene")
|
||||
event = @map_interpreter.get_character(0)
|
||||
@map_interpreter.pbSetSelfSwitch(event.id,"A",true)
|
||||
@map_interpreter.command_end
|
||||
event.start
|
||||
end
|
||||
end
|
||||
end
|
||||
156
Data/Scripts/003_Game classes/003_Game_Picture.rb
Normal file
156
Data/Scripts/003_Game classes/003_Game_Picture.rb
Normal file
@@ -0,0 +1,156 @@
|
||||
#===============================================================================
|
||||
# ** Game_Picture
|
||||
#-------------------------------------------------------------------------------
|
||||
# This class handles the picture. It's used within the Game_Screen class
|
||||
# ($game_screen).
|
||||
#===============================================================================
|
||||
|
||||
class Game_Picture
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Public Instance Variables
|
||||
#-----------------------------------------------------------------------------
|
||||
attr_reader :number # picture number
|
||||
attr_reader :name # file name
|
||||
attr_reader :origin # starting point
|
||||
attr_reader :x # x-coordinate
|
||||
attr_reader :y # y-coordinate
|
||||
attr_reader :zoom_x # x directional zoom rate
|
||||
attr_reader :zoom_y # y directional zoom rate
|
||||
attr_reader :opacity # opacity level
|
||||
attr_reader :blend_type # blend method
|
||||
attr_reader :tone # color tone
|
||||
attr_reader :angle # rotation angle
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Object Initialization
|
||||
# number : picture number
|
||||
#-----------------------------------------------------------------------------
|
||||
def initialize(number)
|
||||
@number = number
|
||||
@name = ""
|
||||
@origin = 0
|
||||
@x = 0.0
|
||||
@y = 0.0
|
||||
@zoom_x = 100.0
|
||||
@zoom_y = 100.0
|
||||
@opacity = 255.0
|
||||
@blend_type = 1
|
||||
@duration = 0
|
||||
@target_x = @x
|
||||
@target_y = @y
|
||||
@target_zoom_x = @zoom_x
|
||||
@target_zoom_y = @zoom_y
|
||||
@target_opacity = @opacity
|
||||
@tone = Tone.new(0, 0, 0, 0)
|
||||
@tone_target = Tone.new(0, 0, 0, 0)
|
||||
@tone_duration = 0
|
||||
@angle = 0
|
||||
@rotate_speed = 0
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Show Picture
|
||||
# name : file name
|
||||
# origin : starting point
|
||||
# x : x-coordinate
|
||||
# y : y-coordinate
|
||||
# zoom_x : x directional zoom rate
|
||||
# zoom_y : y directional zoom rate
|
||||
# opacity : opacity level
|
||||
# blend_type : blend method
|
||||
#-----------------------------------------------------------------------------
|
||||
def show(name, origin, x, y, zoom_x, zoom_y, opacity, blend_type)
|
||||
@name = name
|
||||
@origin = origin
|
||||
@x = x.to_f
|
||||
@y = y.to_f
|
||||
@zoom_x = zoom_x.to_f
|
||||
@zoom_y = zoom_y.to_f
|
||||
@opacity = opacity.to_f
|
||||
@blend_type = blend_type ? blend_type : 0
|
||||
@duration = 0
|
||||
@target_x = @x
|
||||
@target_y = @y
|
||||
@target_zoom_x = @zoom_x
|
||||
@target_zoom_y = @zoom_y
|
||||
@target_opacity = @opacity
|
||||
@tone = Tone.new(0, 0, 0, 0)
|
||||
@tone_target = Tone.new(0, 0, 0, 0)
|
||||
@tone_duration = 0
|
||||
@angle = 0
|
||||
@rotate_speed = 0
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Move Picture
|
||||
# duration : time
|
||||
# origin : starting point
|
||||
# x : x-coordinate
|
||||
# y : y-coordinate
|
||||
# zoom_x : x directional zoom rate
|
||||
# zoom_y : y directional zoom rate
|
||||
# opacity : opacity level
|
||||
# blend_type : blend method
|
||||
#-----------------------------------------------------------------------------
|
||||
def move(duration, origin, x, y, zoom_x, zoom_y, opacity, blend_type)
|
||||
@duration = duration
|
||||
@origin = origin
|
||||
@target_x = x.to_f
|
||||
@target_y = y.to_f
|
||||
@target_zoom_x = zoom_x.to_f
|
||||
@target_zoom_y = zoom_y.to_f
|
||||
@target_opacity = opacity.to_f
|
||||
@blend_type = blend_type ? blend_type : 0
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Change Rotation Speed
|
||||
# speed : rotation speed
|
||||
#-----------------------------------------------------------------------------
|
||||
def rotate(speed)
|
||||
@rotate_speed = speed
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Start Change of Color Tone
|
||||
# tone : color tone
|
||||
# duration : time
|
||||
#-----------------------------------------------------------------------------
|
||||
def start_tone_change(tone, duration)
|
||||
@tone_target = tone.clone
|
||||
@tone_duration = duration
|
||||
if @tone_duration == 0
|
||||
@tone = @tone_target.clone
|
||||
end
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Erase Picture
|
||||
#-----------------------------------------------------------------------------
|
||||
def erase
|
||||
@name = ""
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Frame Update
|
||||
#-----------------------------------------------------------------------------
|
||||
def update
|
||||
if @duration >= 1
|
||||
d = @duration
|
||||
@x = (@x * (d - 1) + @target_x) / d
|
||||
@y = (@y * (d - 1) + @target_y) / d
|
||||
@zoom_x = (@zoom_x * (d - 1) + @target_zoom_x) / d
|
||||
@zoom_y = (@zoom_y * (d - 1) + @target_zoom_y) / d
|
||||
@opacity = (@opacity * (d - 1) + @target_opacity) / d
|
||||
@duration -= 1
|
||||
end
|
||||
if @tone_duration >= 1
|
||||
d = @tone_duration
|
||||
@tone.red = (@tone.red * (d - 1) + @tone_target.red) / d
|
||||
@tone.green = (@tone.green * (d - 1) + @tone_target.green) / d
|
||||
@tone.blue = (@tone.blue * (d - 1) + @tone_target.blue) / d
|
||||
@tone.gray = (@tone.gray * (d - 1) + @tone_target.gray) / d
|
||||
@tone_duration -= 1
|
||||
end
|
||||
if @rotate_speed != 0
|
||||
@angle += @rotate_speed / 2.0
|
||||
while @angle < 0
|
||||
@angle += 360
|
||||
end
|
||||
@angle %= 360
|
||||
end
|
||||
end
|
||||
end
|
||||
81
Data/Scripts/003_Game classes/004_Game_CommonEvent.rb
Normal file
81
Data/Scripts/003_Game classes/004_Game_CommonEvent.rb
Normal file
@@ -0,0 +1,81 @@
|
||||
#===============================================================================
|
||||
# ** Game_CommonEvent
|
||||
#-------------------------------------------------------------------------------
|
||||
# This class handles common events. It includes execution of parallel process
|
||||
# event. This class is used within the Game_Map class ($game_map).
|
||||
#===============================================================================
|
||||
class Game_CommonEvent
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Object Initialization
|
||||
# common_event_id : common event ID
|
||||
#-----------------------------------------------------------------------------
|
||||
def initialize(common_event_id)
|
||||
@common_event_id = common_event_id
|
||||
@interpreter = nil
|
||||
refresh
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get Name
|
||||
#-----------------------------------------------------------------------------
|
||||
def name
|
||||
return $data_common_events[@common_event_id].name
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get Trigger
|
||||
#-----------------------------------------------------------------------------
|
||||
def trigger
|
||||
return $data_common_events[@common_event_id].trigger
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get Condition Switch ID
|
||||
#-----------------------------------------------------------------------------
|
||||
def switch_id
|
||||
return $data_common_events[@common_event_id].switch_id
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Get List of Event Commands
|
||||
#-----------------------------------------------------------------------------
|
||||
def list
|
||||
return $data_common_events[@common_event_id].list
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Checks if switch is on
|
||||
#-----------------------------------------------------------------------------
|
||||
def switchIsOn?(id)
|
||||
switchName = $data_system.switches[id]
|
||||
return false if !switchName
|
||||
if switchName[/^s\:/]
|
||||
return eval($~.post_match)
|
||||
else
|
||||
return $game_switches[id]
|
||||
end
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Refresh
|
||||
#-----------------------------------------------------------------------------
|
||||
def refresh
|
||||
# Create an interpreter for parallel process if necessary
|
||||
if self.trigger == 2 and switchIsOn?(self.switch_id)
|
||||
if @interpreter == nil
|
||||
@interpreter = Interpreter.new
|
||||
end
|
||||
else
|
||||
@interpreter = nil
|
||||
end
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Frame Update
|
||||
#-----------------------------------------------------------------------------
|
||||
def update
|
||||
# If parallel process is valid
|
||||
if @interpreter != nil
|
||||
# If not running
|
||||
unless @interpreter.running?
|
||||
# Set up event
|
||||
@interpreter.setup(self.list, 0)
|
||||
end
|
||||
# Update interpreter
|
||||
@interpreter.update
|
||||
end
|
||||
end
|
||||
end
|
||||
869
Data/Scripts/003_Game classes/005_Game_Character.rb
Normal file
869
Data/Scripts/003_Game classes/005_Game_Character.rb
Normal file
@@ -0,0 +1,869 @@
|
||||
class Game_Character
|
||||
attr_reader :id
|
||||
attr_reader :original_x
|
||||
attr_reader :original_y
|
||||
attr_reader :x
|
||||
attr_reader :y
|
||||
attr_reader :real_x
|
||||
attr_reader :real_y
|
||||
attr_accessor :sprite_size
|
||||
attr_reader :tile_id
|
||||
attr_accessor :character_name
|
||||
attr_accessor :character_hue
|
||||
attr_reader :opacity
|
||||
attr_reader :blend_type
|
||||
attr_reader :direction
|
||||
attr_accessor :pattern
|
||||
attr_reader :pattern_surf
|
||||
attr_accessor :lock_pattern
|
||||
attr_reader :move_route_forcing
|
||||
attr_accessor :through
|
||||
attr_accessor :animation_id
|
||||
attr_accessor :transparent
|
||||
attr_reader :move_speed
|
||||
attr_accessor :walk_anime
|
||||
attr_accessor :bob_height
|
||||
|
||||
def initialize(map=nil)
|
||||
@map = map
|
||||
@id = 0
|
||||
@original_x = 0
|
||||
@original_y = 0
|
||||
@x = 0
|
||||
@y = 0
|
||||
@real_x = 0
|
||||
@real_y = 0
|
||||
@sprite_size = [Game_Map::TILE_WIDTH,Game_Map::TILE_HEIGHT]
|
||||
@tile_id = 0
|
||||
@character_name = ""
|
||||
@character_hue = 0
|
||||
@opacity = 255
|
||||
@blend_type = 0
|
||||
@direction = 2
|
||||
@pattern = 0
|
||||
@pattern_surf = 0
|
||||
@lock_pattern = false
|
||||
@move_route_forcing = false
|
||||
@through = false
|
||||
@animation_id = 0
|
||||
@transparent = false
|
||||
@original_direction = 2
|
||||
@original_pattern = 0
|
||||
@move_type = 0
|
||||
self.move_speed = 4
|
||||
self.move_frequency = 6
|
||||
@move_route = nil
|
||||
@move_route_index = 0
|
||||
@original_move_route = nil
|
||||
@original_move_route_index = 0
|
||||
@walk_anime = true # Whether character should animate while moving
|
||||
@step_anime = false # Whether character should animate while still
|
||||
@direction_fix = false
|
||||
@always_on_top = false
|
||||
@anime_count = 0
|
||||
@stop_count = 0
|
||||
@jump_count = 0
|
||||
@jump_peak = 0
|
||||
@bob_height = 0
|
||||
@wait_count = 0
|
||||
@moved_this_frame = false
|
||||
@locked = false
|
||||
@prelock_direction = 0
|
||||
end
|
||||
|
||||
def move_speed=(val)
|
||||
return if val==@move_speed
|
||||
@move_speed = val
|
||||
# @move_speed_real is the number of quarter-pixels to move each frame. There
|
||||
# are 128 quarter-pixels per tile. By default, it is calculated from
|
||||
# @move_speed and has these values (assuming 40 fps):
|
||||
# 1 => 1.6 # 80 frames per tile
|
||||
# 2 => 3.2 # 40 frames per tile
|
||||
# 3 => 6.4 # 20 frames per tile
|
||||
# 4 => 12.8 # 10 frames per tile - walking speed
|
||||
# 5 => 25.6 # 5 frames per tile - running speed (2x walking speed)
|
||||
# 6 => 32 # 4 frames per tile - cycling speed (1.25x running speed)
|
||||
self.move_speed_real = (val == 6) ? 32 : (1 << val) * 0.8
|
||||
end
|
||||
|
||||
def move_speed_real
|
||||
self.move_speed = @move_speed if !@move_speed_real
|
||||
return @move_speed_real
|
||||
end
|
||||
|
||||
def move_speed_real=(val)
|
||||
@move_speed_real = val * 40.0 / Graphics.frame_rate
|
||||
end
|
||||
|
||||
def move_frequency=(val)
|
||||
return if val==@move_frequency
|
||||
@move_frequency = val
|
||||
# @move_frequency_real is the number of frames to wait between each action
|
||||
# in a move route (not forced). Specifically, this is the number of frames
|
||||
# to wait after the character stops moving because of the previous action.
|
||||
# By default, it is calculated from @move_frequency and has these values
|
||||
# (assuming 40 fps):
|
||||
# 1 => 190 # 4.75 seconds
|
||||
# 2 => 144 # 3.6 seconds
|
||||
# 3 => 102 # 2.55 seconds
|
||||
# 4 => 64 # 1.6 seconds
|
||||
# 5 => 30 # 0.75 seconds
|
||||
# 6 => 0 # 0 seconds, i.e. continuous movement
|
||||
self.move_frequency_real = (40 - val * 2) * (6 - val)
|
||||
end
|
||||
|
||||
def move_frequency_real
|
||||
self.move_frequency = @move_frequency if !@move_frequency_real
|
||||
return @move_frequency_real
|
||||
end
|
||||
|
||||
def move_frequency_real=(val)
|
||||
@move_frequency_real = val * Graphics.frame_rate / 40.0
|
||||
end
|
||||
|
||||
def bob_height
|
||||
@bob_height = 0 if !@bob_height
|
||||
return @bob_height
|
||||
end
|
||||
|
||||
def lock
|
||||
return if @locked
|
||||
@prelock_direction = 0 # Was @direction but disabled
|
||||
turn_toward_player
|
||||
@locked = true
|
||||
end
|
||||
|
||||
def minilock
|
||||
@prelock_direction = 0 # Was @direction but disabled
|
||||
@locked = true
|
||||
end
|
||||
|
||||
def lock?
|
||||
return @locked
|
||||
end
|
||||
|
||||
def unlock
|
||||
return unless @locked
|
||||
@locked = false
|
||||
@direction = @prelock_direction if !@direction_fix and @prelock_direction != 0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Information from map data
|
||||
#=============================================================================
|
||||
def map
|
||||
return (@map) ? @map : $game_map
|
||||
end
|
||||
|
||||
def terrain_tag
|
||||
return self.map.terrain_tag(@x, @y)
|
||||
end
|
||||
|
||||
def bush_depth
|
||||
return 0 if @tile_id > 0 or @always_on_top or @jump_count > 0
|
||||
xbehind = @x + (@direction==4 ? 1 : @direction==6 ? -1 : 0)
|
||||
ybehind = @y + (@direction==8 ? 1 : @direction==2 ? -1 : 0)
|
||||
return Game_Map::TILE_HEIGHT if self.map.deepBush?(@x, @y) and self.map.deepBush?(xbehind, ybehind)
|
||||
return 12 if !moving? and self.map.bush?(@x, @y)
|
||||
return 0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Passability
|
||||
#=============================================================================
|
||||
def passableEx?(x, y, d, strict=false)
|
||||
new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0)
|
||||
new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0)
|
||||
return false unless self.map.valid?(new_x, new_y)
|
||||
return true if @through
|
||||
if strict
|
||||
return false unless self.map.passableStrict?(x, y, d, self)
|
||||
return false unless self.map.passableStrict?(new_x, new_y, 10 - d, self)
|
||||
else
|
||||
return false unless self.map.passable?(x, y, d, self)
|
||||
return false unless self.map.passable?(new_x, new_y, 10 - d, self)
|
||||
end
|
||||
for event in self.map.events.values
|
||||
next if event.x != new_x || event.y != new_y || event.through
|
||||
return false if self != $game_player || event.character_name != ""
|
||||
end
|
||||
if $game_player.x == new_x and $game_player.y == new_y
|
||||
return false if !$game_player.through && @character_name != ""
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def passable?(x,y,d)
|
||||
return passableEx?(x,y,d,false)
|
||||
end
|
||||
|
||||
def passableStrict?(x,y,d)
|
||||
return passableEx?(x,y,d,true)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Screen position of the character
|
||||
#=============================================================================
|
||||
def screen_x
|
||||
ret = ((@real_x - self.map.display_x) / Game_Map::X_SUBPIXELS).round
|
||||
ret += Game_Map::TILE_WIDTH/2
|
||||
return ret
|
||||
end
|
||||
|
||||
def screen_y_ground
|
||||
ret = ((@real_y - self.map.display_y) / Game_Map::Y_SUBPIXELS).round
|
||||
ret += Game_Map::TILE_HEIGHT
|
||||
return ret
|
||||
end
|
||||
|
||||
def screen_y
|
||||
ret = screen_y_ground
|
||||
if jumping?
|
||||
n = ((2 * @jump_count * 20 / Graphics.frame_rate) - @jump_peak).abs
|
||||
return ret - (@jump_peak * @jump_peak - n * n) / 2
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def screen_z(height = 0)
|
||||
return 999 if @always_on_top
|
||||
z = screen_y_ground
|
||||
if @tile_id > 0
|
||||
begin
|
||||
return z + self.map.priorities[@tile_id] * 32
|
||||
rescue
|
||||
raise "Event's graphic is an out-of-range tile (event #{@id}, map #{self.map.map_id})"
|
||||
end
|
||||
end
|
||||
# Add z if height exceeds 32
|
||||
return z + ((height > Game_Map::TILE_HEIGHT) ? Game_Map::TILE_HEIGHT - 1 : 0)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Movement
|
||||
#=============================================================================
|
||||
def moving?
|
||||
return @real_x != @x * Game_Map::REAL_RES_X ||
|
||||
@real_y != @y * Game_Map::REAL_RES_Y
|
||||
end
|
||||
|
||||
def jumping?
|
||||
return @jump_count > 0
|
||||
end
|
||||
|
||||
def straighten
|
||||
@pattern = 0 if @walk_anime or @step_anime
|
||||
@anime_count = 0
|
||||
@prelock_direction = 0
|
||||
end
|
||||
|
||||
def force_move_route(move_route)
|
||||
if @original_move_route == nil
|
||||
@original_move_route = @move_route
|
||||
@original_move_route_index = @move_route_index
|
||||
end
|
||||
@move_route = move_route
|
||||
@move_route_index = 0
|
||||
@move_route_forcing = true
|
||||
@prelock_direction = 0
|
||||
@wait_count = 0
|
||||
move_type_custom
|
||||
end
|
||||
|
||||
def moveto(x, y)
|
||||
@x = x % self.map.width
|
||||
@y = y % self.map.height
|
||||
@real_x = @x * Game_Map::REAL_RES_X
|
||||
@real_y = @y * Game_Map::REAL_RES_Y
|
||||
@prelock_direction = 0
|
||||
triggerLeaveTile
|
||||
end
|
||||
|
||||
def triggerLeaveTile
|
||||
if @oldX && @oldY && @oldMap &&
|
||||
(@oldX!=self.x || @oldY!=self.y || @oldMap!=self.map.map_id)
|
||||
Events.onLeaveTile.trigger(self,self,@oldMap,@oldX,@oldY)
|
||||
end
|
||||
@oldX = self.x
|
||||
@oldY = self.y
|
||||
@oldMap = self.map.map_id
|
||||
end
|
||||
|
||||
def increase_steps
|
||||
@stop_count = 0
|
||||
triggerLeaveTile
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Movement commands
|
||||
#=============================================================================
|
||||
def move_type_random
|
||||
case rand(6)
|
||||
when 0..3; move_random
|
||||
when 4; move_forward
|
||||
when 5; @stop_count = 0
|
||||
end
|
||||
end
|
||||
|
||||
def move_type_toward_player
|
||||
sx = @x - $game_player.x
|
||||
sy = @y - $game_player.y
|
||||
if sx.abs + sy.abs >= 20
|
||||
move_random
|
||||
return
|
||||
end
|
||||
case rand(6)
|
||||
when 0..3; move_toward_player
|
||||
when 4; move_random
|
||||
when 5; move_forward
|
||||
end
|
||||
end
|
||||
|
||||
def move_type_custom
|
||||
return if jumping? or moving?
|
||||
while @move_route_index < @move_route.list.size
|
||||
command = @move_route.list[@move_route_index]
|
||||
if command.code == 0
|
||||
if @move_route.repeat
|
||||
@move_route_index = 0
|
||||
else
|
||||
if @move_route_forcing
|
||||
@move_route_forcing = false
|
||||
@move_route = @original_move_route
|
||||
@move_route_index = @original_move_route_index
|
||||
@original_move_route = nil
|
||||
end
|
||||
@stop_count = 0
|
||||
end
|
||||
return
|
||||
end
|
||||
if command.code <= 14
|
||||
case command.code
|
||||
when 1; move_down
|
||||
when 2; move_left
|
||||
when 3; move_right
|
||||
when 4; move_up
|
||||
when 5; move_lower_left
|
||||
when 6; move_lower_right
|
||||
when 7; move_upper_left
|
||||
when 8; move_upper_right
|
||||
when 9; move_random
|
||||
when 10; move_toward_player
|
||||
when 11; move_away_from_player
|
||||
when 12; move_forward
|
||||
when 13; move_backward
|
||||
when 14; jump(command.parameters[0], command.parameters[1])
|
||||
end
|
||||
@move_route_index += 1 if @move_route.skippable or moving? or jumping?
|
||||
return
|
||||
end
|
||||
if command.code == 15 # Wait
|
||||
@wait_count = (command.parameters[0] * Graphics.frame_rate / 20) - 1
|
||||
@move_route_index += 1
|
||||
return
|
||||
end
|
||||
if command.code >= 16 and command.code <= 26
|
||||
case command.code
|
||||
when 16; turn_down
|
||||
when 17; turn_left
|
||||
when 18; turn_right
|
||||
when 19; turn_up
|
||||
when 20; turn_right_90
|
||||
when 21; turn_left_90
|
||||
when 22; turn_180
|
||||
when 23; turn_right_or_left_90
|
||||
when 24; turn_random
|
||||
when 25; turn_toward_player
|
||||
when 26; turn_away_from_player
|
||||
end
|
||||
@move_route_index += 1
|
||||
return
|
||||
end
|
||||
if command.code >= 27
|
||||
case command.code
|
||||
when 27
|
||||
$game_switches[command.parameters[0]] = true
|
||||
self.map.need_refresh = true
|
||||
when 28
|
||||
$game_switches[command.parameters[0]] = false
|
||||
self.map.need_refresh = true
|
||||
when 29; self.move_speed = command.parameters[0]
|
||||
when 30; self.move_frequency = command.parameters[0]
|
||||
when 31; @walk_anime = true
|
||||
when 32; @walk_anime = false
|
||||
when 33; @step_anime = true
|
||||
when 34; @step_anime = false
|
||||
when 35; @direction_fix = true
|
||||
when 36; @direction_fix = false
|
||||
when 37; @through = true
|
||||
when 38; @through = false
|
||||
when 39; @always_on_top = true
|
||||
when 40; @always_on_top = false
|
||||
when 41
|
||||
@tile_id = 0
|
||||
@character_name = command.parameters[0]
|
||||
@character_hue = command.parameters[1]
|
||||
if @original_direction != command.parameters[2]
|
||||
@direction = command.parameters[2]
|
||||
@original_direction = @direction
|
||||
@prelock_direction = 0
|
||||
end
|
||||
if @original_pattern != command.parameters[3]
|
||||
@pattern = command.parameters[3]
|
||||
@original_pattern = @pattern
|
||||
end
|
||||
when 42; @opacity = command.parameters[0]
|
||||
when 43; @blend_type = command.parameters[0]
|
||||
when 44; pbSEPlay(command.parameters[0])
|
||||
when 45; result = eval(command.parameters[0])
|
||||
end
|
||||
@move_route_index += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def move_up(turn_enabled = true)
|
||||
turn_up if turn_enabled
|
||||
if passable?(@x, @y, 8)
|
||||
turn_up
|
||||
@y -= 1
|
||||
increase_steps
|
||||
else
|
||||
check_event_trigger_touch(@x, @y-1)
|
||||
end
|
||||
end
|
||||
|
||||
def move_down(turn_enabled = true)
|
||||
turn_down if turn_enabled
|
||||
if passable?(@x, @y, 2)
|
||||
turn_down
|
||||
@y += 1
|
||||
increase_steps
|
||||
else
|
||||
check_event_trigger_touch(@x, @y+1)
|
||||
end
|
||||
end
|
||||
|
||||
def move_left(turn_enabled = true)
|
||||
turn_left if turn_enabled
|
||||
if passable?(@x, @y, 4)
|
||||
turn_left
|
||||
@x -= 1
|
||||
increase_steps
|
||||
else
|
||||
check_event_trigger_touch(@x-1, @y)
|
||||
end
|
||||
end
|
||||
|
||||
def move_right(turn_enabled = true)
|
||||
turn_right if turn_enabled
|
||||
if passable?(@x, @y, 6)
|
||||
turn_right
|
||||
@x += 1
|
||||
increase_steps
|
||||
else
|
||||
check_event_trigger_touch(@x+1, @y)
|
||||
end
|
||||
end
|
||||
|
||||
def move_upper_left
|
||||
unless @direction_fix
|
||||
@direction = (@direction == 6 ? 4 : @direction == 2 ? 8 : @direction)
|
||||
end
|
||||
if (passable?(@x, @y, 8) and passable?(@x, @y - 1, 4)) or
|
||||
(passable?(@x, @y, 4) and passable?(@x - 1, @y, 8))
|
||||
@x -= 1
|
||||
@y -= 1
|
||||
increase_steps
|
||||
end
|
||||
end
|
||||
|
||||
def move_upper_right
|
||||
unless @direction_fix
|
||||
@direction = (@direction == 4 ? 6 : @direction == 2 ? 8 : @direction)
|
||||
end
|
||||
if (passable?(@x, @y, 8) and passable?(@x, @y - 1, 6)) or
|
||||
(passable?(@x, @y, 6) and passable?(@x + 1, @y, 8))
|
||||
@x += 1
|
||||
@y -= 1
|
||||
increase_steps
|
||||
end
|
||||
end
|
||||
|
||||
def move_lower_left
|
||||
unless @direction_fix
|
||||
@direction = (@direction == 6 ? 4 : @direction == 8 ? 2 : @direction)
|
||||
end
|
||||
if (passable?(@x, @y, 2) and passable?(@x, @y + 1, 4)) or
|
||||
(passable?(@x, @y, 4) and passable?(@x - 1, @y, 2))
|
||||
@x -= 1
|
||||
@y += 1
|
||||
increase_steps
|
||||
end
|
||||
end
|
||||
|
||||
def move_lower_right
|
||||
unless @direction_fix
|
||||
@direction = (@direction == 4 ? 6 : @direction == 8 ? 2 : @direction)
|
||||
end
|
||||
if (passable?(@x, @y, 2) and passable?(@x, @y + 1, 6)) or
|
||||
(passable?(@x, @y, 6) and passable?(@x + 1, @y, 2))
|
||||
@x += 1
|
||||
@y += 1
|
||||
increase_steps
|
||||
end
|
||||
end
|
||||
|
||||
def moveLeft90 # anticlockwise
|
||||
case self.direction
|
||||
when 2; move_right # down
|
||||
when 4; move_down # left
|
||||
when 6; move_up # right
|
||||
when 8; move_left # up
|
||||
end
|
||||
end
|
||||
|
||||
def moveRight90 # clockwise
|
||||
case self.direction
|
||||
when 2; move_left # down
|
||||
when 4; move_up # left
|
||||
when 6; move_down # right
|
||||
when 8; move_right # up
|
||||
end
|
||||
end
|
||||
|
||||
def move_random
|
||||
case rand(4)
|
||||
when 0; move_down(false)
|
||||
when 1; move_left(false)
|
||||
when 2; move_right(false)
|
||||
when 3; move_up(false)
|
||||
end
|
||||
end
|
||||
|
||||
def move_random_range(xrange=-1,yrange=-1)
|
||||
dirs = [] # 0=down, 1=left, 2=right, 3=up
|
||||
if xrange<0
|
||||
dirs.push(1); dirs.push(2)
|
||||
elsif xrange>0
|
||||
dirs.push(1) if @x > @original_x - xrange
|
||||
dirs.push(2) if @x < @original_x + xrange
|
||||
end
|
||||
if yrange<0
|
||||
dirs.push(0); dirs.push(3)
|
||||
elsif yrange>0
|
||||
dirs.push(0) if @y < @original_y + yrange
|
||||
dirs.push(3) if @y > @original_y - yrange
|
||||
end
|
||||
return if dirs.length==0
|
||||
case dirs[rand(dirs.length)]
|
||||
when 0; move_down(false)
|
||||
when 1; move_left(false)
|
||||
when 2; move_right(false)
|
||||
when 3; move_up(false)
|
||||
end
|
||||
end
|
||||
|
||||
def move_random_UD(range=-1)
|
||||
move_random_range(0,range)
|
||||
end
|
||||
|
||||
def move_random_LR(range=-1)
|
||||
move_random_range(range,0)
|
||||
end
|
||||
|
||||
def move_toward_player
|
||||
sx = @x - $game_player.x
|
||||
sy = @y - $game_player.y
|
||||
return if sx == 0 and sy == 0
|
||||
abs_sx = sx.abs
|
||||
abs_sy = sy.abs
|
||||
if abs_sx == abs_sy
|
||||
(rand(2) == 0) ? abs_sx += 1 : abs_sy += 1
|
||||
end
|
||||
if abs_sx > abs_sy
|
||||
(sx > 0) ? move_left : move_right
|
||||
if not moving? and sy != 0
|
||||
(sy > 0) ? move_up : move_down
|
||||
end
|
||||
else
|
||||
(sy > 0) ? move_up : move_down
|
||||
if not moving? and sx != 0
|
||||
(sx > 0) ? move_left : move_right
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def move_away_from_player
|
||||
sx = @x - $game_player.x
|
||||
sy = @y - $game_player.y
|
||||
return if sx == 0 and sy == 0
|
||||
abs_sx = sx.abs
|
||||
abs_sy = sy.abs
|
||||
if abs_sx == abs_sy
|
||||
(rand(2) == 0) ? abs_sx += 1 : abs_sy += 1
|
||||
end
|
||||
if abs_sx > abs_sy
|
||||
(sx > 0) ? move_right : move_left
|
||||
if not moving? and sy != 0
|
||||
(sy > 0) ? move_down : move_up
|
||||
end
|
||||
else
|
||||
(sy > 0) ? move_down : move_up
|
||||
if not moving? and sx != 0
|
||||
(sx > 0) ? move_right : move_left
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def move_forward
|
||||
case @direction
|
||||
when 2; move_down(false)
|
||||
when 4; move_left(false)
|
||||
when 6; move_right(false)
|
||||
when 8; move_up(false)
|
||||
end
|
||||
end
|
||||
|
||||
def move_backward
|
||||
last_direction_fix = @direction_fix
|
||||
@direction_fix = true
|
||||
case @direction
|
||||
when 2; move_up(false)
|
||||
when 4; move_right(false)
|
||||
when 6; move_left(false)
|
||||
when 8; move_down(false)
|
||||
end
|
||||
@direction_fix = last_direction_fix
|
||||
end
|
||||
|
||||
def jump(x_plus, y_plus)
|
||||
if x_plus != 0 or y_plus != 0
|
||||
if x_plus.abs > y_plus.abs
|
||||
(x_plus < 0) ? turn_left : turn_right
|
||||
else
|
||||
(y_plus < 0) ? turn_up : turn_down
|
||||
end
|
||||
end
|
||||
new_x = @x + x_plus
|
||||
new_y = @y + y_plus
|
||||
if (x_plus == 0 and y_plus == 0) || passable?(new_x, new_y, 0)
|
||||
straighten
|
||||
@x = new_x
|
||||
@y = new_y
|
||||
distance = [4, x_plus * x_plus + y_plus * y_plus].max
|
||||
@jump_peak = (6 + distance - move_speed).floor
|
||||
@jump_count = @jump_peak * Graphics.frame_rate / 20
|
||||
@stop_count = 0
|
||||
if self.is_a?(Game_Player)
|
||||
$PokemonTemp.dependentEvents.pbMoveDependentEvents
|
||||
end
|
||||
triggerLeaveTile
|
||||
end
|
||||
end
|
||||
|
||||
def jumpForward
|
||||
case self.direction
|
||||
when 2; jump(0,1) # down
|
||||
when 4; jump(-1,0) # left
|
||||
when 6; jump(1,0) # right
|
||||
when 8; jump(0,-1) # up
|
||||
end
|
||||
end
|
||||
|
||||
def jumpBackward
|
||||
case self.direction
|
||||
when 2; jump(0,-1) # down
|
||||
when 4; jump(1,0) # left
|
||||
when 6; jump(-1,0) # right
|
||||
when 8; jump(0,1) # up
|
||||
end
|
||||
end
|
||||
|
||||
def turnGeneric(dir)
|
||||
return if @direction_fix
|
||||
oldDirection = @direction
|
||||
@direction = dir
|
||||
@stop_count = 0
|
||||
pbCheckEventTriggerAfterTurning if dir != oldDirection
|
||||
end
|
||||
|
||||
def turn_up; turnGeneric(8); end
|
||||
def turn_down; turnGeneric(2); end
|
||||
def turn_left; turnGeneric(4); end
|
||||
def turn_right; turnGeneric(6); end
|
||||
|
||||
def turn_right_90
|
||||
case @direction
|
||||
when 2; turn_left
|
||||
when 4; turn_up
|
||||
when 6; turn_down
|
||||
when 8; turn_right
|
||||
end
|
||||
end
|
||||
|
||||
def turn_left_90
|
||||
case @direction
|
||||
when 2; turn_right
|
||||
when 4; turn_down
|
||||
when 6; turn_up
|
||||
when 8; turn_left
|
||||
end
|
||||
end
|
||||
|
||||
def turn_180
|
||||
case @direction
|
||||
when 2; turn_up
|
||||
when 4; turn_right
|
||||
when 6; turn_left
|
||||
when 8; turn_down
|
||||
end
|
||||
end
|
||||
|
||||
def turn_right_or_left_90
|
||||
(rand(2) == 0) ? turn_right_90 : turn_left_90
|
||||
end
|
||||
|
||||
def turn_random
|
||||
case rand(4)
|
||||
when 0; turn_up
|
||||
when 1; turn_right
|
||||
when 2; turn_left
|
||||
when 3; turn_down
|
||||
end
|
||||
end
|
||||
|
||||
def turn_toward_player
|
||||
sx = @x - $game_player.x
|
||||
sy = @y - $game_player.y
|
||||
return if sx == 0 and sy == 0
|
||||
if sx.abs > sy.abs
|
||||
(sx > 0) ? turn_left : turn_right
|
||||
else
|
||||
(sy > 0) ? turn_up : turn_down
|
||||
end
|
||||
end
|
||||
|
||||
def turn_away_from_player
|
||||
sx = @x - $game_player.x
|
||||
sy = @y - $game_player.y
|
||||
return if sx == 0 and sy == 0
|
||||
if sx.abs > sy.abs
|
||||
(sx > 0) ? turn_right : turn_left
|
||||
else
|
||||
(sy > 0) ? turn_down : turn_up
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Updating
|
||||
#=============================================================================
|
||||
def update
|
||||
@moved_last_frame = @moved_this_frame
|
||||
if !$game_temp.in_menu
|
||||
# Update command
|
||||
update_command
|
||||
# Update movement
|
||||
if jumping?; update_jump
|
||||
elsif moving?; update_move
|
||||
else; update_stop
|
||||
end
|
||||
end
|
||||
# Update animation
|
||||
update_pattern
|
||||
end
|
||||
|
||||
def update_command
|
||||
if @wait_count > 0
|
||||
@wait_count -= 1
|
||||
elsif @move_route_forcing
|
||||
move_type_custom
|
||||
elsif !@starting && !lock? && !moving? && !jumping?
|
||||
update_command_new
|
||||
end
|
||||
end
|
||||
|
||||
def update_command_new
|
||||
# @stop_count is the number of frames since the last movement finished.
|
||||
# @move_frequency has these values:
|
||||
# 1 => @stop_count > 190 # 4.75 seconds
|
||||
# 2 => @stop_count > 144 # 3.6 seconds
|
||||
# 3 => @stop_count > 102 # 2.55 seconds
|
||||
# 4 => @stop_count > 64 # 1.6 seconds
|
||||
# 5 => @stop_count > 30 # 0.75 seconds
|
||||
# 6 => @stop_count > 0 # 0 seconds
|
||||
if @stop_count >= self.move_frequency_real
|
||||
case @move_type
|
||||
when 1; move_type_random
|
||||
when 2; move_type_toward_player
|
||||
when 3; move_type_custom
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update_jump
|
||||
@jump_count -= 1
|
||||
@real_x = (@real_x * @jump_count + @x * Game_Map::REAL_RES_X) / (@jump_count + 1)
|
||||
@real_y = (@real_y * @jump_count + @y * Game_Map::REAL_RES_Y) / (@jump_count + 1)
|
||||
@moved_this_frame = true
|
||||
# End of a jump, so perform events that happen at this time
|
||||
Events.onStepTakenFieldMovement.trigger(self,self) if !jumping? && !moving?
|
||||
end
|
||||
|
||||
def update_move
|
||||
# Move the character (the 0.1 catches rounding errors)
|
||||
distance = move_speed_real
|
||||
dest_x = @x * Game_Map::REAL_RES_X
|
||||
dest_y = @y * Game_Map::REAL_RES_Y
|
||||
if @real_x < dest_x
|
||||
@real_x += distance
|
||||
@real_x = dest_x if @real_x > dest_x - 0.1
|
||||
else
|
||||
@real_x -= distance
|
||||
@real_x = dest_x if @real_x < dest_x + 0.1
|
||||
end
|
||||
if @real_y < dest_y
|
||||
@real_y += distance
|
||||
@real_y = dest_y if @real_y > dest_y - 0.1
|
||||
else
|
||||
@real_y -= distance
|
||||
@real_y = dest_y if @real_y < dest_y + 0.1
|
||||
end
|
||||
# End of a step, so perform events that happen at this time
|
||||
Events.onStepTakenFieldMovement.trigger(self,self) if !jumping? && !moving?
|
||||
# Increment animation counter
|
||||
@anime_count += 1 if @walk_anime || @step_anime
|
||||
@moved_this_frame = true
|
||||
end
|
||||
|
||||
def update_stop
|
||||
@anime_count += 1 if @step_anime
|
||||
@stop_count += 1 if !@starting && !lock?
|
||||
@moved_this_frame = false
|
||||
end
|
||||
|
||||
def update_pattern
|
||||
return if @lock_pattern
|
||||
# Character has stopped moving, return to original pattern
|
||||
if @moved_last_frame && !@moved_this_frame && !@step_anime
|
||||
@pattern = @original_pattern
|
||||
@anime_count = 0
|
||||
return
|
||||
end
|
||||
# Character has started to move, change pattern immediately
|
||||
if !@moved_last_frame && @moved_this_frame && !jumping? && !@step_anime
|
||||
@pattern = (@pattern + 1) % 4 if @walk_anime
|
||||
@anime_count = 0
|
||||
return
|
||||
end
|
||||
# Calculate how many frames each pattern should display for, i.e. the time
|
||||
# it takes to move half a tile (or a whole tile if cycling). We assume the
|
||||
# game uses square tiles.
|
||||
frames_per_pattern = Game_Map::REAL_RES_X / (move_speed_real * 2.0)
|
||||
frames_per_pattern *= 2 if move_speed == 6 # Cycling/fastest speed
|
||||
return if @anime_count < frames_per_pattern
|
||||
# Advance to the next animation frame
|
||||
@pattern = (@pattern + 1) % 4
|
||||
@anime_count -= frames_per_pattern
|
||||
end
|
||||
end
|
||||
247
Data/Scripts/003_Game classes/006_Game_Event.rb
Normal file
247
Data/Scripts/003_Game classes/006_Game_Event.rb
Normal file
@@ -0,0 +1,247 @@
|
||||
class Game_Event < Game_Character
|
||||
attr_reader :map_id
|
||||
attr_reader :trigger
|
||||
attr_reader :list
|
||||
attr_reader :starting
|
||||
attr_reader :tempSwitches # Temporary self-switches
|
||||
attr_accessor :need_refresh
|
||||
|
||||
def initialize(map_id, event, map=nil)
|
||||
super(map)
|
||||
@map_id = map_id
|
||||
@event = event
|
||||
@id = @event.id
|
||||
@original_x = @event.x
|
||||
@original_y = @event.y
|
||||
@erased = false
|
||||
@starting = false
|
||||
@need_refresh = false
|
||||
@route_erased = false
|
||||
@through = true
|
||||
@to_update = true
|
||||
@tempSwitches = {}
|
||||
moveto(@event.x, @event.y) if map
|
||||
refresh
|
||||
end
|
||||
|
||||
def id; return @event.id; end
|
||||
def name; return @event.name; end
|
||||
|
||||
def clear_starting
|
||||
@starting = false
|
||||
end
|
||||
|
||||
def start
|
||||
@starting = true if @list.size > 1
|
||||
end
|
||||
|
||||
def erase
|
||||
@erased = true
|
||||
refresh
|
||||
end
|
||||
|
||||
def erase_route
|
||||
@route_erased = true
|
||||
refresh
|
||||
end
|
||||
|
||||
def tsOn?(c)
|
||||
return @tempSwitches && @tempSwitches[c]==true
|
||||
end
|
||||
|
||||
def tsOff?(c)
|
||||
return !@tempSwitches || !@tempSwitches[c]
|
||||
end
|
||||
|
||||
def setTempSwitchOn(c)
|
||||
@tempSwitches[c]=true
|
||||
refresh
|
||||
end
|
||||
|
||||
def setTempSwitchOff(c)
|
||||
@tempSwitches[c]=false
|
||||
refresh
|
||||
end
|
||||
|
||||
def isOff?(c)
|
||||
return !$game_self_switches[[@map_id,@event.id,c]]
|
||||
end
|
||||
|
||||
def switchIsOn?(id)
|
||||
switchname = $data_system.switches[id]
|
||||
return false if !switchname
|
||||
if switchname[/^s\:/]
|
||||
return eval($~.post_match)
|
||||
else
|
||||
return $game_switches[id]
|
||||
end
|
||||
end
|
||||
|
||||
def variable
|
||||
return nil if !$PokemonGlobal.eventvars
|
||||
return $PokemonGlobal.eventvars[[@map_id,@event.id]]
|
||||
end
|
||||
|
||||
def setVariable(variable)
|
||||
$PokemonGlobal.eventvars[[@map_id,@event.id]]=variable
|
||||
end
|
||||
|
||||
def varAsInt
|
||||
return 0 if !$PokemonGlobal.eventvars
|
||||
return $PokemonGlobal.eventvars[[@map_id,@event.id]].to_i
|
||||
end
|
||||
|
||||
def expired?(secs=86400)
|
||||
ontime=self.variable
|
||||
time=pbGetTimeNow
|
||||
return ontime && (time.to_i>ontime+secs)
|
||||
end
|
||||
|
||||
def expiredDays?(days=1)
|
||||
ontime=self.variable.to_i
|
||||
return false if !ontime
|
||||
now=pbGetTimeNow
|
||||
elapsed=(now.to_i-ontime)/86400
|
||||
elapsed+=1 if (now.to_i-ontime)%86400>(now.hour*3600+now.min*60+now.sec)
|
||||
return elapsed>=days
|
||||
end
|
||||
|
||||
def onEvent?
|
||||
return @map_id==$game_map.map_id &&
|
||||
$game_player.x==self.x && $game_player.y==self.y
|
||||
end
|
||||
|
||||
def over_trigger?
|
||||
return false if @character_name!="" and not @through
|
||||
return false if @event.name[/hiddenitem/i]
|
||||
return false if !self.map.passable?(@x, @y, 0, $game_player)
|
||||
return true
|
||||
end
|
||||
|
||||
def pbCheckEventTriggerAfterTurning
|
||||
return if $game_system.map_interpreter.running? || @starting
|
||||
if @event.name[/trainer\((\d+)\)/i]
|
||||
distance = $~[1].to_i
|
||||
if @trigger==2 && pbEventCanReachPlayer?(self,$game_player,distance)
|
||||
start if !jumping? && !over_trigger?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_event_trigger_touch(x, y)
|
||||
return if $game_system.map_interpreter.running?
|
||||
return if @trigger!=2
|
||||
return if x != $game_player.x || y != $game_player.y
|
||||
return if jumping? || over_trigger?
|
||||
start
|
||||
end
|
||||
|
||||
def check_event_trigger_auto
|
||||
if @trigger == 2 # Event touch
|
||||
if @x == $game_player.x and @y == $game_player.y
|
||||
start if not jumping? and over_trigger?
|
||||
end
|
||||
elsif @trigger == 3 # Autorun
|
||||
start
|
||||
end
|
||||
end
|
||||
|
||||
def refresh
|
||||
new_page = nil
|
||||
unless @erased
|
||||
for page in @event.pages.reverse
|
||||
c = page.condition
|
||||
next if c.switch1_valid && !switchIsOn?(c.switch1_id)
|
||||
next if c.switch2_valid && !switchIsOn?(c.switch2_id)
|
||||
next if c.variable_valid && $game_variables[c.variable_id] < c.variable_value
|
||||
if c.self_switch_valid
|
||||
key = [@map_id, @event.id, c.self_switch_ch]
|
||||
next if $game_self_switches[key] != true
|
||||
end
|
||||
new_page = page
|
||||
break
|
||||
end
|
||||
end
|
||||
return if new_page == @page
|
||||
@page = new_page
|
||||
clear_starting
|
||||
if @page == nil
|
||||
@tile_id = 0
|
||||
@character_name = ""
|
||||
@character_hue = 0
|
||||
@move_type = 0
|
||||
@through = true
|
||||
@trigger = nil
|
||||
@list = nil
|
||||
@interpreter = nil
|
||||
return
|
||||
end
|
||||
@tile_id = @page.graphic.tile_id
|
||||
@character_name = @page.graphic.character_name
|
||||
@character_hue = @page.graphic.character_hue
|
||||
if @original_direction != @page.graphic.direction
|
||||
@direction = @page.graphic.direction
|
||||
@original_direction = @direction
|
||||
@prelock_direction = 0
|
||||
end
|
||||
if @original_pattern != @page.graphic.pattern
|
||||
@pattern = @page.graphic.pattern
|
||||
@original_pattern = @pattern
|
||||
end
|
||||
@opacity = @page.graphic.opacity
|
||||
@blend_type = @page.graphic.blend_type
|
||||
@move_type = @page.move_type
|
||||
self.move_speed = @page.move_speed
|
||||
self.move_frequency = @page.move_frequency
|
||||
@move_route = (@route_erased) ? RPG::MoveRoute.new : @page.move_route
|
||||
@move_route_index = 0
|
||||
@move_route_forcing = false
|
||||
@walk_anime = @page.walk_anime
|
||||
@step_anime = @page.step_anime
|
||||
@direction_fix = @page.direction_fix
|
||||
@through = @page.through
|
||||
@always_on_top = @page.always_on_top
|
||||
@trigger = @page.trigger
|
||||
@list = @page.list
|
||||
@interpreter = nil
|
||||
if @trigger == 4 # Parallel Process
|
||||
@interpreter = Interpreter.new
|
||||
end
|
||||
check_event_trigger_auto
|
||||
end
|
||||
|
||||
def should_update?(recalc=false)
|
||||
return @to_update if !recalc
|
||||
return true if $PokemonSystem.tilemap==2
|
||||
return true if @trigger && (@trigger == 3 || @trigger == 4)
|
||||
return true if @move_route_forcing
|
||||
return true if @event.name[/update/i]
|
||||
range = 2 # Number of tiles
|
||||
return false if self.screen_x - @sprite_size[0]/2 > Graphics.width + range * Game_Map::TILE_WIDTH
|
||||
return false if self.screen_x + @sprite_size[0]/2 < -range * Game_Map::TILE_WIDTH
|
||||
return false if self.screen_y_ground - @sprite_size[1] > Graphics.height + range * Game_Map::TILE_HEIGHT
|
||||
return false if self.screen_y_ground < -range * Game_Map::TILE_HEIGHT
|
||||
return true
|
||||
end
|
||||
|
||||
def update
|
||||
@to_update = should_update?(true)
|
||||
return if !@to_update
|
||||
last_moving = moving?
|
||||
super
|
||||
if !moving? && last_moving
|
||||
$game_player.pbCheckEventTriggerFromDistance([2])
|
||||
end
|
||||
if @need_refresh
|
||||
@need_refresh = false
|
||||
refresh
|
||||
end
|
||||
check_event_trigger_auto
|
||||
if @interpreter != nil
|
||||
unless @interpreter.running?
|
||||
@interpreter.setup(@list, @event.id, @map_id)
|
||||
end
|
||||
@interpreter.update
|
||||
end
|
||||
end
|
||||
end
|
||||
505
Data/Scripts/003_Game classes/007_Game_Player.rb
Normal file
505
Data/Scripts/003_Game classes/007_Game_Player.rb
Normal file
@@ -0,0 +1,505 @@
|
||||
#===============================================================================
|
||||
# ** Game_Player
|
||||
#-------------------------------------------------------------------------------
|
||||
# This class handles the player. Its functions include event starting
|
||||
# determinants and map scrolling. Refer to "$game_player" for the one
|
||||
# instance of this class.
|
||||
#===============================================================================
|
||||
class Game_Player < Game_Character
|
||||
attr_accessor :bump_se
|
||||
attr_accessor :charsetData
|
||||
attr_accessor :encounter_count
|
||||
|
||||
def initialize(*arg)
|
||||
super(*arg)
|
||||
@lastdir=0
|
||||
@lastdirframe=0
|
||||
@bump_se=0
|
||||
end
|
||||
|
||||
def map
|
||||
@map = nil
|
||||
return $game_map
|
||||
end
|
||||
|
||||
def bush_depth
|
||||
return 0 if @tile_id > 0 or @always_on_top
|
||||
xbehind = (@direction==4) ? @x+1 : (@direction==6) ? @x-1 : @x
|
||||
ybehind = (@direction==8) ? @y+1 : (@direction==2) ? @y-1 : @y
|
||||
# Both current tile and previous tile are on the same map; just return super
|
||||
return super if $game_map.valid?(@x,@y) && $game_map.valid?(xbehind,ybehind)
|
||||
# The current or the previous tile is on a different map; consult MapFactory
|
||||
return 0 if !$MapFactory
|
||||
# Get map and coordinates of the current tile
|
||||
if $game_map.valid?(@x,@y)
|
||||
heremap = self.map; herex = @x; herey = @y
|
||||
else
|
||||
newhere = $MapFactory.getNewMap(@x,@y)
|
||||
return 0 unless newhere && newhere[0] # Map not found
|
||||
heremap = newhere[0]; herex = newhere[1]; herey = newhere[2]
|
||||
end
|
||||
# Get map and coordinates of the previous tile
|
||||
newbehind = $MapFactory.getNewMap(xbehind,ybehind)
|
||||
if $game_map.valid?(xbehind,ybehind)
|
||||
behindmap = self.map; behindx = xbehind; behindy = ybehind
|
||||
else
|
||||
return 0 unless newbehind && newbehind[0] # Map not found
|
||||
behindmap = newbehind[0]; behindx = newbehind[1]; behindy = newbehind[2]
|
||||
end
|
||||
# Return bush depth
|
||||
if @jump_count <= 0
|
||||
return 32 if heremap.deepBush?(herex, herey) && behindmap.deepBush?(behindx, behindy)
|
||||
return 12 if heremap.bush?(herex, herey) && !moving?
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
def pbHasDependentEvents?
|
||||
return $PokemonGlobal.dependentEvents.length>0
|
||||
end
|
||||
|
||||
def bump_into_object
|
||||
return if @bump_se && @bump_se>0
|
||||
pbSEPlay("Player bump")
|
||||
@bump_se = Graphics.frame_rate/4
|
||||
end
|
||||
|
||||
def move_down(turn_enabled = true)
|
||||
turn_down if turn_enabled
|
||||
if passable?(@x, @y, 2)
|
||||
return if pbLedge(0,1)
|
||||
return if pbEndSurf(0,1)
|
||||
turn_down
|
||||
@y += 1
|
||||
$PokemonTemp.dependentEvents.pbMoveDependentEvents
|
||||
increase_steps
|
||||
else
|
||||
if !check_event_trigger_touch(@x, @y+1)
|
||||
bump_into_object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def move_left(turn_enabled = true)
|
||||
turn_left if turn_enabled
|
||||
if passable?(@x, @y, 4)
|
||||
return if pbLedge(-1,0)
|
||||
return if pbEndSurf(-1,0)
|
||||
turn_left
|
||||
@x -= 1
|
||||
$PokemonTemp.dependentEvents.pbMoveDependentEvents
|
||||
increase_steps
|
||||
else
|
||||
if !check_event_trigger_touch(@x-1, @y)
|
||||
bump_into_object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def move_right(turn_enabled = true)
|
||||
turn_right if turn_enabled
|
||||
if passable?(@x, @y, 6)
|
||||
return if pbLedge(1,0)
|
||||
return if pbEndSurf(1,0)
|
||||
turn_right
|
||||
@x += 1
|
||||
$PokemonTemp.dependentEvents.pbMoveDependentEvents
|
||||
increase_steps
|
||||
else
|
||||
if !check_event_trigger_touch(@x+1, @y)
|
||||
bump_into_object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def move_up(turn_enabled = true)
|
||||
turn_up if turn_enabled
|
||||
if passable?(@x, @y, 8)
|
||||
return if pbLedge(0,-1)
|
||||
return if pbEndSurf(0,-1)
|
||||
turn_up
|
||||
@y -= 1
|
||||
$PokemonTemp.dependentEvents.pbMoveDependentEvents
|
||||
increase_steps
|
||||
else
|
||||
if !check_event_trigger_touch(@x, @y-1)
|
||||
bump_into_object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbTriggeredTrainerEvents(triggers,checkIfRunning=true)
|
||||
result = []
|
||||
# If event is running
|
||||
return result if checkIfRunning && $game_system.map_interpreter.running?
|
||||
# All event loops
|
||||
for event in $game_map.events.values
|
||||
next if !event.name[/trainer\((\d+)\)/i]
|
||||
distance = $~[1].to_i
|
||||
# If event coordinates and triggers are consistent
|
||||
if pbEventCanReachPlayer?(event,self,distance) and triggers.include?(event.trigger)
|
||||
# If starting determinant is front event (other than jumping)
|
||||
result.push(event) if not event.jumping? and not event.over_trigger?
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
def pbTriggeredCounterEvents(triggers,checkIfRunning=true)
|
||||
result = []
|
||||
# If event is running
|
||||
return result if checkIfRunning && $game_system.map_interpreter.running?
|
||||
# All event loops
|
||||
for event in $game_map.events.values
|
||||
next if !event.name[/counter\((\d+)\)/i]
|
||||
distance = $~[1].to_i
|
||||
# If event coordinates and triggers are consistent
|
||||
if pbEventFacesPlayer?(event,self,distance) and triggers.include?(event.trigger)
|
||||
# If starting determinant is front event (other than jumping)
|
||||
result.push(event) if not event.jumping? and not event.over_trigger?
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
def pbCheckEventTriggerAfterTurning
|
||||
end
|
||||
|
||||
def pbCheckEventTriggerFromDistance(triggers)
|
||||
ret = pbTriggeredTrainerEvents(triggers)
|
||||
ret.concat(pbTriggeredCounterEvents(triggers))
|
||||
return false if ret.length==0
|
||||
for event in ret
|
||||
event.start
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbFacingEvent(ignoreInterpreter=false)
|
||||
return nil if $game_system.map_interpreter.running? && !ignoreInterpreter
|
||||
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
|
||||
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
|
||||
return nil if !$game_map.valid?(new_x, new_y)
|
||||
for event in $game_map.events.values
|
||||
next if event.x != new_x || event.y != new_y
|
||||
next if event.jumping? || event.over_trigger?
|
||||
return event
|
||||
end
|
||||
if $game_map.counter?(new_x, new_y)
|
||||
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
|
||||
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
|
||||
for event in $game_map.events.values
|
||||
next if event.x != new_x || event.y != new_y
|
||||
next if event.jumping? || event.over_trigger?
|
||||
return event
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Passable Determinants
|
||||
# x : x-coordinate
|
||||
# y : y-coordinate
|
||||
# d : direction (0,2,4,6,8)
|
||||
# * 0 = Determines if all directions are impassable (for jumping)
|
||||
#-----------------------------------------------------------------------------
|
||||
def passable?(x, y, d)
|
||||
# Get new coordinates
|
||||
new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0)
|
||||
new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0)
|
||||
# If coordinates are outside of map
|
||||
return false if !$game_map.validLax?(new_x, new_y)
|
||||
if !$game_map.valid?(new_x, new_y)
|
||||
return false if !$MapFactory
|
||||
return $MapFactory.isPassableFromEdge?(new_x, new_y)
|
||||
end
|
||||
# If debug mode is ON and Ctrl key was pressed
|
||||
return true if $DEBUG and Input.press?(Input::CTRL)
|
||||
return super
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Set Map Display Position to Center of Screen
|
||||
#-----------------------------------------------------------------------------
|
||||
def center(x, y)
|
||||
center_x = (Graphics.width/2 - Game_Map::TILE_WIDTH/2) * Game_Map::X_SUBPIXELS
|
||||
center_y = (Graphics.height/2 - Game_Map::TILE_HEIGHT/2) * Game_Map::Y_SUBPIXELS
|
||||
max_x = (self.map.width - Graphics.width*1.0/Game_Map::TILE_WIDTH) * Game_Map::REAL_RES_X
|
||||
max_y = (self.map.height - Graphics.height*1.0/Game_Map::TILE_HEIGHT) * Game_Map::REAL_RES_Y
|
||||
dispx = x * Game_Map::REAL_RES_X - center_x
|
||||
dispy = y * Game_Map::REAL_RES_Y - center_y
|
||||
self.map.display_x = dispx
|
||||
self.map.display_y = dispy
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Move to Designated Position
|
||||
# x : x-coordinate
|
||||
# y : y-coordinate
|
||||
#-----------------------------------------------------------------------------
|
||||
def moveto(x, y)
|
||||
super
|
||||
# Centering
|
||||
center(x, y)
|
||||
# Make encounter count
|
||||
make_encounter_count
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Make Encounter Count
|
||||
#-----------------------------------------------------------------------------
|
||||
def make_encounter_count
|
||||
# Image of two dice rolling
|
||||
if $game_map.map_id != 0
|
||||
n = $game_map.encounter_step
|
||||
@encounter_count = rand(n) + rand(n) + 1
|
||||
end
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Refresh
|
||||
#-----------------------------------------------------------------------------
|
||||
def refresh
|
||||
@opacity = 255
|
||||
@blend_type = 0
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Same Position Starting Determinant
|
||||
#-----------------------------------------------------------------------------
|
||||
def check_event_trigger_here(triggers)
|
||||
result = false
|
||||
# If event is running
|
||||
return result if $game_system.map_interpreter.running?
|
||||
# All event loops
|
||||
for event in $game_map.events.values
|
||||
# If event coordinates and triggers are consistent
|
||||
next if event.x != @x || event.y != @y
|
||||
next if !triggers.include?(event.trigger)
|
||||
# If starting determinant is same position event (other than jumping)
|
||||
next if event.jumping? || !event.over_trigger?
|
||||
event.start
|
||||
result = true
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Front Event Starting Determinant
|
||||
#-----------------------------------------------------------------------------
|
||||
def check_event_trigger_there(triggers)
|
||||
result = false
|
||||
# If event is running
|
||||
return result if $game_system.map_interpreter.running?
|
||||
# Calculate front event coordinates
|
||||
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
|
||||
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
|
||||
return false if !$game_map.valid?(new_x, new_y)
|
||||
# All event loops
|
||||
for event in $game_map.events.values
|
||||
# If event coordinates and triggers are consistent
|
||||
next if event.x != new_x || event.y != new_y
|
||||
next if !triggers.include?(event.trigger)
|
||||
# If starting determinant is front event (other than jumping)
|
||||
next if event.jumping? || event.over_trigger?
|
||||
event.start
|
||||
result = true
|
||||
end
|
||||
# If fitting event is not found
|
||||
if result == false
|
||||
# If front tile is a counter
|
||||
if $game_map.counter?(new_x, new_y)
|
||||
# Calculate coordinates of 1 tile further away
|
||||
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
|
||||
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
|
||||
return false if !$game_map.valid?(new_x, new_y)
|
||||
# All event loops
|
||||
for event in $game_map.events.values
|
||||
# If event coordinates and triggers are consistent
|
||||
next if event.x != new_x || event.y != new_y
|
||||
next if !triggers.include?(event.trigger)
|
||||
# If starting determinant is front event (other than jumping)
|
||||
next if event.jumping? || event.over_trigger?
|
||||
event.start
|
||||
result = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Touch Event Starting Determinant
|
||||
#-----------------------------------------------------------------------------
|
||||
def check_event_trigger_touch(x, y)
|
||||
result = false
|
||||
# If event is running
|
||||
return result if $game_system.map_interpreter.running?
|
||||
# All event loops
|
||||
for event in $game_map.events.values
|
||||
# If event coordinates and triggers are consistent
|
||||
next if event.x != x || event.y != y
|
||||
if event.name[/trainer\((\d+)\)/i]
|
||||
distance = $~[1].to_i
|
||||
next if !pbEventCanReachPlayer?(event,self,distance)
|
||||
elsif event.name[/counter\((\d+)\)/i]
|
||||
distance = $~[1].to_i
|
||||
next if !pbEventFacesPlayer?(event,self,distance)
|
||||
end
|
||||
next if ![1,2].include?(event.trigger)
|
||||
# If starting determinant is front event (other than jumping)
|
||||
next if event.jumping? || event.over_trigger?
|
||||
event.start
|
||||
result = true
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Frame Update
|
||||
#-----------------------------------------------------------------------------
|
||||
def update
|
||||
last_real_x = @real_x
|
||||
last_real_y = @real_y
|
||||
super
|
||||
update_screen_position(last_real_x, last_real_y)
|
||||
# Update dependent events
|
||||
$PokemonTemp.dependentEvents.updateDependentEvents
|
||||
# Count down the time between allowed bump sounds
|
||||
@bump_se -= 1 if @bump_se && @bump_se>0
|
||||
# Finish up dismounting from surfing
|
||||
if $PokemonTemp.endSurf && !moving?
|
||||
pbCancelVehicles
|
||||
$PokemonTemp.surfJump = nil
|
||||
$PokemonTemp.endSurf = false
|
||||
end
|
||||
update_event_triggering
|
||||
end
|
||||
|
||||
def update_command_new
|
||||
dir = Input.dir4
|
||||
unless pbMapInterpreterRunning? or $game_temp.message_window_showing or
|
||||
$PokemonTemp.miniupdate or $game_temp.in_menu
|
||||
# Move player in the direction the directional button is being pressed
|
||||
if dir==@lastdir && Graphics.frame_count-@lastdirframe>Graphics.frame_rate/20
|
||||
case dir
|
||||
when 2; move_down
|
||||
when 4; move_left
|
||||
when 6; move_right
|
||||
when 8; move_up
|
||||
end
|
||||
elsif dir!=@lastdir
|
||||
case dir
|
||||
when 2; turn_down
|
||||
when 4; turn_left
|
||||
when 6; turn_right
|
||||
when 8; turn_up
|
||||
end
|
||||
end
|
||||
end
|
||||
# Record last direction input
|
||||
@lastdirframe = Graphics.frame_count if dir!=@lastdir
|
||||
@lastdir = dir
|
||||
end
|
||||
|
||||
# Center player on-screen
|
||||
def update_screen_position(last_real_x, last_real_y)
|
||||
return if !@moved_this_frame
|
||||
center_x = (Graphics.width/2 - Game_Map::TILE_WIDTH/2) * Game_Map::X_SUBPIXELS
|
||||
center_y = (Graphics.height/2 - Game_Map::TILE_HEIGHT/2) * Game_Map::Y_SUBPIXELS
|
||||
if @real_y < last_real_y and @real_y - $game_map.display_y < center_y
|
||||
$game_map.scroll_up(last_real_y - @real_y)
|
||||
end
|
||||
if @real_y > last_real_y and @real_y - $game_map.display_y > center_y
|
||||
$game_map.scroll_down(@real_y - last_real_y)
|
||||
end
|
||||
if @real_x < last_real_x and @real_x - $game_map.display_x < center_x
|
||||
$game_map.scroll_left(last_real_x - @real_x)
|
||||
end
|
||||
if @real_x > last_real_x and @real_x - $game_map.display_x > center_x
|
||||
$game_map.scroll_right(@real_x - last_real_x)
|
||||
end
|
||||
end
|
||||
|
||||
def update_event_triggering
|
||||
return if moving?
|
||||
# Try triggering events upon walking into them/in front of them
|
||||
if @moved_this_frame
|
||||
$PokemonTemp.dependentEvents.pbTurnDependentEvents
|
||||
result = pbCheckEventTriggerFromDistance([2])
|
||||
# Event determinant is via touch of same position event
|
||||
result |= check_event_trigger_here([1,2])
|
||||
# No events triggered, try other event triggers upon finishing a step
|
||||
pbOnStepTaken(result)
|
||||
end
|
||||
# If C button was pressed, try to manually interact with events
|
||||
if Input.trigger?(Input::C) && !$PokemonTemp.miniupdate
|
||||
# Same position and front event determinant
|
||||
check_event_trigger_here([0])
|
||||
check_event_trigger_there([0,2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def pbGetPlayerCharset(meta,charset,trainer=nil,force=false)
|
||||
trainer = $Trainer if !trainer
|
||||
outfit = (trainer) ? trainer.outfit : 0
|
||||
if $game_player && $game_player.charsetData && !force
|
||||
return nil if $game_player.charsetData[0]==$PokemonGlobal.playerID &&
|
||||
$game_player.charsetData[1]==charset &&
|
||||
$game_player.charsetData[2]==outfit
|
||||
end
|
||||
$game_player.charsetData = [$PokemonGlobal.playerID,charset,outfit] if $game_player
|
||||
ret = meta[charset]
|
||||
ret = meta[1] if !ret || ret==""
|
||||
if pbResolveBitmap("Graphics/Characters/"+ret+"_"+outfit.to_s)
|
||||
ret = ret+"_"+outfit.to_s
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbUpdateVehicle
|
||||
meta = pbGetMetadata(0,MetadataPlayerA+$PokemonGlobal.playerID)
|
||||
if meta
|
||||
newCharName = nil
|
||||
charset = 1 # Regular graphic
|
||||
if $PokemonGlobal.diving; charset = 5 # Diving graphic
|
||||
elsif $PokemonGlobal.surfing; charset = 3 # Surfing graphic
|
||||
elsif $PokemonGlobal.bicycle; charset = 2 # Bicycle graphic
|
||||
end
|
||||
newCharName = pbGetPlayerCharset(meta,charset)
|
||||
$game_player.character_name = newCharName if newCharName
|
||||
end
|
||||
end
|
||||
|
||||
def pbCancelVehicles(destination=nil)
|
||||
$PokemonGlobal.surfing = false
|
||||
$PokemonGlobal.diving = false
|
||||
$PokemonGlobal.bicycle = false if !destination || !pbCanUseBike?(destination)
|
||||
pbUpdateVehicle
|
||||
end
|
||||
|
||||
def pbCanUseBike?(mapid)
|
||||
return true if pbGetMetadata(mapid,MetadataBicycleAlways)
|
||||
val = pbGetMetadata(mapid,MetadataBicycle)
|
||||
val = pbGetMetadata(mapid,MetadataOutdoor) if val==nil
|
||||
return (val) ? true : false
|
||||
end
|
||||
|
||||
def pbMountBike
|
||||
return if $PokemonGlobal.bicycle
|
||||
$PokemonGlobal.bicycle = true
|
||||
pbUpdateVehicle
|
||||
bikebgm = pbGetMetadata(0,MetadataBicycleBGM)
|
||||
pbCueBGM(bikebgm,0.5) if bikebgm
|
||||
end
|
||||
|
||||
def pbDismountBike
|
||||
return if !$PokemonGlobal.bicycle
|
||||
$PokemonGlobal.bicycle = false
|
||||
pbUpdateVehicle
|
||||
$game_map.autoplayAsCue
|
||||
end
|
||||
103
Data/Scripts/003_Game classes/008_Game_Player_Visuals.rb
Normal file
103
Data/Scripts/003_Game classes/008_Game_Player_Visuals.rb
Normal file
@@ -0,0 +1,103 @@
|
||||
class Game_Player < Game_Character
|
||||
@@bobFrameSpeed = 1.0/15
|
||||
|
||||
def fullPattern
|
||||
case self.direction
|
||||
when 2; return self.pattern
|
||||
when 4; return 4+self.pattern
|
||||
when 6; return 8+self.pattern
|
||||
when 8; return 12+self.pattern
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
def setDefaultCharName(chname,pattern,lockpattern=false)
|
||||
return if pattern<0 || pattern>=16
|
||||
@defaultCharacterName = chname
|
||||
@direction = [2,4,6,8][pattern/4]
|
||||
@pattern = pattern%4
|
||||
@lock_pattern = lockpattern
|
||||
end
|
||||
|
||||
def pbCanRun?
|
||||
return false if $game_temp.in_menu || $game_temp.in_battle ||
|
||||
@move_route_forcing || $game_temp.message_window_showing ||
|
||||
pbMapInterpreterRunning?
|
||||
terrain = pbGetTerrainTag
|
||||
input = ($PokemonSystem.runstyle==1) ? $PokemonGlobal.runtoggle : Input.press?(Input::A)
|
||||
return input && $PokemonGlobal.runningShoes &&
|
||||
!$PokemonGlobal.diving && !$PokemonGlobal.surfing &&
|
||||
!$PokemonGlobal.bicycle && !PBTerrain.onlyWalk?(terrain)
|
||||
end
|
||||
|
||||
def pbIsRunning?
|
||||
return moving? && !@move_route_forcing && pbCanRun?
|
||||
end
|
||||
|
||||
def character_name
|
||||
@defaultCharacterName = "" if !@defaultCharacterName
|
||||
return @defaultCharacterName if @defaultCharacterName!=""
|
||||
if !@move_route_forcing && $PokemonGlobal.playerID>=0
|
||||
meta = pbGetMetadata(0,MetadataPlayerA+$PokemonGlobal.playerID)
|
||||
if meta && !$PokemonGlobal.bicycle && !$PokemonGlobal.diving && !$PokemonGlobal.surfing
|
||||
charset = 1 # Display normal character sprite
|
||||
if pbCanRun? && (moving? || @wasmoving) && Input.dir4!=0 && meta[4] && meta[4]!=""
|
||||
charset = 4 # Display running character sprite
|
||||
end
|
||||
newCharName = pbGetPlayerCharset(meta,charset)
|
||||
@character_name = newCharName if newCharName
|
||||
@wasmoving = moving?
|
||||
end
|
||||
end
|
||||
return @character_name
|
||||
end
|
||||
|
||||
def update_command
|
||||
if PBTerrain.isIce?(pbGetTerrainTag)
|
||||
self.move_speed = 5 # Sliding on ice
|
||||
elsif !moving? && !@move_route_forcing && $PokemonGlobal
|
||||
if $PokemonGlobal.bicycle
|
||||
self.move_speed = 6 # Cycling
|
||||
elsif pbCanRun? || $PokemonGlobal.surfing || $PokemonGlobal.diving
|
||||
self.move_speed = 5 # Running, surfing or diving
|
||||
else
|
||||
self.move_speed = 4 # Walking
|
||||
end
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def update_pattern
|
||||
if $PokemonGlobal.surfing || $PokemonGlobal.diving
|
||||
p = ((Graphics.frame_count%60)*@@bobFrameSpeed).floor
|
||||
@pattern = p if !@lock_pattern
|
||||
@pattern_surf = p
|
||||
@bob_height = (p>=2) ? 2 : 0
|
||||
else
|
||||
@bob_height = 0
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
=begin
|
||||
class Game_Character
|
||||
alias update_old2 update
|
||||
|
||||
def update
|
||||
if self.is_a?(Game_Event)
|
||||
if @dependentEvents
|
||||
for i in 0...@dependentEvents.length
|
||||
if @dependentEvents[i][0]==$game_map.map_id &&
|
||||
@dependentEvents[i][1]==self.id
|
||||
self.move_speed_real = $game_player.move_speed_real
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
update_old2
|
||||
end
|
||||
end
|
||||
=end
|
||||
438
Data/Scripts/003_Game classes/009_Game_Map.rb
Normal file
438
Data/Scripts/003_Game classes/009_Game_Map.rb
Normal file
@@ -0,0 +1,438 @@
|
||||
#==============================================================================
|
||||
# ** Game_Map
|
||||
#------------------------------------------------------------------------------
|
||||
# This class handles the map. It includes scrolling and passable determining
|
||||
# functions. Refer to "$game_map" for the instance of this class.
|
||||
#==============================================================================
|
||||
class Game_Map
|
||||
attr_accessor :map_id
|
||||
attr_accessor :tileset_name # tileset file name
|
||||
attr_accessor :autotile_names # autotile file name
|
||||
attr_reader :passages # passage table
|
||||
attr_reader :priorities # prioroty table
|
||||
attr_reader :terrain_tags # terrain tag table
|
||||
attr_reader :events # events
|
||||
attr_accessor :panorama_name # panorama file name
|
||||
attr_accessor :panorama_hue # panorama hue
|
||||
attr_accessor :fog_name # fog file name
|
||||
attr_accessor :fog_hue # fog hue
|
||||
attr_accessor :fog_opacity # fog opacity level
|
||||
attr_accessor :fog_blend_type # fog blending method
|
||||
attr_accessor :fog_zoom # fog zoom rate
|
||||
attr_accessor :fog_sx # fog sx
|
||||
attr_accessor :fog_sy # fog sy
|
||||
attr_reader :fog_ox # fog x-coordinate starting point
|
||||
attr_reader :fog_oy # fog y-coordinate starting point
|
||||
attr_reader :fog_tone # fog color tone
|
||||
attr_accessor :battleback_name # battleback file name
|
||||
attr_accessor :display_x # display x-coordinate * 128
|
||||
attr_accessor :display_y # display y-coordinate * 128
|
||||
attr_accessor :need_refresh # refresh request flag
|
||||
|
||||
TILE_WIDTH = 32
|
||||
TILE_HEIGHT = 32
|
||||
X_SUBPIXELS = ($RPGVX) ? 8 : 4
|
||||
Y_SUBPIXELS = ($RPGVX) ? 8 : 4
|
||||
REAL_RES_X = TILE_WIDTH * X_SUBPIXELS
|
||||
REAL_RES_Y = TILE_HEIGHT * Y_SUBPIXELS
|
||||
|
||||
def initialize
|
||||
@map_id = 0
|
||||
@display_x = 0
|
||||
@display_y = 0
|
||||
end
|
||||
|
||||
def setup(map_id)
|
||||
@map_id = map_id
|
||||
@map = load_data(sprintf("Data/Map%03d.%s",map_id,($RPGVX) ? "rvdata" : "rxdata"))
|
||||
tileset = $data_tilesets[@map.tileset_id]
|
||||
updateTileset
|
||||
@fog_ox = 0
|
||||
@fog_oy = 0
|
||||
@fog_tone = Tone.new(0, 0, 0, 0)
|
||||
@fog_tone_target = Tone.new(0, 0, 0, 0)
|
||||
@fog_tone_duration = 0
|
||||
@fog_opacity_duration = 0
|
||||
@fog_opacity_target = 0
|
||||
self.display_x = 0
|
||||
self.display_y = 0
|
||||
@need_refresh = false
|
||||
Events.onMapCreate.trigger(self,map_id,@map,tileset)
|
||||
@events = {}
|
||||
for i in @map.events.keys
|
||||
@events[i] = Game_Event.new(@map_id, @map.events[i],self)
|
||||
end
|
||||
@common_events = {}
|
||||
for i in 1...$data_common_events.size
|
||||
@common_events[i] = Game_CommonEvent.new(i)
|
||||
end
|
||||
@scroll_direction = 2
|
||||
@scroll_rest = 0
|
||||
@scroll_speed = 4
|
||||
end
|
||||
|
||||
def updateTileset
|
||||
tileset = $data_tilesets[@map.tileset_id]
|
||||
@tileset_name = tileset.tileset_name
|
||||
@autotile_names = tileset.autotile_names
|
||||
@panorama_name = tileset.panorama_name
|
||||
@panorama_hue = tileset.panorama_hue
|
||||
@fog_name = tileset.fog_name
|
||||
@fog_hue = tileset.fog_hue
|
||||
@fog_opacity = tileset.fog_opacity
|
||||
@fog_blend_type = tileset.fog_blend_type
|
||||
@fog_zoom = tileset.fog_zoom
|
||||
@fog_sx = tileset.fog_sx
|
||||
@fog_sy = tileset.fog_sy
|
||||
@battleback_name = tileset.battleback_name
|
||||
@passages = tileset.passages
|
||||
@priorities = tileset.priorities
|
||||
@terrain_tags = tileset.terrain_tags
|
||||
end
|
||||
|
||||
def width; return @map.width; end
|
||||
def height; return @map.height; end
|
||||
def encounter_list; return @map.encounter_list; end
|
||||
def encounter_step; return @map.encounter_step; end
|
||||
def data; return @map.data; end
|
||||
|
||||
def name
|
||||
ret = pbGetMessage(MessageTypes::MapNames,@map_id)
|
||||
ret.gsub!(/\\PN/,$Trainer.name) if $Trainer
|
||||
return ret
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Autoplays background music
|
||||
# Plays music called "[normal BGM]n" if it's night time and it exists
|
||||
#-----------------------------------------------------------------------------
|
||||
def autoplayAsCue
|
||||
if @map.autoplay_bgm
|
||||
if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/"+ @map.bgm.name+ "_n")
|
||||
pbCueBGM(@map.bgm.name+"_n",1.0,@map.bgm.volume,@map.bgm.pitch)
|
||||
else
|
||||
pbCueBGM(@map.bgm,1.0)
|
||||
end
|
||||
end
|
||||
if @map.autoplay_bgs
|
||||
pbBGSPlay(@map.bgs)
|
||||
end
|
||||
end
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Plays background music
|
||||
# Plays music called "[normal BGM]n" if it's night time and it exists
|
||||
#-----------------------------------------------------------------------------
|
||||
def autoplay
|
||||
if @map.autoplay_bgm
|
||||
if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/"+ @map.bgm.name+ "n")
|
||||
pbBGMPlay(@map.bgm.name+"n",@map.bgm.volume,@map.bgm.pitch)
|
||||
else
|
||||
pbBGMPlay(@map.bgm)
|
||||
end
|
||||
end
|
||||
if @map.autoplay_bgs
|
||||
pbBGSPlay(@map.bgs)
|
||||
end
|
||||
end
|
||||
|
||||
def valid?(x, y)
|
||||
return (x>=0 and x<width and y>=0 and y<height)
|
||||
end
|
||||
|
||||
def validLax?(x, y)
|
||||
return (x>=-10 and x<=width+10 and y>=-10 and y<=height+10)
|
||||
end
|
||||
|
||||
def passable?(x, y, d, self_event = nil)
|
||||
return false if !valid?(x, y)
|
||||
bit = (1 << (d / 2 - 1)) & 0x0f
|
||||
for event in events.values
|
||||
next if event.tile_id <= 0
|
||||
terrain = @terrain_tags[event.tile_id]
|
||||
next if terrain == PBTerrain::Neutral
|
||||
next if event == self_event
|
||||
next if event.x != x || event.y != y
|
||||
next if event.through
|
||||
passage = @passages[event.tile_id]
|
||||
return false if passage & bit != 0
|
||||
return false if passage & 0x0f == 0x0f
|
||||
return true if @priorities[event.tile_id] == 0
|
||||
end
|
||||
return playerPassable?(x, y, d, self_event) if self_event==$game_player
|
||||
# All other events
|
||||
newx = x; newy = y
|
||||
case d
|
||||
when 1; newx -= 1; newy += 1
|
||||
when 2; newy += 1
|
||||
when 3; newx += 1; newy += 1
|
||||
when 4; newx -= 1
|
||||
when 6; newx += 1
|
||||
when 7; newx -= 1; newy -= 1
|
||||
when 8; newy -= 1
|
||||
when 9; newx += 1; newy -= 1
|
||||
end
|
||||
return false if !valid?(newx, newy)
|
||||
for i in [2, 1, 0]
|
||||
tile_id = data[x, y, i]
|
||||
terrain = @terrain_tags[tile_id]
|
||||
passage = @passages[tile_id]
|
||||
# If already on water, only allow movement to another water tile
|
||||
if self_event!=nil && PBTerrain.isJustWater?(terrain)
|
||||
for j in [2, 1, 0]
|
||||
facing_tile_id = data[newx, newy, j]
|
||||
return false if facing_tile_id==nil
|
||||
facing_terrain = @terrain_tags[facing_tile_id]
|
||||
if facing_terrain!=0 && facing_terrain!=PBTerrain::Neutral
|
||||
return PBTerrain.isJustWater?(facing_terrain)
|
||||
end
|
||||
end
|
||||
return false
|
||||
# Can't walk onto ice
|
||||
elsif PBTerrain.isIce?(terrain)
|
||||
return false
|
||||
elsif self_event!=nil && self_event.x==x && self_event.y==y
|
||||
# Can't walk onto ledges
|
||||
for j in [2, 1, 0]
|
||||
facing_tile_id = data[newx, newy, j]
|
||||
return false if facing_tile_id==nil
|
||||
facing_terrain = @terrain_tags[facing_tile_id]
|
||||
if facing_terrain!=0 && facing_terrain!=PBTerrain::Neutral
|
||||
return false if PBTerrain.isLedge?(facing_terrain)
|
||||
break
|
||||
end
|
||||
end
|
||||
# Regular passability checks
|
||||
if terrain!=PBTerrain::Neutral
|
||||
if passage & bit != 0 || passage & 0x0f == 0x0f
|
||||
return false
|
||||
elsif @priorities[tile_id] == 0
|
||||
return true
|
||||
end
|
||||
end
|
||||
# Regular passability checks
|
||||
elsif terrain!=PBTerrain::Neutral
|
||||
if passage & bit != 0 || passage & 0x0f == 0x0f
|
||||
return false
|
||||
elsif @priorities[tile_id] == 0
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def playerPassable?(x, y, d, self_event = nil)
|
||||
bit = (1 << (d / 2 - 1)) & 0x0f
|
||||
for i in [2, 1, 0]
|
||||
tile_id = data[x, y, i]
|
||||
terrain = @terrain_tags[tile_id]
|
||||
passage = @passages[tile_id]
|
||||
# Ignore bridge tiles if not on a bridge
|
||||
next if PBTerrain.isBridge?(terrain) && $PokemonGlobal.bridge==0
|
||||
# Make water tiles passable if player is surfing
|
||||
if $PokemonGlobal.surfing && PBTerrain.isPassableWater?(terrain)
|
||||
return true
|
||||
# Prevent cycling in really tall grass/on ice
|
||||
elsif $PokemonGlobal.bicycle && PBTerrain.onlyWalk?(terrain)
|
||||
return false
|
||||
# Depend on passability of bridge tile if on bridge
|
||||
elsif PBTerrain.isBridge?(terrain) && $PokemonGlobal.bridge>0
|
||||
return (passage & bit == 0 && passage & 0x0f != 0x0f)
|
||||
# Regular passability checks
|
||||
elsif terrain!=PBTerrain::Neutral
|
||||
if passage & bit != 0 || passage & 0x0f == 0x0f
|
||||
return false
|
||||
elsif @priorities[tile_id] == 0
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
# Returns whether the position x,y is fully passable (there is no blocking
|
||||
# event there, and the tile is fully passable in all directions)
|
||||
def passableStrict?(x, y, d, self_event = nil)
|
||||
return false if !valid?(x, y)
|
||||
for event in events.values
|
||||
next if event == self_event || event.tile_id < 0 || event.through
|
||||
next if event.x != x || event.y != y
|
||||
terrain = @terrain_tags[event.tile_id]
|
||||
next if terrain == PBTerrain::Neutral
|
||||
return false if @passages[event.tile_id] & 0x0f != 0
|
||||
return true if @priorities[event.tile_id] == 0
|
||||
end
|
||||
for i in [2, 1, 0]
|
||||
tile_id = data[x, y, i]
|
||||
terrain = @terrain_tags[tile_id]
|
||||
next if terrain == PBTerrain::Neutral
|
||||
return false if @passages[tile_id] & 0x0f != 0
|
||||
return true if @priorities[tile_id] == 0
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def bush?(x,y)
|
||||
for i in [2, 1, 0]
|
||||
tile_id = data[x, y, i]
|
||||
return false if PBTerrain.isBridge?(@terrain_tags[tile_id]) && $PokemonGlobal.bridge>0
|
||||
return true if @passages[tile_id] & 0x40 == 0x40
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def deepBush?(x,y)
|
||||
for i in [2, 1, 0]
|
||||
tile_id = data[x, y, i]
|
||||
terrain = @terrain_tags[tile_id]
|
||||
return false if $PokemonGlobal.bridge>0 && PBTerrain.isBridge?(terrain)
|
||||
return true if terrain==PBTerrain::TallGrass && @passages[tile_id] & 0x40 == 0x40
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def counter?(x,y)
|
||||
for i in [2, 1, 0]
|
||||
tile_id = data[x, y, i]
|
||||
passage = @passages[tile_id]
|
||||
return true if passage & 0x80 == 0x80
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def terrain_tag(x,y,countBridge=false)
|
||||
return 0 if !valid?(x, y)
|
||||
for i in [2, 1, 0]
|
||||
tile_id = data[x, y, i]
|
||||
terrain = @terrain_tags[tile_id]
|
||||
next if !countBridge && PBTerrain.isBridge?(terrain) && $PokemonGlobal.bridge==0
|
||||
return terrain if terrain > 0 && terrain!=PBTerrain::Neutral
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
def check_event(x,y)
|
||||
for event in self.events.values
|
||||
return event.id if event.x == x and event.y == y
|
||||
end
|
||||
end
|
||||
|
||||
def display_x=(value)
|
||||
@display_x = value
|
||||
if pbGetMetadata(self.map_id,MetadataSnapEdges)
|
||||
max_x = (self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X
|
||||
@display_x = [0, [@display_x, max_x].min].max
|
||||
end
|
||||
$MapFactory.setMapsInRange if $MapFactory
|
||||
end
|
||||
|
||||
def display_y=(value)
|
||||
@display_y = value
|
||||
if pbGetMetadata(self.map_id,MetadataSnapEdges)
|
||||
max_y = (self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y
|
||||
@display_y = [0, [@display_y, max_y].min].max
|
||||
end
|
||||
$MapFactory.setMapsInRange if $MapFactory
|
||||
end
|
||||
|
||||
def scroll_up(distance)
|
||||
self.display_y -= distance
|
||||
end
|
||||
def scroll_down(distance)
|
||||
self.display_y += distance
|
||||
end
|
||||
|
||||
def scroll_left(distance)
|
||||
self.display_x -= distance
|
||||
end
|
||||
|
||||
def scroll_right(distance)
|
||||
self.display_x += distance
|
||||
end
|
||||
|
||||
def start_scroll(direction, distance, speed)
|
||||
@scroll_direction = direction
|
||||
if direction==2 || direction==8 # down or up
|
||||
@scroll_rest = distance * REAL_RES_Y
|
||||
else
|
||||
@scroll_rest = distance * REAL_RES_X
|
||||
end
|
||||
@scroll_speed = speed
|
||||
end
|
||||
|
||||
def scrolling?
|
||||
return @scroll_rest > 0
|
||||
end
|
||||
|
||||
def start_fog_tone_change(tone,duration)
|
||||
@fog_tone_target = tone.clone
|
||||
@fog_tone_duration = duration
|
||||
if @fog_tone_duration == 0
|
||||
@fog_tone = @fog_tone_target.clone
|
||||
end
|
||||
end
|
||||
|
||||
def start_fog_opacity_change(opacity,duration)
|
||||
@fog_opacity_target = opacity*1.0
|
||||
@fog_opacity_duration = duration
|
||||
if @fog_opacity_duration==0
|
||||
@fog_opacity = @fog_opacity_target
|
||||
end
|
||||
end
|
||||
|
||||
def refresh
|
||||
for event in @events.values
|
||||
event.refresh
|
||||
end
|
||||
for common_event in @common_events.values
|
||||
common_event.refresh
|
||||
end
|
||||
@need_refresh = false
|
||||
end
|
||||
|
||||
def update
|
||||
# refresh maps if necessary
|
||||
if $MapFactory
|
||||
for i in $MapFactory.maps
|
||||
i.refresh if i.need_refresh
|
||||
end
|
||||
$MapFactory.setCurrentMap
|
||||
end
|
||||
# If scrolling
|
||||
if @scroll_rest>0
|
||||
distance = (1<<@scroll_speed)*40.0/Graphics.frame_rate
|
||||
distance = @scroll_rest if distance>@scroll_rest
|
||||
case @scroll_direction
|
||||
when 2; scroll_down(distance)
|
||||
when 4; scroll_left(distance)
|
||||
when 6; scroll_right(distance)
|
||||
when 8; scroll_up(distance)
|
||||
end
|
||||
@scroll_rest -= distance
|
||||
end
|
||||
# Only update events that are on-screen
|
||||
for event in @events.values
|
||||
event.update
|
||||
end
|
||||
# Update common events
|
||||
for common_event in @common_events.values
|
||||
common_event.update
|
||||
end
|
||||
# Update fog
|
||||
@fog_ox -= @fog_sx/8.0
|
||||
@fog_oy -= @fog_sy/8.0
|
||||
if @fog_tone_duration>=1
|
||||
d = @fog_tone_duration
|
||||
target = @fog_tone_target
|
||||
@fog_tone.red = (@fog_tone.red * (d - 1) + target.red) / d
|
||||
@fog_tone.green = (@fog_tone.green * (d - 1) + target.green) / d
|
||||
@fog_tone.blue = (@fog_tone.blue * (d - 1) + target.blue) / d
|
||||
@fog_tone.gray = (@fog_tone.gray * (d - 1) + target.gray) / d
|
||||
@fog_tone_duration -= 1
|
||||
end
|
||||
if @fog_opacity_duration >= 1
|
||||
d = @fog_opacity_duration
|
||||
@fog_opacity = (@fog_opacity * (d - 1) + @fog_opacity_target) / d
|
||||
@fog_opacity_duration -= 1
|
||||
end
|
||||
end
|
||||
end
|
||||
194
Data/Scripts/003_Game classes/010_Game_Map_Autoscroll.rb
Normal file
194
Data/Scripts/003_Game classes/010_Game_Map_Autoscroll.rb
Normal file
@@ -0,0 +1,194 @@
|
||||
#===============================================================================
|
||||
# ** Map Autoscroll
|
||||
#-------------------------------------------------------------------------------
|
||||
# Wachunga
|
||||
# Version 1.02
|
||||
# 2005-12-18
|
||||
#===============================================================================
|
||||
=begin
|
||||
|
||||
This script supplements the built-in "Scroll Map" event command with the
|
||||
aim of simplifying cutscenes (and map scrolling in general). Whereas the
|
||||
normal event command requires a direction and number of tiles to scroll,
|
||||
Map Autoscroll scrolls the map to center on the tile whose x and y
|
||||
coordinates are given.
|
||||
|
||||
FEATURES
|
||||
- automatic map scrolling to given x,y coordinate (or player)
|
||||
- destination is fixed, so it's possible to scroll to same place even if
|
||||
origin is variable (e.g. moving NPC)
|
||||
- variable speed (just like "Scroll Map" event command)
|
||||
- diagonal scrolling supported
|
||||
|
||||
SETUP
|
||||
Instead of a "Scroll Map" event command, use the "Call Script" command
|
||||
and enter on the following on the first line:
|
||||
|
||||
autoscroll(x,y)
|
||||
|
||||
(replacing "x" and "y" with the x and y coordinates of the tile to scroll to)
|
||||
|
||||
To specify a scroll speed other than the default (4), use:
|
||||
|
||||
autoscroll(x,y,speed)
|
||||
|
||||
(now also replacing "speed" with the scroll speed from 1-6)
|
||||
|
||||
Diagonal scrolling happens automatically when the destination is diagonal
|
||||
relative to the starting point (i.e., not directly up, down, left or right).
|
||||
|
||||
To scroll to the player, instead use the following:
|
||||
|
||||
autoscroll_player(speed)
|
||||
|
||||
Note: because of how the interpreter and the "Call Script" event command
|
||||
are setup, the call to autoscroll(...) can only be on the first line of
|
||||
the "Call Script" event command (and not flowing down to subsequent lines).
|
||||
|
||||
For example, the following call may not work as expected:
|
||||
|
||||
autoscroll($game_variables[1],
|
||||
$game_variables[2])
|
||||
|
||||
(since the long argument names require dropping down to a second line)
|
||||
A work-around is to setup new variables with shorter names in a preceding
|
||||
(separate) "Call Script" event command:
|
||||
|
||||
@x = $game_variables[1]
|
||||
@y = $game_variables[2]
|
||||
|
||||
and then use those as arguments:
|
||||
|
||||
autoscroll(@x,@y)
|
||||
|
||||
The renaming must be in a separate "Call Script" because otherwise
|
||||
the call to autoscroll(...) isn't on the first line.
|
||||
|
||||
Originally requested by militantmilo80:
|
||||
http://www.rmxp.net/forums/index.php?showtopic=29519
|
||||
|
||||
=end
|
||||
|
||||
class Interpreter
|
||||
SCROLL_SPEED_DEFAULT = 4
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Map Autoscroll to Coordinates
|
||||
# x : x coordinate to scroll to and center on
|
||||
# y : y coordinate to scroll to and center on
|
||||
# speed : (optional) scroll speed (from 1-6, default being 4)
|
||||
#-----------------------------------------------------------------------------
|
||||
def autoscroll(x,y,speed=SCROLL_SPEED_DEFAULT)
|
||||
if $game_map.scrolling?
|
||||
return false
|
||||
elsif not $game_map.valid?(x,y)
|
||||
print 'Map Autoscroll: given x,y is invalid'
|
||||
return command_skip
|
||||
elsif not (1..6).include?(speed)
|
||||
print 'Map Autoscroll: invalid speed (1-6 only)'
|
||||
return command_skip
|
||||
end
|
||||
center_x = (Graphics.width/2 - Game_Map::TILE_WIDTH/2) * 4 # X coordinate in the center of the screen
|
||||
center_y = (Graphics.height/2 - Game_Map::TILE_HEIGHT/2) * 4 # Y coordinate in the center of the screen
|
||||
max_x = ($game_map.width - Graphics.width*1.0/Game_Map::TILE_WIDTH) * 4 * Game_Map::TILE_WIDTH
|
||||
max_y = ($game_map.height - Graphics.height*1.0/Game_Map::TILE_HEIGHT) * 4 * Game_Map::TILE_HEIGHT
|
||||
count_x = ($game_map.display_x - [0,[x*Game_Map::REAL_RES_X-center_x,max_x].min].max)/Game_Map::REAL_RES_X
|
||||
count_y = ($game_map.display_y - [0,[y*Game_Map::REAL_RES_Y-center_y,max_y].min].max)/Game_Map::REAL_RES_Y
|
||||
if not @diag
|
||||
@diag = true
|
||||
dir = nil
|
||||
if count_x > 0
|
||||
if count_y > 0
|
||||
dir = 7
|
||||
elsif count_y < 0
|
||||
dir = 1
|
||||
end
|
||||
elsif count_x < 0
|
||||
if count_y > 0
|
||||
dir = 9
|
||||
elsif count_y < 0
|
||||
dir = 3
|
||||
end
|
||||
end
|
||||
count = [count_x.abs,count_y.abs].min
|
||||
else
|
||||
@diag = false
|
||||
dir = nil
|
||||
if count_x != 0 and count_y != 0
|
||||
return false
|
||||
elsif count_x > 0
|
||||
dir = 4
|
||||
elsif count_x < 0
|
||||
dir = 6
|
||||
elsif count_y > 0
|
||||
dir = 8
|
||||
elsif count_y < 0
|
||||
dir = 2
|
||||
end
|
||||
count = count_x != 0 ? count_x.abs : count_y.abs
|
||||
end
|
||||
$game_map.start_scroll(dir, count, speed) if dir != nil
|
||||
if @diag
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# * Map Autoscroll (to Player)
|
||||
# speed : (optional) scroll speed (from 1-6, default being 4)
|
||||
#-----------------------------------------------------------------------------
|
||||
def autoscroll_player(speed=SCROLL_SPEED_DEFAULT)
|
||||
autoscroll($game_player.x,$game_player.y,speed)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Game_Map
|
||||
def scroll_downright(distance)
|
||||
@display_x = [@display_x + distance,
|
||||
(self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X].min
|
||||
@display_y = [@display_y + distance,
|
||||
(self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y].min
|
||||
end
|
||||
|
||||
def scroll_downleft(distance)
|
||||
@display_x = [@display_x - distance, 0].max
|
||||
@display_y = [@display_y + distance,
|
||||
(self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y].min
|
||||
end
|
||||
|
||||
def scroll_upright(distance)
|
||||
@display_x = [@display_x + distance,
|
||||
(self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X].min
|
||||
@display_y = [@display_y - distance, 0].max
|
||||
end
|
||||
|
||||
def scroll_upleft(distance)
|
||||
@display_x = [@display_x - distance, 0].max
|
||||
@display_y = [@display_y - distance, 0].max
|
||||
end
|
||||
|
||||
def update_scrolling
|
||||
# If scrolling
|
||||
if @scroll_rest > 0
|
||||
# Change from scroll speed to distance in map coordinates
|
||||
distance = (1<<@scroll_speed)*40/Graphics.frame_rate
|
||||
distance = @scroll_rest if distance>@scroll_rest
|
||||
# Execute scrolling
|
||||
case @scroll_direction
|
||||
when 1; scroll_downleft(distance)
|
||||
when 2; scroll_down(distance)
|
||||
when 3; scroll_downright(distance)
|
||||
when 4; scroll_left(distance)
|
||||
when 6; scroll_right(distance)
|
||||
when 7; scroll_upleft(distance)
|
||||
when 8; scroll_up(distance)
|
||||
when 9; scroll_upright(distance)
|
||||
end
|
||||
# Subtract distance scrolled
|
||||
@scroll_rest -= distance
|
||||
end
|
||||
end
|
||||
end
|
||||
488
Data/Scripts/003_Game classes/011_MapFactory.rb
Normal file
488
Data/Scripts/003_Game classes/011_MapFactory.rb
Normal file
@@ -0,0 +1,488 @@
|
||||
#===============================================================================
|
||||
# Map Factory (allows multiple maps to be loaded at once and connected)
|
||||
#===============================================================================
|
||||
class PokemonMapFactory
|
||||
attr_reader :maps
|
||||
|
||||
def initialize(id)
|
||||
@maps = []
|
||||
@fixup = false
|
||||
@mapChanged = false # transient instance variable
|
||||
setup(id)
|
||||
end
|
||||
|
||||
# Clears all maps and sets up the current map with id. This function also sets
|
||||
# the positions of neighboring maps and notifies the game system of a map
|
||||
# change.
|
||||
def setup(id)
|
||||
@maps.clear
|
||||
@maps[0] = Game_Map.new
|
||||
@mapIndex = 0
|
||||
oldID = ($game_map) ? $game_map.map_id : 0
|
||||
setMapChanging(id,@maps[0]) if oldID!=0 && oldID!=@maps[0].map_id
|
||||
$game_map = @maps[0]
|
||||
@maps[0].setup(id)
|
||||
setMapsInRange
|
||||
setMapChanged(oldID)
|
||||
end
|
||||
|
||||
def map
|
||||
@mapIndex = 0 if !@mapIndex || @mapIndex<0
|
||||
return @maps[@mapIndex] if @maps[@mapIndex]
|
||||
raise "No maps in save file... (mapIndex=#{@mapIndex})" if @maps.length==0
|
||||
for i in 0...@maps.length
|
||||
if @maps[i]
|
||||
echo("Using next map, may be incorrect (mapIndex=#{@mapIndex}, length=#{@maps.length})")
|
||||
return @maps[i]
|
||||
end
|
||||
raise "No maps in save file... (all maps empty; mapIndex=#{@mapIndex})"
|
||||
end
|
||||
end
|
||||
|
||||
def hasMap?(id)
|
||||
for map in @maps
|
||||
return true if map.map_id==id
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def getMapIndex(id)
|
||||
for i in 0...@maps.length
|
||||
return i if @maps[i].map_id==id
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
def getMap(id,add=true)
|
||||
for map in @maps
|
||||
return map if map.map_id==id
|
||||
end
|
||||
map = Game_Map.new
|
||||
map.setup(id)
|
||||
@maps.push(map) if add
|
||||
return map
|
||||
end
|
||||
|
||||
def getMapNoAdd(id)
|
||||
return getMap(id,false)
|
||||
end
|
||||
|
||||
def getNewMap(playerX,playerY)
|
||||
id = $game_map.map_id
|
||||
conns = MapFactoryHelper.getMapConnections
|
||||
for conn in conns
|
||||
next if conn[0]!=id && conn[3]!=id
|
||||
mapidB = nil
|
||||
newx = 0
|
||||
newy = 0
|
||||
if conn[0]==id
|
||||
mapidB = conn[3]
|
||||
mapB = MapFactoryHelper.getMapDims(conn[3])
|
||||
newx = conn[4] - conn[1] + playerX
|
||||
newy = conn[5] - conn[2] + playerY
|
||||
else
|
||||
mapidB = conn[0]
|
||||
mapB = MapFactoryHelper.getMapDims(conn[0])
|
||||
newx = conn[1] - conn[4] + playerX
|
||||
newy = conn[2] - conn[5] + playerY
|
||||
end
|
||||
if newx>=0 && newx<mapB[0] && newy>=0 && newy<mapB[1]
|
||||
return [getMap(mapidB),newx,newy]
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def setCurrentMap
|
||||
return if $game_player.moving?
|
||||
return if $game_map.valid?($game_player.x,$game_player.y)
|
||||
newmap = getNewMap($game_player.x,$game_player.y)
|
||||
return if !newmap
|
||||
oldmap=$game_map.map_id
|
||||
if oldmap!=0 && oldmap!=newmap[0].map_id
|
||||
setMapChanging(newmap[0].map_id,newmap[0])
|
||||
end
|
||||
$game_map = newmap[0]
|
||||
@mapIndex = getMapIndex($game_map.map_id)
|
||||
$game_player.moveto(newmap[1],newmap[2])
|
||||
$game_map.update
|
||||
pbAutoplayOnTransition
|
||||
$game_map.refresh
|
||||
setMapChanged(oldmap)
|
||||
end
|
||||
|
||||
def setMapsInRange
|
||||
return if @fixup
|
||||
@fixup = true
|
||||
id = $game_map.map_id
|
||||
conns = MapFactoryHelper.getMapConnections
|
||||
for conn in conns
|
||||
if conn[0]==id
|
||||
mapA = getMap(conn[0])
|
||||
newdispx = (conn[4] - conn[1]) * Game_Map::REAL_RES_X + mapA.display_x
|
||||
newdispy = (conn[5] - conn[2]) * Game_Map::REAL_RES_Y + mapA.display_y
|
||||
if hasMap?(conn[3]) || MapFactoryHelper.mapInRangeById?(conn[3],newdispx,newdispy)
|
||||
mapB = getMap(conn[3])
|
||||
mapB.display_x = newdispx if mapB.display_x!=newdispx
|
||||
mapB.display_y = newdispy if mapB.display_y!=newdispy
|
||||
end
|
||||
elsif conn[3]==id
|
||||
mapA = getMap(conn[3])
|
||||
newdispx = (conn[1] - conn[4]) * Game_Map::REAL_RES_X + mapA.display_x
|
||||
newdispy = (conn[2] - conn[5]) * Game_Map::REAL_RES_Y + mapA.display_y
|
||||
if hasMap?(conn[0]) || MapFactoryHelper.mapInRangeById?(conn[0],newdispx,newdispy)
|
||||
mapB = getMap(conn[0])
|
||||
mapB.display_x = newdispx if mapB.display_x!=newdispx
|
||||
mapB.display_y = newdispy if mapB.display_y!=newdispy
|
||||
end
|
||||
end
|
||||
end
|
||||
@fixup = false
|
||||
end
|
||||
|
||||
def setMapChanging(newID,newMap)
|
||||
Events.onMapChanging.trigger(self,newID,newMap)
|
||||
end
|
||||
|
||||
def setMapChanged(prevMap)
|
||||
Events.onMapChange.trigger(self,prevMap)
|
||||
@mapChanged = true
|
||||
end
|
||||
|
||||
def setSceneStarted(scene)
|
||||
Events.onMapSceneChange.trigger(self,scene,@mapChanged)
|
||||
@mapChanged = false
|
||||
end
|
||||
|
||||
# Similar to Game_Player#passable?, but supports map connections
|
||||
def isPassableFromEdge?(x,y)
|
||||
return true if $game_map.valid?(x,y)
|
||||
newmap = getNewMap(x,y)
|
||||
return false if !newmap
|
||||
return isPassable?(newmap[0].map_id,newmap[1],newmap[2])
|
||||
end
|
||||
|
||||
def isPassable?(mapID,x,y,thisEvent=nil)
|
||||
thisEvent = $game_player if !thisEvent
|
||||
map = getMapNoAdd(mapID)
|
||||
return false if !map
|
||||
return false if !map.valid?(x,y)
|
||||
return true if thisEvent.through
|
||||
if thisEvent==$game_player
|
||||
return false unless ($DEBUG && Input.press?(Input::CTRL)) ||
|
||||
map.passable?(x,y,0,thisEvent)
|
||||
else
|
||||
return false unless map.passable?(x,y,0,thisEvent)
|
||||
end
|
||||
for event in map.events.values
|
||||
next if event.x != x || event.y != y
|
||||
return false if !event.through && event.character_name!=""
|
||||
end
|
||||
if thisEvent.is_a?(Game_Player)
|
||||
if thisEvent.x == x and thisEvent.y == y
|
||||
return false if !thisEvent.through && thisEvent.character_name!=""
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
# Only used by dependent events
|
||||
def isPassableStrict?(mapID,x,y,thisEvent=nil)
|
||||
thisEvent = $game_player if !thisEvent
|
||||
map = getMapNoAdd(mapID)
|
||||
return false if !map
|
||||
return false if !map.valid?(x,y)
|
||||
return true if thisEvent.through
|
||||
if thisEvent==$game_player
|
||||
if !($DEBUG && Input.press?(Input::CTRL))
|
||||
return false if !map.passableStrict?(x,y,0,thisEvent)
|
||||
end
|
||||
else
|
||||
return false if !map.passableStrict?(x,y,0,thisEvent)
|
||||
end
|
||||
for event in map.events.values
|
||||
next if event == thisEvent || event.x != x || event.y != y
|
||||
return false if !event.through && event.character_name!=""
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def getTerrainTag(mapid,x,y,countBridge=false)
|
||||
map = getMapNoAdd(mapid)
|
||||
return map.terrain_tag(x,y,countBridge)
|
||||
end
|
||||
|
||||
def getFacingTerrainTag(dir=nil,event=nil)
|
||||
tile = getFacingTile(dir,event)
|
||||
return 0 if !tile
|
||||
return getTerrainTag(tile[0],tile[1],tile[2])
|
||||
end
|
||||
|
||||
def getTerrainTagFromCoords(mapid,x,y,countBridge=false)
|
||||
tile = getRealTilePos(mapid,x,y)
|
||||
return 0 if !tile
|
||||
return getTerrainTag(tile[0],tile[1],tile[2])
|
||||
end
|
||||
|
||||
def areConnected?(mapID1,mapID2)
|
||||
return true if mapID1==mapID2
|
||||
conns = MapFactoryHelper.getMapConnections
|
||||
for conn in conns
|
||||
if (conn[0]==mapID1 && conn[3]==mapID2) ||
|
||||
(conn[0]==mapID2 && conn[3]==mapID1)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def getRelativePos(thisMapID,thisX,thisY,otherMapID,otherX,otherY)
|
||||
if thisMapID==otherMapID
|
||||
# Both events share the same map
|
||||
return [otherX-thisX,otherY-thisY]
|
||||
end
|
||||
conns = MapFactoryHelper.getMapConnections
|
||||
for conn in conns
|
||||
if conn[0]==thisMapID && conn[1]==otherMapID
|
||||
posX = thisX + conn[4] - conn[1] + otherX
|
||||
posY = thisY + conn[5] - conn[2] + otherY
|
||||
return [posX,posY]
|
||||
elsif conn[1]==thisMapID && conn[0]==otherMapID
|
||||
posX = thisX + conn[1] - conn[4] + otherX
|
||||
posY = thisY + conn[2] - conn[5] + otherY
|
||||
return [posX,posY]
|
||||
end
|
||||
end
|
||||
return [0,0]
|
||||
end
|
||||
|
||||
# Gets the distance from this event to another event. Example: If this event's
|
||||
# coordinates are (2,5) and the other event's coordinates are (5,1), returns
|
||||
# the array (3,-4), because (5-2=3) and (1-5=-4).
|
||||
def getThisAndOtherEventRelativePos(thisEvent,otherEvent)
|
||||
return [0,0] if !thisEvent || !otherEvent
|
||||
return getRelativePos(
|
||||
thisEvent.map.map_id,thisEvent.x,thisEvent.y,
|
||||
otherEvent.map.map_id,otherEvent.x,otherEvent.y)
|
||||
end
|
||||
|
||||
def getThisAndOtherPosRelativePos(thisEvent,otherMapID,otherX,otherY)
|
||||
return [0,0] if !thisEvent
|
||||
return getRelativePos(
|
||||
thisEvent.map.map_id,thisEvent.x,thisEvent.y,otherMapID,otherX,otherY)
|
||||
end
|
||||
|
||||
def getOffsetEventPos(event,xOffset,yOffset)
|
||||
event = $game_player if !event
|
||||
return nil if !event
|
||||
return getRealTilePos(event.map.map_id,event.x+xOffset,event.y+yOffset)
|
||||
end
|
||||
|
||||
def getFacingTile(direction=nil,event=nil,steps=1)
|
||||
event = $game_player if event==nil
|
||||
return [0,0,0] if !event
|
||||
x = event.x
|
||||
y = event.y
|
||||
id = event.map.map_id
|
||||
direction = event.direction if direction==nil
|
||||
return getFacingTileFromPos(id,x,y,direction,steps)
|
||||
end
|
||||
|
||||
def getFacingTileFromPos(mapID,x,y,direction=0,steps=1)
|
||||
id = mapID
|
||||
case direction
|
||||
when 1; x -= steps; y += steps
|
||||
when 2; y += steps
|
||||
when 3; x += steps; y += steps
|
||||
when 4; x -= steps
|
||||
when 6; x += steps
|
||||
when 7; x -= steps; y -= steps
|
||||
when 8; y -= steps
|
||||
when 9; x += steps; y -= steps
|
||||
else; return [id,x,y]
|
||||
end
|
||||
return getRealTilePos(mapID,x,y)
|
||||
end
|
||||
|
||||
def getRealTilePos(mapID,x,y)
|
||||
id = mapID
|
||||
return [id,x,y] if getMapNoAdd(id).valid?(x,y)
|
||||
conns = MapFactoryHelper.getMapConnections
|
||||
for conn in conns
|
||||
if conn[0]==id
|
||||
newX = x + conn[4] - conn[1]
|
||||
newY = y + conn[5] - conn[2]
|
||||
next if newX<0 || newY<0
|
||||
dims = MapFactoryHelper.getMapDims(conn[3])
|
||||
next if newX>=dims[0] || newY>=dims[1]
|
||||
return [conn[3],newX,newY]
|
||||
elsif conn[3]==id
|
||||
newX = x + conn[1] - conn[4]
|
||||
newY = y + conn[2] - conn[5]
|
||||
next if newX<0 || newY<0
|
||||
dims = MapFactoryHelper.getMapDims(conn[0])
|
||||
next if newX>=dims[0] || newY>=dims[1]
|
||||
return [conn[0],newX,newY]
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def getFacingCoords(x,y,direction=0,steps=1)
|
||||
case direction
|
||||
when 1; x -= steps; y += steps
|
||||
when 2; y += steps
|
||||
when 3; x += steps; y += steps
|
||||
when 4; x -= steps
|
||||
when 6; x += steps
|
||||
when 7; x -= steps; y -= steps
|
||||
when 8; y -= steps
|
||||
when 9; x += steps; y -= steps
|
||||
end
|
||||
return [x,y]
|
||||
end
|
||||
|
||||
def updateMaps(scene)
|
||||
updateMapsInternal
|
||||
$MapFactory.setSceneStarted(scene) if @mapChanged
|
||||
end
|
||||
|
||||
def updateMapsInternal
|
||||
return if $game_player.moving?
|
||||
if !MapFactoryHelper.hasConnections?($game_map.map_id)
|
||||
return if @maps.length==1
|
||||
for i in 0...@maps.length
|
||||
@maps[i] = nil if $game_map.map_id!=@maps[i].map_id
|
||||
end
|
||||
@maps.compact!
|
||||
@mapIndex = getMapIndex($game_map.map_id)
|
||||
return
|
||||
end
|
||||
setMapsInRange
|
||||
deleted = false
|
||||
for i in 0...@maps.length
|
||||
next if MapFactoryHelper.mapInRange?(@maps[i])
|
||||
@maps[i] = nil
|
||||
deleted = true
|
||||
end
|
||||
if deleted
|
||||
@maps.compact!
|
||||
@mapIndex = getMapIndex($game_map.map_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Map Factory Helper (stores map connection and size data and calculations
|
||||
# involving them)
|
||||
#===============================================================================
|
||||
module MapFactoryHelper
|
||||
@@MapConnections = nil
|
||||
@@MapDims = nil
|
||||
|
||||
def self.clear
|
||||
@@MapConnections = nil
|
||||
@@MapDims = nil
|
||||
end
|
||||
|
||||
def self.getMapConnections
|
||||
if !@@MapConnections
|
||||
@@MapConnections = []
|
||||
begin
|
||||
conns = load_data("Data/map_connections.dat")
|
||||
rescue
|
||||
conns = []
|
||||
end
|
||||
for i in 0...conns.length
|
||||
conn = conns[i]
|
||||
v = getMapEdge(conn[0],conn[1])
|
||||
dims = getMapDims(conn[0])
|
||||
next if dims[0]==0 || dims[1]==0
|
||||
if conn[1]=="N" || conn[1]=="S"
|
||||
conn[1] = conn[2]
|
||||
conn[2] = v
|
||||
elsif conn[1]=="E" || conn[1]=="W"
|
||||
conn[1] = v
|
||||
end
|
||||
v = getMapEdge(conn[3],conn[4])
|
||||
dims = getMapDims(conn[3])
|
||||
next if dims[0]==0 || dims[1]==0
|
||||
if conn[4]=="N" || conn[4]=="S"
|
||||
conn[4] = conn[5]
|
||||
conn[5] = v
|
||||
elsif conn[4]=="E" || conn[4]=="W"
|
||||
conn[4] = v
|
||||
end
|
||||
@@MapConnections.push(conn)
|
||||
end
|
||||
end
|
||||
return @@MapConnections
|
||||
end
|
||||
|
||||
def self.hasConnections?(id)
|
||||
conns = MapFactoryHelper.getMapConnections
|
||||
for conn in conns
|
||||
return true if conn[0]==id || conn[3]==id
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Gets the height and width of the map with id
|
||||
def self.getMapDims(id)
|
||||
# Create cache if doesn't exist
|
||||
@@MapDims = [] if !@@MapDims
|
||||
# Add map to cache if can't be found
|
||||
if !@@MapDims[id]
|
||||
begin
|
||||
map = pbLoadRxData(sprintf("Data/Map%03d", id))
|
||||
@@MapDims[id] = [map.width,map.height]
|
||||
rescue
|
||||
@@MapDims[id] = [0,0]
|
||||
end
|
||||
end
|
||||
# Return map in cache
|
||||
return @@MapDims[id]
|
||||
end
|
||||
|
||||
# Returns the X or Y coordinate of an edge on the map with id.
|
||||
# Considers the special strings "N","W","E","S"
|
||||
def self.getMapEdge(id,edge)
|
||||
return 0 if edge=="N" || edge=="W"
|
||||
dims = getMapDims(id) # Get dimensions
|
||||
return dims[0] if edge=="E"
|
||||
return dims[1] if edge=="S"
|
||||
return dims[0] # real dimension (use width)
|
||||
end
|
||||
|
||||
def self.mapInRange?(map)
|
||||
range = 6 # Number of tiles
|
||||
dispx = map.display_x
|
||||
dispy = map.display_y
|
||||
return false if dispx >= (map.width + range) * Game_Map::REAL_RES_X
|
||||
return false if dispy >= (map.height + range) * Game_Map::REAL_RES_Y
|
||||
return false if dispx <= -(Graphics.width + range * Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS
|
||||
return false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
|
||||
return true
|
||||
end
|
||||
|
||||
def self.mapInRangeById?(id,dispx,dispy)
|
||||
range = 6 # Number of tiles
|
||||
dims = MapFactoryHelper.getMapDims(id)
|
||||
return false if dispx >= (dims[0] + range) * Game_Map::REAL_RES_X
|
||||
return false if dispy >= (dims[1] + range) * Game_Map::REAL_RES_Y
|
||||
return false if dispx <= -(Graphics.width + range * Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS
|
||||
return false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def updateTilesets
|
||||
maps = $MapFactory.maps
|
||||
for map in maps
|
||||
map.updateTileset if map
|
||||
end
|
||||
end
|
||||
58
Data/Scripts/004_Sprites/001_Sprite_Picture.rb
Normal file
58
Data/Scripts/004_Sprites/001_Sprite_Picture.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
class Sprite_Picture
|
||||
def initialize(viewport, picture)
|
||||
@viewport = viewport
|
||||
@picture = picture
|
||||
@sprite = nil
|
||||
update
|
||||
end
|
||||
|
||||
def dispose
|
||||
@sprite.dispose if @sprite
|
||||
end
|
||||
|
||||
def update
|
||||
@sprite.update if @sprite
|
||||
# If picture file name is different from current one
|
||||
if @picture_name != @picture.name
|
||||
# Remember file name to instance variables
|
||||
@picture_name = @picture.name
|
||||
# If file name is not empty
|
||||
if @picture_name != ""
|
||||
# Get picture graphic
|
||||
@sprite=IconSprite.new(0,0,@viewport) if !@sprite
|
||||
@sprite.setBitmap("Graphics/Pictures/"+@picture_name)
|
||||
end
|
||||
end
|
||||
# If file name is empty
|
||||
if @picture_name == ""
|
||||
# Set sprite to invisible
|
||||
if @sprite
|
||||
@sprite.dispose if @sprite
|
||||
@sprite=nil
|
||||
end
|
||||
return
|
||||
end
|
||||
# Set sprite to visible
|
||||
@sprite.visible = true
|
||||
# Set transfer starting point
|
||||
if @picture.origin == 0
|
||||
@sprite.ox = 0
|
||||
@sprite.oy = 0
|
||||
else
|
||||
@sprite.ox = @sprite.bitmap.width / 2
|
||||
@sprite.oy = @sprite.bitmap.height / 2
|
||||
end
|
||||
# Set sprite coordinates
|
||||
@sprite.x = @picture.x
|
||||
@sprite.y = @picture.y
|
||||
@sprite.z = @picture.number
|
||||
# Set zoom rate, opacity level, and blend method
|
||||
@sprite.zoom_x = @picture.zoom_x / 100.0
|
||||
@sprite.zoom_y = @picture.zoom_y / 100.0
|
||||
@sprite.opacity = @picture.opacity
|
||||
@sprite.blend_type = @picture.blend_type
|
||||
# Set rotation angle and color tone
|
||||
@sprite.angle = @picture.angle
|
||||
@sprite.tone = @picture.tone
|
||||
end
|
||||
end
|
||||
44
Data/Scripts/004_Sprites/002_Sprite_Timer.rb
Normal file
44
Data/Scripts/004_Sprites/002_Sprite_Timer.rb
Normal file
@@ -0,0 +1,44 @@
|
||||
class Sprite_Timer
|
||||
def initialize(viewport=nil)
|
||||
@viewport=viewport
|
||||
@timer=nil
|
||||
@total_sec=nil
|
||||
@disposed=false
|
||||
end
|
||||
|
||||
def dispose
|
||||
@timer.dispose if @timer
|
||||
@timer=nil
|
||||
@disposed=true
|
||||
end
|
||||
|
||||
def disposed?
|
||||
@disposed
|
||||
end
|
||||
|
||||
def update
|
||||
return if disposed?
|
||||
if $game_system.timer_working
|
||||
if !@timer
|
||||
@timer=Window_AdvancedTextPokemon.newWithSize("",Graphics.width-120,0,120,64)
|
||||
@timer.width=@timer.borderX+96
|
||||
@timer.x=Graphics.width-@timer.width
|
||||
@timer.viewport=@viewport
|
||||
@timer.z=99998
|
||||
end
|
||||
curtime=$game_system.timer / Graphics.frame_rate
|
||||
curtime=0 if curtime<0
|
||||
if curtime != @total_sec
|
||||
# Calculate total number of seconds
|
||||
@total_sec = curtime
|
||||
# Make a string for displaying the timer
|
||||
min = @total_sec / 60
|
||||
sec = @total_sec % 60
|
||||
@timer.text = _ISPRINTF("<ac>{1:02d}:{2:02d}", min, sec)
|
||||
end
|
||||
@timer.update
|
||||
else
|
||||
@timer.visible=false if @timer
|
||||
end
|
||||
end
|
||||
end
|
||||
174
Data/Scripts/004_Sprites/003_Sprite_Character.rb
Normal file
174
Data/Scripts/004_Sprites/003_Sprite_Character.rb
Normal file
@@ -0,0 +1,174 @@
|
||||
class BushBitmap
|
||||
def initialize(bitmap,isTile,depth)
|
||||
@bitmaps = []
|
||||
@bitmap = bitmap
|
||||
@isTile = isTile
|
||||
@isBitmap = @bitmap.is_a?(Bitmap)
|
||||
@depth = depth
|
||||
end
|
||||
|
||||
def dispose
|
||||
for b in @bitmaps
|
||||
b.dispose if b
|
||||
end
|
||||
end
|
||||
|
||||
def bitmap
|
||||
thisBitmap = (@isBitmap) ? @bitmap : @bitmap.bitmap
|
||||
current = (@isBitmap) ? 0 : @bitmap.currentIndex
|
||||
if !@bitmaps[current]
|
||||
if @isTile
|
||||
@bitmaps[current] = pbBushDepthTile(thisBitmap,@depth)
|
||||
else
|
||||
@bitmaps[current] = pbBushDepthBitmap(thisBitmap,@depth)
|
||||
end
|
||||
end
|
||||
return @bitmaps[current]
|
||||
end
|
||||
|
||||
def pbBushDepthBitmap(bitmap,depth)
|
||||
ret = Bitmap.new(bitmap.width,bitmap.height)
|
||||
charheight = ret.height/4
|
||||
cy = charheight-depth-2
|
||||
for i in 0...4
|
||||
y = i*charheight
|
||||
if cy>=0
|
||||
ret.blt(0,y,bitmap,Rect.new(0,y,ret.width,cy))
|
||||
ret.blt(0,y+cy,bitmap,Rect.new(0,y+cy,ret.width,2),170)
|
||||
end
|
||||
ret.blt(0,y+cy+2,bitmap,Rect.new(0,y+cy+2,ret.width,2),85) if cy+2>=0
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbBushDepthTile(bitmap,depth)
|
||||
ret = Bitmap.new(bitmap.width,bitmap.height)
|
||||
charheight = ret.height
|
||||
cy = charheight-depth-2
|
||||
y = charheight
|
||||
if cy>=0
|
||||
ret.blt(0,y,bitmap,Rect.new(0,y,ret.width,cy))
|
||||
ret.blt(0,y+cy,bitmap,Rect.new(0,y+cy,ret.width,2),170)
|
||||
end
|
||||
ret.blt(0,y+cy+2,bitmap,Rect.new(0,y+cy+2,ret.width,2),85) if cy+2>=0
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Sprite_Character < RPG::Sprite
|
||||
attr_accessor :character
|
||||
|
||||
def initialize(viewport, character = nil)
|
||||
super(viewport)
|
||||
@character = character
|
||||
@oldbushdepth = 0
|
||||
@spriteoffset = false
|
||||
if !character || character==$game_player || (character.name[/reflection/i] rescue false)
|
||||
@reflection = Sprite_Reflection.new(self,character,viewport)
|
||||
end
|
||||
@surfbase = Sprite_SurfBase.new(self,character,viewport) if character==$game_player
|
||||
update
|
||||
end
|
||||
|
||||
def groundY
|
||||
return @character.screen_y_ground
|
||||
end
|
||||
|
||||
def visible=(value)
|
||||
super(value)
|
||||
@reflection.visible = value if @reflection
|
||||
end
|
||||
|
||||
def dispose
|
||||
@bushbitmap.dispose if @bushbitmap
|
||||
@bushbitmap = nil
|
||||
@charbitmap.dispose if @charbitmap
|
||||
@charbitmap = nil
|
||||
@reflection.dispose if @reflection
|
||||
@reflection = nil
|
||||
@surfbase.dispose if @surfbase
|
||||
@surfbase = nil
|
||||
super
|
||||
end
|
||||
|
||||
def update
|
||||
return if @character.is_a?(Game_Event) && !@character.should_update?
|
||||
super
|
||||
if @tile_id!=@character.tile_id or
|
||||
@character_name!=@character.character_name or
|
||||
@character_hue!=@character.character_hue or
|
||||
@oldbushdepth!=@character.bush_depth
|
||||
@tile_id = @character.tile_id
|
||||
@character_name = @character.character_name
|
||||
@character_hue = @character.character_hue
|
||||
@oldbushdepth = @character.bush_depth
|
||||
if @tile_id>=384
|
||||
@charbitmap.dispose if @charbitmap
|
||||
@charbitmap = pbGetTileBitmap(@character.map.tileset_name,@tile_id,@character.character_hue)
|
||||
@charbitmapAnimated = false
|
||||
@bushbitmap.dispose if @bushbitmap
|
||||
@bushbitmap = nil
|
||||
@spriteoffset = false
|
||||
@cw = Game_Map::TILE_WIDTH
|
||||
@ch = Game_Map::TILE_HEIGHT
|
||||
self.src_rect.set(0,0,@cw,@ch)
|
||||
self.ox = @cw/2
|
||||
self.oy = @ch
|
||||
@character.sprite_size = [@cw,@ch]
|
||||
else
|
||||
@charbitmap.dispose if @charbitmap
|
||||
@charbitmap = AnimatedBitmap.new(
|
||||
"Graphics/Characters/"+@character.character_name,@character.character_hue)
|
||||
@charbitmapAnimated = true
|
||||
@bushbitmap.dispose if @bushbitmap
|
||||
@bushbitmap = nil
|
||||
@spriteoffset = @character_name[/offset/i]
|
||||
@cw = @charbitmap.width/4
|
||||
@ch = @charbitmap.height/4
|
||||
self.ox = @cw/2
|
||||
@character.sprite_size = [@cw,@ch]
|
||||
end
|
||||
end
|
||||
@charbitmap.update if @charbitmapAnimated
|
||||
bushdepth = @character.bush_depth
|
||||
if bushdepth==0
|
||||
self.bitmap = (@charbitmapAnimated) ? @charbitmap.bitmap : @charbitmap
|
||||
else
|
||||
@bushbitmap = BushBitmap.new(@charbitmap,(@tile_id>=384),bushdepth) if !@bushbitmap
|
||||
self.bitmap = @bushbitmap.bitmap
|
||||
end
|
||||
self.visible = !@character.transparent
|
||||
if @tile_id==0
|
||||
sx = @character.pattern*@cw
|
||||
sy = ((@character.direction-2)/2)*@ch
|
||||
self.src_rect.set(sx,sy,@cw,@ch)
|
||||
self.oy = (@spriteoffset rescue false) ? @ch-16 : @ch
|
||||
self.oy -= @character.bob_height
|
||||
end
|
||||
if self.visible
|
||||
if $PokemonSystem.tilemap==0 ||
|
||||
(@character.is_a?(Game_Event) && @character.name[/regulartone/i])
|
||||
self.tone.set(0,0,0,0)
|
||||
else
|
||||
pbDayNightTint(self)
|
||||
end
|
||||
end
|
||||
self.x = @character.screen_x
|
||||
self.y = @character.screen_y
|
||||
self.z = @character.screen_z(@ch)
|
||||
# self.zoom_x = Game_Map::TILE_WIDTH/32.0
|
||||
# self.zoom_y = Game_Map::TILE_HEIGHT/32.0
|
||||
self.opacity = @character.opacity
|
||||
self.blend_type = @character.blend_type
|
||||
# self.bush_depth = @character.bush_depth
|
||||
if @character.animation_id!=0
|
||||
animation = $data_animations[@character.animation_id]
|
||||
animation(animation,true)
|
||||
@character.animation_id = 0
|
||||
end
|
||||
@reflection.update if @reflection
|
||||
@surfbase.update if @surfbase
|
||||
end
|
||||
end
|
||||
90
Data/Scripts/004_Sprites/004_Sprite_WaterReflection.rb
Normal file
90
Data/Scripts/004_Sprites/004_Sprite_WaterReflection.rb
Normal file
@@ -0,0 +1,90 @@
|
||||
class Sprite_Reflection
|
||||
attr_reader :visible
|
||||
attr_accessor :event
|
||||
|
||||
def initialize(sprite,event,viewport=nil)
|
||||
@rsprite = sprite
|
||||
@sprite = nil
|
||||
@event = event
|
||||
@height = 0
|
||||
@fixedheight = false
|
||||
if @event && @event!=$game_player
|
||||
if @event.name[/reflection\((\d+)\)/i]
|
||||
@height = $~[1].to_i || 0
|
||||
@fixedheight = true
|
||||
end
|
||||
end
|
||||
@viewport = viewport
|
||||
@disposed = false
|
||||
update
|
||||
end
|
||||
|
||||
def dispose
|
||||
if !@disposed
|
||||
@sprite.dispose if @sprite
|
||||
@sprite = nil
|
||||
@disposed = true
|
||||
end
|
||||
end
|
||||
|
||||
def disposed?
|
||||
@disposed
|
||||
end
|
||||
|
||||
def visible=(value)
|
||||
@visible = value
|
||||
@sprite.visible = value if @sprite && !@sprite.disposed?
|
||||
end
|
||||
|
||||
def update
|
||||
return if disposed?
|
||||
shouldShow = @rsprite.visible
|
||||
if !shouldShow
|
||||
# Just-in-time disposal of sprite
|
||||
if @sprite
|
||||
@sprite.dispose
|
||||
@sprite = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
# Just-in-time creation of sprite
|
||||
@sprite = Sprite.new(@viewport) if !@sprite
|
||||
if @sprite
|
||||
x = @rsprite.x-@rsprite.ox
|
||||
y = @rsprite.y-@rsprite.oy
|
||||
y -= 32 if @rsprite.character.character_name[/offset/i]
|
||||
@height = $PokemonGlobal.bridge if !@fixedheight
|
||||
y += @height*16
|
||||
width = @rsprite.src_rect.width
|
||||
height = @rsprite.src_rect.height
|
||||
@sprite.x = x+width/2
|
||||
@sprite.y = y+height+height/2
|
||||
@sprite.ox = width/2
|
||||
@sprite.oy = height/2-2 # Hard-coded 2 pixel shift up
|
||||
@sprite.oy -= @rsprite.character.bob_height*2
|
||||
@sprite.z = -50 # Still water is -100, map is 0 and above
|
||||
@sprite.zoom_x = @rsprite.zoom_x
|
||||
@sprite.zoom_y = @rsprite.zoom_y
|
||||
frame = (Graphics.frame_count%40)/10
|
||||
case frame
|
||||
when 1; @sprite.zoom_x *= 0.95
|
||||
when 3; @sprite.zoom_x *= 1.05
|
||||
else; @sprite.zoom_x *= 1.0
|
||||
end
|
||||
@sprite.angle = 180.0
|
||||
@sprite.mirror = true
|
||||
@sprite.bitmap = @rsprite.bitmap
|
||||
@sprite.tone = @rsprite.tone
|
||||
if @height>0
|
||||
@sprite.color = Color.new(48,96,160,255) # Dark still water
|
||||
@sprite.opacity = @rsprite.opacity
|
||||
@sprite.visible = !TIME_SHADING # Can't time-tone a colored sprite
|
||||
else
|
||||
@sprite.color = Color.new(224,224,224,96)
|
||||
@sprite.opacity = @rsprite.opacity*3/4
|
||||
@sprite.visible = true
|
||||
end
|
||||
@sprite.src_rect = @rsprite.src_rect
|
||||
end
|
||||
end
|
||||
end
|
||||
77
Data/Scripts/004_Sprites/005_Sprite_SurfBase.rb
Normal file
77
Data/Scripts/004_Sprites/005_Sprite_SurfBase.rb
Normal file
@@ -0,0 +1,77 @@
|
||||
class Sprite_SurfBase
|
||||
attr_reader :visible
|
||||
attr_accessor :event
|
||||
|
||||
def initialize(sprite,event,viewport=nil)
|
||||
@rsprite = sprite
|
||||
@sprite = nil
|
||||
@event = event
|
||||
@viewport = viewport
|
||||
@disposed = false
|
||||
@surfbitmap = AnimatedBitmap.new("Graphics/Characters/base_surf")
|
||||
@divebitmap = AnimatedBitmap.new("Graphics/Characters/base_dive")
|
||||
@cws = @surfbitmap.width/4
|
||||
@chs = @surfbitmap.height/4
|
||||
@cwd = @divebitmap.width/4
|
||||
@chd = @divebitmap.height/4
|
||||
update
|
||||
end
|
||||
|
||||
def dispose
|
||||
if !@disposed
|
||||
@sprite.dispose if @sprite
|
||||
@sprite = nil
|
||||
@surfbitmap.dispose
|
||||
@disposed = true
|
||||
end
|
||||
end
|
||||
|
||||
def disposed?
|
||||
@disposed
|
||||
end
|
||||
|
||||
def visible=(value)
|
||||
@visible = value
|
||||
@sprite.visible = value if @sprite && !@sprite.disposed?
|
||||
end
|
||||
|
||||
def update
|
||||
return if disposed?
|
||||
if !$PokemonGlobal.surfing && !$PokemonGlobal.diving
|
||||
# Just-in-time disposal of sprite
|
||||
if @sprite
|
||||
@sprite.dispose
|
||||
@sprite = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
# Just-in-time creation of sprite
|
||||
@sprite = Sprite.new(@viewport) if !@sprite
|
||||
if @sprite
|
||||
if $PokemonGlobal.surfing
|
||||
@sprite.bitmap = @surfbitmap.bitmap; cw = @cws; ch = @chs
|
||||
elsif $PokemonGlobal.diving
|
||||
@sprite.bitmap = @divebitmap.bitmap; cw = @cwd; ch = @chd
|
||||
end
|
||||
sx = @event.pattern_surf*cw
|
||||
sy = ((@event.direction-2)/2)*ch
|
||||
@sprite.src_rect.set(sx,sy,cw,ch)
|
||||
if $PokemonTemp.surfJump
|
||||
@sprite.x = ($PokemonTemp.surfJump[0]*Game_Map::REAL_RES_X-@event.map.display_x+3)/4+(Game_Map::TILE_WIDTH/2)
|
||||
@sprite.y = ($PokemonTemp.surfJump[1]*Game_Map::REAL_RES_Y-@event.map.display_y+3)/4+(Game_Map::TILE_HEIGHT/2)+16
|
||||
else
|
||||
@sprite.x = @rsprite.x
|
||||
@sprite.y = @rsprite.y
|
||||
end
|
||||
@sprite.ox = cw/2
|
||||
@sprite.oy = ch-16 # Assume base needs offsetting
|
||||
@sprite.oy -= @event.bob_height
|
||||
@sprite.z = @event.screen_z(ch)-1
|
||||
@sprite.zoom_x = @rsprite.zoom_x
|
||||
@sprite.zoom_y = @rsprite.zoom_y
|
||||
@sprite.tone = @rsprite.tone
|
||||
@sprite.color = @rsprite.color
|
||||
@sprite.opacity = @rsprite.opacity
|
||||
end
|
||||
end
|
||||
end
|
||||
167
Data/Scripts/004_Sprites/006_Spriteset_Map.rb
Normal file
167
Data/Scripts/004_Sprites/006_Spriteset_Map.rb
Normal file
@@ -0,0 +1,167 @@
|
||||
class ClippableSprite < Sprite_Character
|
||||
def initialize(viewport,event,tilemap)
|
||||
@tilemap = tilemap
|
||||
@_src_rect = Rect.new(0,0,0,0)
|
||||
super(viewport,event)
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
@_src_rect = self.src_rect
|
||||
tmright = @tilemap.map_data.xsize*Game_Map::TILE_WIDTH-@tilemap.ox
|
||||
echoln("x=#{self.x},ox=#{self.ox},tmright=#{tmright},tmox=#{@tilemap.ox}")
|
||||
if @tilemap.ox-self.ox<-self.x
|
||||
# clipped on left
|
||||
diff = -self.x-@tilemap.ox+self.ox
|
||||
self.src_rect = Rect.new(@_src_rect.x+diff,@_src_rect.y,
|
||||
@_src_rect.width-diff,@_src_rect.height)
|
||||
echoln("clipped out left: #{diff} #{@tilemap.ox-self.ox} #{self.x}")
|
||||
elsif tmright-self.ox<self.x
|
||||
# clipped on right
|
||||
diff = self.x-tmright+self.ox
|
||||
self.src_rect = Rect.new(@_src_rect.x,@_src_rect.y,
|
||||
@_src_rect.width-diff,@_src_rect.height)
|
||||
echoln("clipped out right: #{diff} #{tmright+self.ox} #{self.x}")
|
||||
else
|
||||
echoln("-not- clipped out left: #{diff} #{@tilemap.ox-self.ox} #{self.x}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Spriteset_Map
|
||||
attr_reader :map
|
||||
attr_accessor :tilemap
|
||||
@@viewport0 = Viewport.new(0,0,Graphics.width,Graphics.height) # Panorama
|
||||
@@viewport0.z = -100
|
||||
@@viewport1 = Viewport.new(0,0,Graphics.width,Graphics.height) # Map, events, player, fog
|
||||
@@viewport1.z = 0
|
||||
@@viewport3 = Viewport.new(0,0,Graphics.width,Graphics.height) # Flashing
|
||||
@@viewport3.z = 500
|
||||
|
||||
def Spriteset_Map.viewport # For access by Spriteset_Global
|
||||
return @@viewport1
|
||||
end
|
||||
|
||||
def initialize(map=nil)
|
||||
@map = (map) ? map : $game_map
|
||||
@tilemap = TilemapLoader.new(@@viewport1)
|
||||
@tilemap.tileset = pbGetTileset(@map.tileset_name)
|
||||
for i in 0...7
|
||||
autotile_name = @map.autotile_names[i]
|
||||
@tilemap.autotiles[i] = pbGetAutotile(autotile_name)
|
||||
end
|
||||
@tilemap.map_data = @map.data
|
||||
@tilemap.priorities = @map.priorities
|
||||
@tilemap.terrain_tags = @map.terrain_tags
|
||||
@panorama = AnimatedPlane.new(@@viewport0)
|
||||
@fog = AnimatedPlane.new(@@viewport1)
|
||||
@fog.z = 3000
|
||||
@character_sprites = []
|
||||
for i in @map.events.keys.sort
|
||||
sprite = Sprite_Character.new(@@viewport1,@map.events[i])
|
||||
@character_sprites.push(sprite)
|
||||
end
|
||||
@weather = RPG::Weather.new(@@viewport1)
|
||||
pbOnSpritesetCreate(self,@@viewport1)
|
||||
update
|
||||
end
|
||||
|
||||
def dispose
|
||||
@tilemap.tileset.dispose
|
||||
for i in 0...7
|
||||
@tilemap.autotiles[i].dispose
|
||||
end
|
||||
@tilemap.dispose
|
||||
@panorama.dispose
|
||||
@fog.dispose
|
||||
for sprite in @character_sprites
|
||||
sprite.dispose
|
||||
end
|
||||
@weather.dispose
|
||||
@tilemap = nil
|
||||
@panorama = nil
|
||||
@fog = nil
|
||||
@character_sprites.clear
|
||||
@weather = nil
|
||||
end
|
||||
|
||||
def getAnimations
|
||||
return @usersprites
|
||||
end
|
||||
|
||||
def restoreAnimations(anims)
|
||||
@usersprites = anims
|
||||
end
|
||||
|
||||
def update
|
||||
if @panorama_name!=@map.panorama_name || @panorama_hue!=@map.panorama_hue
|
||||
@panorama_name = @map.panorama_name
|
||||
@panorama_hue = @map.panorama_hue
|
||||
@panorama.setPanorama(nil) if @panorama.bitmap!=nil
|
||||
@panorama.setPanorama(@panorama_name,@panorama_hue) if @panorama_name!=""
|
||||
Graphics.frame_reset
|
||||
end
|
||||
if @fog_name!=@map.fog_name || @fog_hue!=@map.fog_hue
|
||||
@fog_name = @map.fog_name
|
||||
@fog_hue = @map.fog_hue
|
||||
@fog.setFog(nil) if @fog.bitmap!=nil
|
||||
@fog.setFog(@fog_name,@fog_hue) if @fog_name!=""
|
||||
Graphics.frame_reset
|
||||
end
|
||||
tmox = (@map.display_x/Game_Map::X_SUBPIXELS).round
|
||||
tmoy = (@map.display_y/Game_Map::Y_SUBPIXELS).round
|
||||
@tilemap.ox = tmox
|
||||
@tilemap.oy = tmoy
|
||||
if $PokemonSystem.tilemap==0 # Original Map View only, to prevent wrapping
|
||||
@@viewport1.rect.x = [-tmox,0].max
|
||||
@@viewport1.rect.y = [-tmoy,0].max
|
||||
@@viewport1.rect.width = [@tilemap.map_data.xsize*Game_Map::TILE_WIDTH-tmox,Graphics.width].min
|
||||
@@viewport1.rect.height = [@tilemap.map_data.ysize*Game_Map::TILE_HEIGHT-tmoy,Graphics.height].min
|
||||
@@viewport1.ox = [-tmox,0].max
|
||||
@@viewport1.oy = [-tmoy,0].max
|
||||
else
|
||||
@@viewport1.rect.set(0,0,Graphics.width,Graphics.height)
|
||||
@@viewport1.ox = 0
|
||||
@@viewport1.oy = 0
|
||||
end
|
||||
@@viewport1.ox += $game_screen.shake
|
||||
@tilemap.update
|
||||
@panorama.ox = tmox/2
|
||||
@panorama.oy = tmoy/2
|
||||
@fog.ox = tmox+@map.fog_ox
|
||||
@fog.oy = tmoy+@map.fog_oy
|
||||
@fog.zoom_x = @map.fog_zoom/100.0
|
||||
@fog.zoom_y = @map.fog_zoom/100.0
|
||||
@fog.opacity = @map.fog_opacity
|
||||
@fog.blend_type = @map.fog_blend_type
|
||||
@fog.tone = @map.fog_tone
|
||||
@panorama.update
|
||||
@fog.update
|
||||
for sprite in @character_sprites
|
||||
sprite.update
|
||||
end
|
||||
if self.map!=$game_map
|
||||
if @weather.max>0
|
||||
@weather.max -= 2
|
||||
if @weather.max<=0
|
||||
@weather.max = 0
|
||||
@weather.type = 0
|
||||
@weather.ox = 0
|
||||
@weather.oy = 0
|
||||
end
|
||||
end
|
||||
else
|
||||
@weather.type = $game_screen.weather_type
|
||||
@weather.max = $game_screen.weather_max
|
||||
@weather.ox = tmox
|
||||
@weather.oy = tmoy
|
||||
end
|
||||
@weather.update
|
||||
@@viewport1.tone = $game_screen.tone
|
||||
@@viewport3.color = $game_screen.flash_color
|
||||
@@viewport1.update
|
||||
@@viewport3.update
|
||||
end
|
||||
end
|
||||
34
Data/Scripts/004_Sprites/007_Spriteset_Global.rb
Normal file
34
Data/Scripts/004_Sprites/007_Spriteset_Global.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
class Spriteset_Global
|
||||
attr_reader :playersprite
|
||||
@@viewport2 = Viewport.new(0,0,Graphics.width,Graphics.height)
|
||||
@@viewport2.z = 200
|
||||
|
||||
def initialize
|
||||
@playersprite = Sprite_Character.new(Spriteset_Map.viewport,$game_player)
|
||||
@picture_sprites = []
|
||||
for i in 1..100
|
||||
@picture_sprites.push(Sprite_Picture.new(@@viewport2,$game_screen.pictures[i]))
|
||||
end
|
||||
@timer_sprite = Sprite_Timer.new
|
||||
update
|
||||
end
|
||||
|
||||
def dispose
|
||||
@playersprite.dispose
|
||||
for sprite in @picture_sprites
|
||||
sprite.dispose
|
||||
end
|
||||
@timer_sprite.dispose
|
||||
@playersprite = nil
|
||||
@picture_sprites.clear
|
||||
@timer_sprite = nil
|
||||
end
|
||||
|
||||
def update
|
||||
@playersprite.update
|
||||
for sprite in @picture_sprites
|
||||
sprite.update
|
||||
end
|
||||
@timer_sprite.update
|
||||
end
|
||||
end
|
||||
94
Data/Scripts/004_Sprites/008_Sprite_AnimationSprite.rb
Normal file
94
Data/Scripts/004_Sprites/008_Sprite_AnimationSprite.rb
Normal file
@@ -0,0 +1,94 @@
|
||||
=begin
|
||||
A sprite whose sole purpose is to display an animation. This sprite
|
||||
can be displayed anywhere on the map and is disposed
|
||||
automatically when its animation is finished.
|
||||
Used for grass rustling and so forth.
|
||||
=end
|
||||
class AnimationSprite < RPG::Sprite
|
||||
def initialize(animID,map,tileX,tileY,viewport=nil,tinting=false,height=3)
|
||||
super(viewport)
|
||||
@tileX = tileX
|
||||
@tileY = tileY
|
||||
self.bitmap = Bitmap.new(1, 1)
|
||||
self.bitmap.clear
|
||||
@map = map
|
||||
setCoords
|
||||
pbDayNightTint(self) if tinting
|
||||
self.animation($data_animations[animID],true,height)
|
||||
end
|
||||
|
||||
def setCoords
|
||||
self.x = ((@tileX * Game_Map::REAL_RES_X - @map.display_x) / Game_Map::X_SUBPIXELS).ceil
|
||||
self.x += Game_Map::TILE_WIDTH / 2
|
||||
self.y = ((@tileY * Game_Map::REAL_RES_Y - @map.display_y) / Game_Map::Y_SUBPIXELS).ceil
|
||||
self.y += Game_Map::TILE_HEIGHT
|
||||
end
|
||||
|
||||
def dispose
|
||||
self.bitmap.dispose
|
||||
super
|
||||
end
|
||||
|
||||
def update
|
||||
if !self.disposed?
|
||||
setCoords
|
||||
super
|
||||
self.dispose if !self.effect?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Spriteset_Map
|
||||
alias _animationSprite_initialize initialize
|
||||
alias _animationSprite_update update
|
||||
alias _animationSprite_dispose dispose
|
||||
|
||||
def initialize(map=nil)
|
||||
@usersprites=[]
|
||||
_animationSprite_initialize(map)
|
||||
end
|
||||
|
||||
def addUserAnimation(animID,x,y,tinting=false,height=3)
|
||||
sprite=AnimationSprite.new(animID,$game_map,x,y,@@viewport1,tinting,height)
|
||||
addUserSprite(sprite)
|
||||
return sprite
|
||||
end
|
||||
|
||||
def addUserSprite(sprite)
|
||||
for i in 0...@usersprites.length
|
||||
if @usersprites[i]==nil || @usersprites[i].disposed?
|
||||
@usersprites[i]=sprite
|
||||
return
|
||||
end
|
||||
end
|
||||
@usersprites.push(sprite)
|
||||
end
|
||||
|
||||
def dispose
|
||||
_animationSprite_dispose
|
||||
for i in 0...@usersprites.length
|
||||
@usersprites[i].dispose
|
||||
end
|
||||
@usersprites.clear
|
||||
end
|
||||
|
||||
def update
|
||||
return if @tilemap.disposed?
|
||||
if $RPGVX || $PokemonSystem.tilemap==0
|
||||
if self.map==$game_map
|
||||
pbDayNightTint(@@viewport3)
|
||||
else
|
||||
@@viewport3.tone.set(0,0,0,0)
|
||||
end
|
||||
else
|
||||
pbDayNightTint(@tilemap)
|
||||
@@viewport3.tone.set(0,0,0,0)
|
||||
end
|
||||
_animationSprite_update
|
||||
for i in 0...@usersprites.length
|
||||
@usersprites[i].update if !@usersprites[i].disposed?
|
||||
end
|
||||
end
|
||||
end
|
||||
255
Data/Scripts/004_Sprites/009_Sprite_DynamicShadows.rb
Normal file
255
Data/Scripts/004_Sprites/009_Sprite_DynamicShadows.rb
Normal file
@@ -0,0 +1,255 @@
|
||||
#===============================================================================
|
||||
# Sprite_Shadow (Sprite_Ombre )
|
||||
# Based on Genzai Kawakami's shadows, dynamisme & features by Rataime, extra
|
||||
# features Boushy
|
||||
# Modified by Peter O. to be compatible with Pokémon Essentials
|
||||
#===============================================================================
|
||||
SHADOW_WARN = true
|
||||
|
||||
class Sprite_Shadow < RPG::Sprite
|
||||
attr_accessor :character
|
||||
|
||||
def initialize(viewport, character = nil,params=[])
|
||||
super(viewport)
|
||||
@source = params[0]
|
||||
@anglemin = (params.size>1) ? params[1] : 0
|
||||
@anglemax = (params.size>2) ? params[2] : 0
|
||||
@self_opacity = (params.size>4) ? params[4] : 100
|
||||
@distancemax = (params.size>3) ? params[3] : 350
|
||||
@character = character
|
||||
update
|
||||
end
|
||||
|
||||
def dispose
|
||||
@chbitmap.dispose if @chbitmap
|
||||
super
|
||||
end
|
||||
|
||||
def update
|
||||
if !in_range?(@character, @source, @distancemax)
|
||||
self.opacity = 0
|
||||
return
|
||||
end
|
||||
super
|
||||
if @tile_id != @character.tile_id or
|
||||
@character_name != @character.character_name or
|
||||
@character_hue != @character.character_hue
|
||||
@tile_id = @character.tile_id
|
||||
@character_name = @character.character_name
|
||||
@character_hue = @character.character_hue
|
||||
if @tile_id >= 384
|
||||
@chbitmap.dispose if @chbitmap
|
||||
@chbitmap = pbGetTileBitmap(@character.map.tileset_name,
|
||||
@tile_id, @character.character_hue)
|
||||
self.src_rect.set(0, 0, 32, 32)
|
||||
@ch = 32
|
||||
@cw = 32
|
||||
self.ox = 16
|
||||
self.oy = 32
|
||||
else
|
||||
@chbitmap.dispose if @chbitmap
|
||||
@chbitmap = AnimatedBitmap.new(
|
||||
"Graphics/Characters/"+@character.character_name,@character.character_hue)
|
||||
@cw = @chbitmap.width / 4
|
||||
@ch = @chbitmap.height / 4
|
||||
self.ox = @cw / 2
|
||||
self.oy = @ch
|
||||
end
|
||||
end
|
||||
if @chbitmap.is_a?(AnimatedBitmap)
|
||||
@chbitmap.update
|
||||
self.bitmap = @chbitmap.bitmap
|
||||
else
|
||||
self.bitmap = @chbitmap
|
||||
end
|
||||
self.visible = (not @character.transparent)
|
||||
if @tile_id == 0
|
||||
sx = @character.pattern * @cw
|
||||
sy = (@character.direction - 2) / 2 * @ch
|
||||
if self.angle > 90 or angle < -90
|
||||
case @character.direction
|
||||
when 2; sy = (8- 2) / 2 * @ch
|
||||
when 4; sy = (6- 2) / 2 * @ch
|
||||
when 6; sy = (4- 2) / 2 * @ch
|
||||
when 8; sy = (2- 2) / 2 * @ch
|
||||
end
|
||||
end
|
||||
self.src_rect.set(sx, sy, @cw, @ch)
|
||||
end
|
||||
self.x = ScreenPosHelper.pbScreenX(@character)
|
||||
self.y = ScreenPosHelper.pbScreenY(@character)-5
|
||||
self.z = ScreenPosHelper.pbScreenZ(@character,@ch)-1
|
||||
self.zoom_x = ScreenPosHelper.pbScreenZoomX(@character)
|
||||
self.zoom_y = ScreenPosHelper.pbScreenZoomY(@character)
|
||||
self.blend_type = @character.blend_type
|
||||
self.bush_depth = @character.bush_depth
|
||||
if @character.animation_id != 0
|
||||
animation = $data_animations[@character.animation_id]
|
||||
animation(animation, true)
|
||||
@character.animation_id = 0
|
||||
end
|
||||
@deltax = ScreenPosHelper.pbScreenX(@source) - self.x
|
||||
@deltay = ScreenPosHelper.pbScreenY(@source) - self.y
|
||||
self.color = Color.new(0, 0, 0)
|
||||
@distance = ((@deltax ** 2) + (@deltay ** 2))
|
||||
self.opacity = @self_opacity * 13000 / ((@distance * 370 / @distancemax) + 6000)
|
||||
self.angle = 57.3 * Math.atan2(@deltax, @deltay)
|
||||
@angle_trigo = self.angle+90
|
||||
@angle_trigo += 360 if @angle_trigo < 0
|
||||
if @anglemin != 0 or @anglemax != 0
|
||||
if (@angle_trigo < @anglemin or @angle_trigo > @anglemax) and @anglemin < @anglemax
|
||||
self.opacity = 0
|
||||
return
|
||||
end
|
||||
if (@angle_trigo < @anglemin and @angle_trigo > @anglemax) and @anglemin > @anglemax
|
||||
self.opacity = 0
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def in_range?(element, object, range) # From Near's Anti Lag Script, edited
|
||||
elemScreenX = ScreenPosHelper.pbScreenX(element)
|
||||
elemScreenY = ScreenPosHelper.pbScreenY(element)
|
||||
objScreenX = ScreenPosHelper.pbScreenX(object)
|
||||
objScreenY = ScreenPosHelper.pbScreenY(object)
|
||||
x = (elemScreenX - objScreenX) * (elemScreenX - objScreenX)
|
||||
y = (elemScreenY - objScreenY) * (elemScreenY - objScreenY)
|
||||
r = x + y
|
||||
return r <= range * range
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===================================================
|
||||
# ? CLASS Sprite_Character edit
|
||||
#===================================================
|
||||
class Sprite_Character < RPG::Sprite
|
||||
alias :shadow_initialize :initialize
|
||||
|
||||
def initialize(viewport, character = nil)
|
||||
@ombrelist = []
|
||||
@character = character
|
||||
shadow_initialize(viewport, @character)
|
||||
end
|
||||
|
||||
def setShadows(map,shadows)
|
||||
if character.is_a?(Game_Event) and shadows.length > 0
|
||||
params = XPML_read(map,"Shadow",@character,4)
|
||||
if params != nil
|
||||
for i in 0...shadows.size
|
||||
@ombrelist.push(Sprite_Shadow.new(viewport, @character, shadows[i]))
|
||||
end
|
||||
end
|
||||
end
|
||||
if character.is_a?(Game_Player) and shadows.length > 0
|
||||
for i in 0...shadows.size
|
||||
@ombrelist.push(Sprite_Shadow.new(viewport, $game_player, shadows[i]))
|
||||
end
|
||||
end
|
||||
update
|
||||
end
|
||||
|
||||
alias shadow_update update
|
||||
|
||||
def update
|
||||
shadow_update
|
||||
if @ombrelist.length>0
|
||||
for i in 0...@ombrelist.size
|
||||
@ombrelist[i].update
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===================================================
|
||||
# ? CLASS Game_Event edit
|
||||
#===================================================
|
||||
class Game_Event
|
||||
attr_accessor :id
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===================================================
|
||||
# ? CLASS Spriteset_Map edit
|
||||
#===================================================
|
||||
class Spriteset_Map
|
||||
attr_accessor :shadows
|
||||
|
||||
alias shadow_initialize initialize
|
||||
def initialize(map=nil)
|
||||
@shadows = []
|
||||
warn = false
|
||||
map = $game_map if !map
|
||||
for k in map.events.keys.sort
|
||||
ev = map.events[k]
|
||||
warn = true if (ev.list != nil and ev.list.length > 0 and
|
||||
ev.list[0].code == 108 and
|
||||
(ev.list[0].parameters == ["s"] or ev.list[0].parameters == ["o"]))
|
||||
params = XPML_read(map,"Shadow Source", ev, 4)
|
||||
@shadows.push([ev] + params) if params != nil
|
||||
end
|
||||
if warn == true and SHADOW_WARN
|
||||
p "Warning : At least one event on this map uses the obsolete way to add shadows"
|
||||
end
|
||||
shadow_initialize(map)
|
||||
for sprite in @character_sprites
|
||||
sprite.setShadows(map, @shadows)
|
||||
end
|
||||
$scene.spritesetGlobal.playersprite.setShadows(map, @shadows)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===================================================
|
||||
# ? XPML Definition, by Rataime, using ideas from Near Fantastica
|
||||
#
|
||||
# Returns nil if the markup wasn't present at all,
|
||||
# returns [] if there wasn't any parameters, else
|
||||
# returns a parameters list with "int" converted as int
|
||||
# eg :
|
||||
# begin first
|
||||
# begin second
|
||||
# param1 1
|
||||
# param2 two
|
||||
# begin third
|
||||
# anything 3
|
||||
#
|
||||
# p XPML_read("first", event_id) -> []
|
||||
# p XPML_read("second", event_id) -> [1, "two"]
|
||||
# p XPML_read("third", event_id) -> [3]
|
||||
# p XPML_read("forth", event_id) -> nil
|
||||
#===================================================
|
||||
def XPML_read(map,markup,event,max_param_number=0)
|
||||
parameter_list = nil
|
||||
return nil if !event || event.list == nil
|
||||
for i in 0...event.list.size
|
||||
if event.list[i].code == 108 and
|
||||
event.list[i].parameters[0].downcase == "begin " + markup.downcase
|
||||
parameter_list = [] if parameter_list == nil
|
||||
for j in i+1...event.list.size
|
||||
if event.list[j].code == 108
|
||||
parts = event.list[j].parameters[0].split
|
||||
if parts.size != 1 and parts[0].downcase != "begin"
|
||||
if parts[1].to_i != 0 or parts[1] == "0"
|
||||
parameter_list.push(parts[1].to_i)
|
||||
else
|
||||
parameter_list.push(parts[1])
|
||||
end
|
||||
else
|
||||
return parameter_list
|
||||
end
|
||||
else
|
||||
return parameter_list
|
||||
end
|
||||
return parameter_list if max_param_number != 0 and j == i + max_param_number
|
||||
end
|
||||
end
|
||||
end
|
||||
return parameter_list
|
||||
end
|
||||
586
Data/Scripts/004_Sprites/010_ParticleEngine.rb
Normal file
586
Data/Scripts/004_Sprites/010_ParticleEngine.rb
Normal file
@@ -0,0 +1,586 @@
|
||||
# Particle Engine, Peter O., 2007-11-03
|
||||
# Based on version 2 by Near Fantastica, 04.01.06
|
||||
# In turn based on the Particle Engine designed by PinkMan
|
||||
class Particle_Engine
|
||||
def initialize(viewport=nil,map=nil)
|
||||
@map = (map) ? map : $game_map
|
||||
@viewport = viewport
|
||||
@effect = []
|
||||
@disposed = false
|
||||
@firsttime = true
|
||||
@effects = {
|
||||
# PinkMan's Effects
|
||||
"fire" => Particle_Engine::Fire,
|
||||
"smoke" => Particle_Engine::Smoke,
|
||||
"teleport" => Particle_Engine::Teleport,
|
||||
"spirit" => Particle_Engine::Spirit,
|
||||
"explosion" => Particle_Engine::Explosion,
|
||||
"aura" => Particle_Engine::Aura,
|
||||
# BlueScope's Effects
|
||||
"soot" => Particle_Engine::Soot,
|
||||
"sootsmoke" => Particle_Engine::SootSmoke,
|
||||
"rocket" => Particle_Engine::Rocket,
|
||||
"fixteleport" => Particle_Engine::FixedTeleport,
|
||||
"smokescreen" => Particle_Engine::Smokescreen,
|
||||
"flare" => Particle_Engine::Flare,
|
||||
"splash" => Particle_Engine::Splash,
|
||||
# By Peter O.
|
||||
"starteleport" => Particle_Engine::StarTeleport
|
||||
}
|
||||
end
|
||||
|
||||
def dispose
|
||||
return if disposed?
|
||||
for particle in @effect
|
||||
next if particle.nil?
|
||||
particle.dispose
|
||||
end
|
||||
@effect.clear
|
||||
@map = nil
|
||||
@disposed = true
|
||||
end
|
||||
|
||||
def disposed?
|
||||
return @disposed
|
||||
end
|
||||
|
||||
def add_effect(event)
|
||||
@effect[event.id] = pbParticleEffect(event)
|
||||
end
|
||||
|
||||
def remove_effect(event)
|
||||
return if @effect[event.id].nil?
|
||||
@effect[event.id].dispose
|
||||
@effect.delete_at(event.id)
|
||||
end
|
||||
|
||||
def realloc_effect(event,particle)
|
||||
type = pbEventCommentInput(event, 1, "Particle Engine Type")
|
||||
if type.nil?
|
||||
particle.dispose if particle
|
||||
return nil
|
||||
end
|
||||
type = type[0].downcase
|
||||
cls = @effects[type]
|
||||
if cls.nil?
|
||||
particle.dispose if particle
|
||||
return nil
|
||||
end
|
||||
if !particle || !particle.is_a?(cls)
|
||||
particle.dispose if particle
|
||||
particle = cls.new(event,@viewport)
|
||||
end
|
||||
return particle
|
||||
end
|
||||
|
||||
def pbParticleEffect(event)
|
||||
return realloc_effect(event,nil)
|
||||
end
|
||||
|
||||
def update
|
||||
if @firsttime
|
||||
@firsttime = false
|
||||
for event in @map.events.values
|
||||
remove_effect(event)
|
||||
add_effect(event)
|
||||
end
|
||||
end
|
||||
for i in 0...@effect.length
|
||||
particle = @effect[i]
|
||||
next if particle.nil?
|
||||
if particle.event.pe_refresh
|
||||
event = particle.event
|
||||
event.pe_refresh = false
|
||||
particle = realloc_effect(event,particle)
|
||||
@effect[i] = particle
|
||||
end
|
||||
particle.update if particle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class ParticleEffect
|
||||
attr_accessor :x, :y, :z
|
||||
|
||||
def initialize
|
||||
@x = 0
|
||||
@y = 0
|
||||
@z = 0
|
||||
end
|
||||
|
||||
def update; end
|
||||
def dispose; end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class ParticleSprite
|
||||
attr_accessor :x, :y, :z, :ox, :oy, :opacity, :bitmap, :blend_type
|
||||
|
||||
def initialize(viewport)
|
||||
@viewport = viewport
|
||||
@sprite = nil
|
||||
@x = 0
|
||||
@y = 0
|
||||
@z = 0
|
||||
@ox = 0
|
||||
@oy = 0
|
||||
@opacity = 255
|
||||
@bitmap = nil
|
||||
@blend_type = 0
|
||||
@minleft = 0
|
||||
@mintop = 0
|
||||
end
|
||||
|
||||
def dispose
|
||||
@sprite.dispose if @sprite
|
||||
end
|
||||
|
||||
def bitmap=(value)
|
||||
@bitmap = value
|
||||
if value
|
||||
@minleft = -value.width
|
||||
@mintop = -value.height
|
||||
else
|
||||
@minleft = 0
|
||||
@mintop = 0
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
w = Graphics.width
|
||||
h = Graphics.height
|
||||
if !@sprite && @x>=@minleft && @y>=@mintop && @x<w && @y<h
|
||||
@sprite = Sprite.new(@viewport)
|
||||
elsif @sprite && (@x<@minleft || @y<@mintop || @x>=w || @y>=h)
|
||||
@sprite.dispose
|
||||
@sprite = nil
|
||||
end
|
||||
if @sprite
|
||||
@sprite.x = @x if @sprite.x!=@x
|
||||
@sprite.x -= @ox
|
||||
@sprite.y = @y if @sprite.y!=@y
|
||||
@sprite.y -= @oy
|
||||
@sprite.z = @z if @sprite.z!=@z
|
||||
@sprite.opacity = @opacity if @sprite.opacity!=@opacity
|
||||
@sprite.blend_type = @blend_type if @sprite.blend_type!=@blend_type
|
||||
@sprite.bitmap = @bitmap if @sprite.bitmap!=@bitmap
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class ParticleEffect_Event < ParticleEffect
|
||||
attr_accessor :event
|
||||
|
||||
def initialize(event,viewport=nil)
|
||||
@event = event
|
||||
@viewport = viewport
|
||||
@particles = []
|
||||
@bitmaps = {}
|
||||
end
|
||||
|
||||
def setParameters(params)
|
||||
@randomhue,@leftright,@fade,
|
||||
@maxparticless,@hue,@slowdown,
|
||||
@ytop,@ybottom,@xleft,@xright,
|
||||
@xgravity,@ygravity,@xoffset,@yoffset,
|
||||
@opacityvar,@originalopacity = params
|
||||
end
|
||||
|
||||
def loadBitmap(filename,hue)
|
||||
key = [filename,hue]
|
||||
bitmap = @bitmaps[key]
|
||||
if !bitmap || bitmap.disposed?
|
||||
bitmap = AnimatedBitmap.new("Graphics/Fogs/"+filename,hue).deanimate
|
||||
@bitmaps[key] = bitmap
|
||||
end
|
||||
return bitmap
|
||||
end
|
||||
|
||||
def initParticles(filename,opacity,zOffset=0,blendtype=1)
|
||||
@particles = []
|
||||
@particlex = []
|
||||
@particley = []
|
||||
@opacity = []
|
||||
@startingx = self.x + @xoffset
|
||||
@startingy = self.y + @yoffset
|
||||
@screen_x = self.x
|
||||
@screen_y = self.y
|
||||
@real_x = @event.real_x
|
||||
@real_y = @event.real_y
|
||||
@filename = filename
|
||||
@zoffset = zOffset
|
||||
@bmwidth = 32
|
||||
@bmheight = 32
|
||||
for i in 0...@maxparticless
|
||||
@particlex[i] = -@xoffset
|
||||
@particley[i] = -@yoffset
|
||||
@particles[i] = ParticleSprite.new(@viewport)
|
||||
@particles[i].bitmap = loadBitmap(filename, @hue) if filename
|
||||
if i==0 && @particles[i].bitmap
|
||||
@bmwidth = @particles[i].bitmap.width
|
||||
@bmheight = @particles[i].bitmap.height
|
||||
end
|
||||
@particles[i].blend_type = blendtype
|
||||
@particles[i].y = @startingy
|
||||
@particles[i].x = @startingx
|
||||
@particles[i].z = self.z+zOffset
|
||||
@opacity[i] = rand(opacity/4)
|
||||
@particles[i].opacity = @opacity[i]
|
||||
@particles[i].update
|
||||
end
|
||||
end
|
||||
|
||||
def x; return ScreenPosHelper.pbScreenX(@event); end
|
||||
def y; return ScreenPosHelper.pbScreenY(@event); end
|
||||
def z; return ScreenPosHelper.pbScreenZ(@event); end
|
||||
|
||||
def update
|
||||
if @viewport &&
|
||||
(@viewport.rect.x >= Graphics.width ||
|
||||
@viewport.rect.y >= Graphics.height)
|
||||
return
|
||||
end
|
||||
selfX = self.x
|
||||
selfY = self.y
|
||||
selfZ = self.z
|
||||
newRealX = @event.real_x
|
||||
newRealY = @event.real_y
|
||||
@startingx = selfX + @xoffset
|
||||
@startingy = selfY + @yoffset
|
||||
@__offsetx = (@real_x==newRealX) ? 0 : selfX-@screen_x
|
||||
@__offsety = (@real_y==newRealY) ? 0 : selfY-@screen_y
|
||||
@screen_x = selfX
|
||||
@screen_y = selfY
|
||||
@real_x = newRealX
|
||||
@real_y = newRealY
|
||||
if @opacityvar>0 && @viewport
|
||||
opac = 255.0/@opacityvar
|
||||
minX = opac*(-@xgravity*1.0 / @slowdown).floor + @startingx
|
||||
maxX = opac*(@xgravity*1.0 / @slowdown).floor + @startingx
|
||||
minY = opac*(-@ygravity*1.0 / @slowdown).floor + @startingy
|
||||
maxY = @startingy
|
||||
minX -= @bmwidth
|
||||
minY -= @bmheight
|
||||
maxX += @bmwidth
|
||||
maxY += @bmheight
|
||||
if maxX<0 || maxY<0 || minX>=Graphics.width || minY>=Graphics.height
|
||||
# echo "skipped"
|
||||
return
|
||||
end
|
||||
end
|
||||
particleZ = selfZ+@zoffset
|
||||
for i in 0...@maxparticless
|
||||
@particles[i].z = particleZ
|
||||
if @particles[i].y <= @ytop
|
||||
@particles[i].y = @startingy + @yoffset
|
||||
@particles[i].x = @startingx + @xoffset
|
||||
@particlex[i] = 0.0
|
||||
@particley[i] = 0.0
|
||||
end
|
||||
if @particles[i].x <= @xleft
|
||||
@particles[i].y = @startingy + @yoffset
|
||||
@particles[i].x = @startingx + @xoffset
|
||||
@particlex[i] = 0.0
|
||||
@particley[i] = 0.0
|
||||
end
|
||||
if @particles[i].y >= @ybottom
|
||||
@particles[i].y = @startingy + @yoffset
|
||||
@particles[i].x = @startingx + @xoffset
|
||||
@particlex[i] = 0.0
|
||||
@particley[i] = 0.0
|
||||
end
|
||||
if @particles[i].x >= @xright
|
||||
@particles[i].y = @startingy + @yoffset
|
||||
@particles[i].x = @startingx + @xoffset
|
||||
@particlex[i] = 0.0
|
||||
@particley[i] = 0.0
|
||||
end
|
||||
if @fade == 0
|
||||
if @opacity[i] <= 0
|
||||
@opacity[i] = @originalopacity
|
||||
@particles[i].y = @startingy + @yoffset
|
||||
@particles[i].x = @startingx + @xoffset
|
||||
@particlex[i] = 0.0
|
||||
@particley[i] = 0.0
|
||||
end
|
||||
else
|
||||
if @opacity[i] <= 0
|
||||
@opacity[i] = 250
|
||||
@particles[i].y = @startingy + @yoffset
|
||||
@particles[i].x = @startingx + @xoffset
|
||||
@particlex[i] = 0.0
|
||||
@particley[i] = 0.0
|
||||
end
|
||||
end
|
||||
calcParticlePos(i)
|
||||
if @randomhue == 1
|
||||
@hue += 0.5
|
||||
@hue = 0 if @hue >= 360
|
||||
@particles[i].bitmap = loadBitmap(@filename, @hue) if @filename
|
||||
end
|
||||
@opacity[i] = @opacity[i] - rand(@opacityvar)
|
||||
@particles[i].opacity = @opacity[i]
|
||||
@particles[i].update
|
||||
end
|
||||
end
|
||||
|
||||
def calcParticlePos(i)
|
||||
@leftright = rand(2)
|
||||
if @leftright == 1
|
||||
xo = -@xgravity*1.0 / @slowdown
|
||||
else
|
||||
xo = @xgravity*1.0 / @slowdown
|
||||
end
|
||||
yo = -@ygravity*1.0 / @slowdown
|
||||
@particlex[i] += xo
|
||||
@particley[i] += yo
|
||||
@particlex[i] -= @__offsetx
|
||||
@particley[i] -= @__offsety
|
||||
@particlex[i] = @particlex[i].floor
|
||||
@particley[i] = @particley[i].floor
|
||||
@particles[i].x = @particlex[i]+@startingx+@xoffset
|
||||
@particles[i].y = @particley[i]+@startingy+@yoffset
|
||||
end
|
||||
|
||||
def dispose
|
||||
for particle in @particles
|
||||
particle.dispose
|
||||
end
|
||||
for bitmap in @bitmaps.values
|
||||
bitmap.dispose
|
||||
end
|
||||
@particles.clear
|
||||
@bitmaps.clear
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Fire < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,1,20,40,0.5,-64,
|
||||
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
|
||||
initParticles("particle",250)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Smoke < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,0,80,20,0.5,-64,
|
||||
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
|
||||
initParticles("smoke",250)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Teleport < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([1,1,1,10,rand(360),1,-64,
|
||||
Graphics.height,-64,Graphics.width,0,3,-8,-15,20,0])
|
||||
initParticles("wideportal",250)
|
||||
for i in 0...@maxparticless
|
||||
@particles[i].ox = 16
|
||||
@particles[i].oy = 16
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Spirit < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([1,0,1,20,rand(360),0.5,-64,
|
||||
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
|
||||
initParticles("particle",250)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Explosion < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,1,20,0,0.5,-64,
|
||||
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
|
||||
initParticles("explosion",250)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Aura < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,1,20,0,1,-64,
|
||||
Graphics.height,-64,Graphics.width,2,2,-5,-13,30,0])
|
||||
initParticles("particle",250)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Soot < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,0,20,0,0.5,-64,
|
||||
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
|
||||
initParticles("smoke",100,0,2)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::SootSmoke < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,0,30,0,0.5,-64,
|
||||
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
|
||||
initParticles("smoke",100,0)
|
||||
for i in 0...@maxparticless
|
||||
@particles[i].blend_type = rand(6) < 3 ? 1 : 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Rocket < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,0,60,0,0.5,-64,
|
||||
Graphics.height,-64,Graphics.width,0.5,0,-5,-15,5,80])
|
||||
initParticles("smoke",100,-1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::FixedTeleport < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([1,0,1,10,rand(360),1,
|
||||
-Graphics.height,Graphics.height,0,Graphics.width,0,3,-8,-15,20,0])
|
||||
initParticles("wideportal",250)
|
||||
for i in 0...@maxparticless
|
||||
@particles[i].ox = 16
|
||||
@particles[i].oy = 16
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# By Peter O.
|
||||
class Particle_Engine::StarTeleport < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,1,10,0,1,
|
||||
-Graphics.height,Graphics.height,0,Graphics.width,0,3,-8,-15,10,0])
|
||||
initParticles("star",250)
|
||||
for i in 0...@maxparticless
|
||||
@particles[i].ox = 48
|
||||
@particles[i].oy = 48
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Smokescreen < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,0,250,0,0.2,-64,
|
||||
Graphics.height,-64,Graphics.width,0.8,0.8,-5,-15,5,80])
|
||||
initParticles(nil,100)
|
||||
for i in 0...@maxparticless
|
||||
rnd = rand(3)
|
||||
@opacity[i] = (rnd==0) ? 1 : 100
|
||||
filename = (rnd==0) ? "explosionsmoke" : "smoke"
|
||||
@particles[i].bitmap = loadBitmap(filename, @hue)
|
||||
end
|
||||
end
|
||||
|
||||
def calcParticlePos(i)
|
||||
if @randomhue==1
|
||||
filename = (rand(3)==0) ? "explosionsmoke" : "smoke"
|
||||
@particles[i].bitmap = loadBitmap(filename, @hue)
|
||||
end
|
||||
multiple = 1.7
|
||||
xgrav = @xgravity*multiple/@slowdown
|
||||
xgrav = -xgrav if (rand(2)==1)
|
||||
ygrav = @ygravity*multiple/@slowdown
|
||||
ygrav = -ygrav if (rand(2)==1)
|
||||
@particlex[i] += xgrav
|
||||
@particley[i] += ygrav
|
||||
@particlex[i] -= @__offsetx
|
||||
@particley[i] -= @__offsety
|
||||
@particlex[i] = @particlex[i].floor
|
||||
@particley[i] = @particley[i].floor
|
||||
@particles[i].x = @particlex[i]+@startingx+@xoffset
|
||||
@particles[i].y = @particley[i]+@startingy+@yoffset
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Flare < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,1,30,10,1,-64,
|
||||
Graphics.height,-64,Graphics.width,2,2,-5,-12,30,0])
|
||||
initParticles("particle",255)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Particle_Engine::Splash < ParticleEffect_Event
|
||||
def initialize(event,viewport)
|
||||
super
|
||||
setParameters([0,0,1,30,255,1,-64,
|
||||
Graphics.height,-64,Graphics.width,4,2,-5,-12,30,0])
|
||||
initParticles("smoke",50)
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
for i in 0...@maxparticless
|
||||
@particles[i].opacity = 50
|
||||
@particles[i].update
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Game_Event < Game_Character
|
||||
attr_accessor :pe_refresh
|
||||
|
||||
alias nf_particles_game_map_initialize initialize
|
||||
def initialize(map_id,event,map=nil)
|
||||
@pe_refresh = false
|
||||
begin
|
||||
nf_particles_game_map_initialize(map_id, event, map)
|
||||
rescue ArgumentError
|
||||
nf_particles_game_map_initialize(map_id, event)
|
||||
end
|
||||
end
|
||||
|
||||
alias nf_particles_game_map_refresh refresh
|
||||
def refresh
|
||||
nf_particles_game_map_refresh
|
||||
@pe_refresh = true
|
||||
end
|
||||
end
|
||||
947
Data/Scripts/005_Map renderer/001_Tilemap_XP.rb
Normal file
947
Data/Scripts/005_Map renderer/001_Tilemap_XP.rb
Normal file
@@ -0,0 +1,947 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class CustomTilemapAutotiles
|
||||
attr_accessor :changed
|
||||
|
||||
def initialize
|
||||
@changed = true
|
||||
@tiles = [nil,nil,nil,nil,nil,nil,nil]
|
||||
end
|
||||
|
||||
def [](i)
|
||||
return @tiles[i]
|
||||
end
|
||||
|
||||
def []=(i,value)
|
||||
@tiles[i] = value
|
||||
@changed = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#Console::setup_console
|
||||
class CustomTilemapSprite < Sprite
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class CustomTilemap
|
||||
attr_reader :tileset
|
||||
attr_reader :autotiles
|
||||
attr_reader :map_data
|
||||
attr_reader :flash_data
|
||||
attr_reader :priorities
|
||||
attr_reader :terrain_tags
|
||||
attr_reader :visible
|
||||
attr_reader :viewport
|
||||
attr_reader :graphicsWidth
|
||||
attr_reader :graphicsHeight
|
||||
attr_accessor :ox
|
||||
attr_accessor :oy
|
||||
attr_accessor :tone
|
||||
attr_accessor :color
|
||||
|
||||
Autotiles = [
|
||||
[ [27, 28, 33, 34], [ 5, 28, 33, 34], [27, 6, 33, 34], [ 5, 6, 33, 34],
|
||||
[27, 28, 33, 12], [ 5, 28, 33, 12], [27, 6, 33, 12], [ 5, 6, 33, 12] ],
|
||||
[ [27, 28, 11, 34], [ 5, 28, 11, 34], [27, 6, 11, 34], [ 5, 6, 11, 34],
|
||||
[27, 28, 11, 12], [ 5, 28, 11, 12], [27, 6, 11, 12], [ 5, 6, 11, 12] ],
|
||||
[ [25, 26, 31, 32], [25, 6, 31, 32], [25, 26, 31, 12], [25, 6, 31, 12],
|
||||
[15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
|
||||
[ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
|
||||
[39, 40, 45, 46], [ 5, 40, 45, 46], [39, 6, 45, 46], [ 5, 6, 45, 46] ],
|
||||
[ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
|
||||
[17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
|
||||
[ [37, 38, 43, 44], [37, 6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
|
||||
[37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1, 2, 7, 8] ]
|
||||
]
|
||||
Animated_Autotiles_Frames = 5*Graphics.frame_rate/20 # Frequency of updating animated autotiles
|
||||
FlashOpacity = [100,90,80,70,80,90]
|
||||
|
||||
def initialize(viewport)
|
||||
@tileset = nil # Refers to Map Tileset Name
|
||||
@autotiles = CustomTilemapAutotiles.new
|
||||
@map_data = nil # Refers to 3D Array Of Tile Settings
|
||||
@flash_data = nil # Refers to 3D Array of Tile Flashdata
|
||||
@priorities = nil # Refers to Tileset Priorities
|
||||
@terrain_tags = nil # Refers to Tileset Terrain Tags
|
||||
@visible = true # Refers to Tileset Visibleness
|
||||
@ox = 0 # Bitmap Offsets
|
||||
@oy = 0 # Bitmap Offsets
|
||||
@plane = false
|
||||
@haveGraphicsWH = (Graphics.width!=nil rescue false)
|
||||
if @haveGraphicsWH
|
||||
@graphicsWidth = Graphics.width
|
||||
@graphicsHeight = Graphics.height
|
||||
else
|
||||
@graphicsWidth = 640
|
||||
@graphicsHeight = 480
|
||||
end
|
||||
@tileWidth = Game_Map::TILE_WIDTH rescue 32
|
||||
@tileHeight = Game_Map::TILE_HEIGHT rescue 32
|
||||
@tileSrcWidth = 32
|
||||
@tileSrcHeight = 32
|
||||
@diffsizes = (@tileWidth!=@tileSrcWidth) || (@tileHeight!=@tileSrcHeight)
|
||||
@tone = Tone.new(0,0,0,0)
|
||||
@oldtone = Tone.new(0,0,0,0)
|
||||
@color = Color.new(0,0,0,0)
|
||||
@oldcolor = Color.new(0,0,0,0)
|
||||
@selfviewport = Viewport.new(0,0,graphicsWidth,graphicsHeight)
|
||||
@viewport = (viewport) ? viewport : @selfviewport
|
||||
@tiles = []
|
||||
@autotileInfo = []
|
||||
@regularTileInfo = []
|
||||
@oldOx = 0
|
||||
@oldOy = 0
|
||||
@oldViewportOx = 0
|
||||
@oldViewportOy = 0
|
||||
@layer0 = CustomTilemapSprite.new(viewport)
|
||||
@layer0.visible = true
|
||||
@nowshown = false
|
||||
@layer0.bitmap = Bitmap.new([graphicsWidth+320,1].max,[graphicsHeight+320,1].max)
|
||||
@layer0.z = 0
|
||||
@layer0.ox = 0
|
||||
@layer0.oy = 0
|
||||
@oxLayer0 = 0
|
||||
@oyLayer0 = 0
|
||||
@flash = nil
|
||||
@oxFlash = 0
|
||||
@oyFlash = 0
|
||||
@priotiles = []
|
||||
@priotilesfast = []
|
||||
@prioautotiles = []
|
||||
@autosprites = []
|
||||
@framecount = [0,0,0,0,0,0,0,0] # For autotiles
|
||||
@tilesetChanged = true
|
||||
@flashChanged = false
|
||||
@firsttime = true
|
||||
@disposed = false
|
||||
@usedsprites = false
|
||||
@layer0clip = true
|
||||
@firsttimeflash = true
|
||||
@fullyrefreshed = false
|
||||
@fullyrefreshedautos = false
|
||||
end
|
||||
|
||||
def dispose
|
||||
return if disposed?
|
||||
@help.dispose if @help
|
||||
@help = nil
|
||||
i = 0; len = @autotileInfo.length; while i<len
|
||||
if @autotileInfo[i]
|
||||
@autotileInfo[i].dispose
|
||||
@autotileInfo[i] = nil
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
i = 0; len = @regularTileInfo.length; while i<len
|
||||
if @regularTileInfo[i]
|
||||
@regularTileInfo[i].dispose
|
||||
@regularTileInfo[i] = nil
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
i = 0; len = @tiles.length; while i<len
|
||||
@tiles[i].dispose
|
||||
@tiles[i] = nil
|
||||
i += 2
|
||||
end
|
||||
i = 0; len = @autosprites.length; while i<len
|
||||
@autosprites[i].dispose
|
||||
@autosprites[i] = nil
|
||||
i += 2
|
||||
end
|
||||
if @layer0
|
||||
@layer0.bitmap.dispose if !@layer0.disposed?
|
||||
@layer0.bitmap = nil if !@layer0.disposed?
|
||||
@layer0.dispose
|
||||
@layer0 = nil
|
||||
end
|
||||
if @flash
|
||||
@flash.bitmap.dispose if !@flash.disposed?
|
||||
@flash.bitmap = nil if !@flash.disposed?
|
||||
@flash.dispose
|
||||
@flash = nil
|
||||
end
|
||||
for i in 0...7
|
||||
self.autotiles[i] = nil
|
||||
end
|
||||
@tiles.clear
|
||||
@autosprites.clear
|
||||
@autotileInfo.clear
|
||||
@regularTileInfo.clear
|
||||
@tilemap = nil
|
||||
@tileset = nil
|
||||
@priorities = nil
|
||||
@selfviewport.dispose
|
||||
@selfviewport = nil
|
||||
@disposed = true
|
||||
end
|
||||
|
||||
def disposed?
|
||||
return @disposed
|
||||
end
|
||||
|
||||
def flash_data=(value)
|
||||
@flash_data = value
|
||||
@flashChanged = true
|
||||
end
|
||||
|
||||
def map_data=(value)
|
||||
@map_data = value
|
||||
@tilesetChanged = true
|
||||
end
|
||||
|
||||
def priorities=(value)
|
||||
@priorities = value
|
||||
@tilesetChanged = true
|
||||
end
|
||||
|
||||
def terrain_tags=(value)
|
||||
@terrain_tags = value
|
||||
@tilesetChanged = true
|
||||
end
|
||||
|
||||
def tileset=(value)
|
||||
@tileset = value
|
||||
@tilesetChanged = true
|
||||
end
|
||||
|
||||
def getResizeFactor
|
||||
return $ResizeFactor || 1.0
|
||||
end
|
||||
|
||||
def ox=(val)
|
||||
rf = getResizeFactor
|
||||
if rf!=1.0
|
||||
val = (val*rf).to_i
|
||||
val = (val/rf).to_i
|
||||
end
|
||||
wasshown = self.shown?
|
||||
@ox = val.floor
|
||||
@nowshown = (!wasshown && self.shown?)
|
||||
end
|
||||
|
||||
def oy=(val)
|
||||
rf = getResizeFactor
|
||||
if rf!=1.0
|
||||
val = (val*rf).to_i
|
||||
val = (val/rf).to_i
|
||||
end
|
||||
wasshown = self.shown?
|
||||
@oy = val.floor
|
||||
@nowshown = (!wasshown && self.shown?)
|
||||
end
|
||||
|
||||
def visible=(val)
|
||||
wasshown = @visible
|
||||
@visible = val
|
||||
@nowshown = (!wasshown && val)
|
||||
end
|
||||
|
||||
def shown?
|
||||
return false if !@visible
|
||||
xsize = @map_data.xsize
|
||||
xStart = @ox/@tileWidth - 1
|
||||
xStart = 0 if xStart<0
|
||||
xStart = xsize-1 if xStart>=xsize
|
||||
xEnd = (@ox+@viewport.rect.width)/@tileWidth + 1
|
||||
xEnd = 0 if xEnd<0
|
||||
xEnd = xsize-1 if xEnd>=xsize
|
||||
return false if xStart>=xEnd
|
||||
ysize = @map_data.ysize
|
||||
yStart = @oy/@tileHeight - 1
|
||||
yStart = 0 if yStart<0
|
||||
yStart = ysize-1 if yStart>=ysize
|
||||
yEnd = (@oy+@viewport.rect.height)/@tileHeight + 1
|
||||
yEnd = 0 if yEnd<0
|
||||
yEnd = ysize-1 if yEnd>=ysize
|
||||
return false if yStart>=yEnd
|
||||
return true
|
||||
end
|
||||
|
||||
def autotileNumFrames(id)
|
||||
autotile = @autotiles[id/48-1]
|
||||
return 0 if !autotile || autotile.disposed?
|
||||
frames = 1
|
||||
if autotile.height==@tileHeight
|
||||
frames = autotile.width/@tileWidth
|
||||
else
|
||||
frames = autotile.width/(3*@tileWidth)
|
||||
end
|
||||
return frames
|
||||
end
|
||||
|
||||
def autotileFrame(id)
|
||||
autotile = @autotiles[id/48-1]
|
||||
return -1 if !autotile || autotile.disposed?
|
||||
frames = 1
|
||||
if autotile.height==@tileHeight
|
||||
frames = autotile.width/@tileWidth
|
||||
else
|
||||
frames = autotile.width/(3*@tileWidth)
|
||||
end
|
||||
return (Graphics.frame_count/Animated_Autotiles_Frames)%frames
|
||||
end
|
||||
|
||||
def repaintAutotiles
|
||||
for i in 0...@autotileInfo.length
|
||||
next if !@autotileInfo[i]
|
||||
frame = autotileFrame(i)
|
||||
@autotileInfo[i].clear
|
||||
bltAutotile(@autotileInfo[i],0,0,i,frame)
|
||||
end
|
||||
end
|
||||
|
||||
def bltAutotile(bitmap,x,y,id,frame)
|
||||
return if frame<0
|
||||
autotile = @autotiles[id/48-1]
|
||||
return if !autotile || autotile.disposed?
|
||||
if autotile.height==@tileSrcHeight
|
||||
anim = frame*@tileSrcWidth
|
||||
src_rect = Rect.new(anim,0,@tileSrcWidth,@tileSrcHeight)
|
||||
if @diffsizes
|
||||
bitmap.stretch_blt(Rect.new(x,y,@tileWidth,@tileHeight),autotile,src_rect)
|
||||
else
|
||||
bitmap.blt(x,y,autotile,src_rect)
|
||||
end
|
||||
else
|
||||
anim = frame*3*@tileSrcWidth
|
||||
id %= 48
|
||||
tiles = Autotiles[id>>3][id&7]
|
||||
src = Rect.new(0,0,0,0)
|
||||
halfTileWidth = @tileWidth>>1
|
||||
halfTileHeight = @tileHeight>>1
|
||||
halfTileSrcWidth = @tileSrcWidth>>1
|
||||
halfTileSrcHeight = @tileSrcHeight>>1
|
||||
for i in 0...4
|
||||
tile_position = tiles[i] - 1
|
||||
src.set( (tile_position % 6)*halfTileSrcWidth + anim,
|
||||
(tile_position / 6)*halfTileSrcHeight, halfTileSrcWidth, halfTileSrcHeight)
|
||||
if @diffsizes
|
||||
bitmap.stretch_blt(
|
||||
Rect.new(i%2*halfTileWidth+x,i/2*halfTileHeight+y,halfTileWidth,halfTileHeight),
|
||||
autotile,src)
|
||||
else
|
||||
bitmap.blt(i%2*halfTileWidth+x,i/2*halfTileHeight+y, autotile, src)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def getAutotile(sprite,id)
|
||||
frames = @framecount[id/48-1]
|
||||
if frames<=1
|
||||
anim = 0
|
||||
else
|
||||
anim = (Graphics.frame_count/Animated_Autotiles_Frames)%frames
|
||||
end
|
||||
return if anim<0
|
||||
bitmap = @autotileInfo[id]
|
||||
if !bitmap
|
||||
bitmap = Bitmap.new(@tileWidth,@tileHeight)
|
||||
bltAutotile(bitmap,0,0,id,anim)
|
||||
@autotileInfo[id] = bitmap
|
||||
end
|
||||
sprite.bitmap = bitmap if sprite.bitmap!=bitmap
|
||||
end
|
||||
|
||||
def getRegularTile(sprite,id)
|
||||
if @diffsizes
|
||||
bitmap = @regularTileInfo[id]
|
||||
if !bitmap
|
||||
bitmap = Bitmap.new(@tileWidth,@tileHeight)
|
||||
rect = Rect.new(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
|
||||
@tileSrcWidth,@tileSrcHeight)
|
||||
bitmap.stretch_blt(Rect.new(0,0,@tileWidth,@tileHeight),@tileset,rect)
|
||||
@regularTileInfo[id] = bitmap
|
||||
end
|
||||
sprite.bitmap = bitmap if sprite.bitmap!=bitmap
|
||||
else
|
||||
sprite.bitmap = @tileset if sprite.bitmap!=@tileset
|
||||
sprite.src_rect.set(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
|
||||
@tileSrcWidth,@tileSrcHeight)
|
||||
end
|
||||
end
|
||||
|
||||
def addTile(tiles,count,xpos,ypos,id)
|
||||
terrain = @terrain_tags[id]
|
||||
priority = @priorities[id]
|
||||
if id>=384
|
||||
if count>=tiles.length
|
||||
sprite = CustomTilemapSprite.new(@viewport)
|
||||
tiles.push(sprite,0)
|
||||
else
|
||||
sprite = tiles[count]
|
||||
tiles[count+1] = 0
|
||||
end
|
||||
sprite.visible = @visible
|
||||
sprite.x = xpos
|
||||
sprite.y = ypos
|
||||
sprite.tone = @tone
|
||||
sprite.color = @color
|
||||
getRegularTile(sprite,id)
|
||||
else
|
||||
if count>=tiles.length
|
||||
sprite = CustomTilemapSprite.new(@viewport)
|
||||
tiles.push(sprite,1)
|
||||
else
|
||||
sprite = tiles[count]
|
||||
tiles[count+1] = 1
|
||||
end
|
||||
sprite.visible = @visible
|
||||
sprite.x = xpos
|
||||
sprite.y = ypos
|
||||
sprite.tone = @tone
|
||||
sprite.color = @color
|
||||
getAutotile(sprite,id)
|
||||
end
|
||||
if PBTerrain.hasReflections?(terrain)
|
||||
spriteZ = -100
|
||||
elsif $PokemonGlobal.bridge>0 && PBTerrain.isBridge?(terrain)
|
||||
spriteZ = 1
|
||||
else
|
||||
spriteZ = (priority==0) ? 0 : ypos+priority*32+32
|
||||
end
|
||||
sprite.z = spriteZ
|
||||
count += 2
|
||||
return count
|
||||
end
|
||||
|
||||
def refresh_flash
|
||||
if @flash_data && !@flash
|
||||
@flash = CustomTilemapSprite.new(viewport)
|
||||
@flash.visible = true
|
||||
@flash.z = 1
|
||||
@flash.tone = tone
|
||||
@flash.color = color
|
||||
@flash.blend_type = 1
|
||||
@flash.bitmap = Bitmap.new([graphicsWidth*2,1].max,[graphicsHeight*2,1].max)
|
||||
@firsttimeflash = true
|
||||
elsif !@flash_data && @flash
|
||||
@flash.bitmap.dispose if @flash.bitmap
|
||||
@flash.dispose
|
||||
@flash = nil
|
||||
@firsttimeflash = false
|
||||
end
|
||||
end
|
||||
|
||||
def refreshFlashSprite
|
||||
return if !@flash || @flash_data.nil?
|
||||
ptX = @ox-@oxFlash
|
||||
ptY = @oy-@oyFlash
|
||||
if !@firsttimeflash && !@usedsprites &&
|
||||
ptX>=0 && ptX+@viewport.rect.width<=@flash.bitmap.width &&
|
||||
ptY>=0 && ptY+@viewport.rect.height<=@flash.bitmap.height
|
||||
@flash.ox = 0
|
||||
@flash.oy = 0
|
||||
@flash.src_rect.set(ptX.round,ptY.round,
|
||||
@viewport.rect.width,@viewport.rect.height)
|
||||
return
|
||||
end
|
||||
width = @flash.bitmap.width
|
||||
height = @flash.bitmap.height
|
||||
bitmap = @flash.bitmap
|
||||
ysize = @map_data.ysize
|
||||
xsize = @map_data.xsize
|
||||
zsize = @map_data.zsize
|
||||
@firsttimeflash = false
|
||||
@oxFlash = @ox-(width>>2)
|
||||
@oyFlash = @oy-(height>>2)
|
||||
@flash.ox = 0
|
||||
@flash.oy = 0
|
||||
@flash.src_rect.set(width>>2,height>>2,
|
||||
@viewport.rect.width,@viewport.rect.height)
|
||||
@flash.bitmap.clear
|
||||
@oxFlash = @oxFlash.floor
|
||||
@oyFlash = @oyFlash.floor
|
||||
xStart = @oxFlash/@tileWidth
|
||||
xStart = 0 if xStart<0
|
||||
yStart = @oyFlash/@tileHeight
|
||||
yStart = 0 if yStart<0
|
||||
xEnd = xStart+(width/@tileWidth)+1
|
||||
yEnd = yStart+(height/@tileHeight)+1
|
||||
xEnd = xsize if xEnd>=xsize
|
||||
yEnd = ysize if yEnd>=ysize
|
||||
if xStart<xEnd && yStart<yEnd
|
||||
yrange = yStart...yEnd
|
||||
xrange = xStart...xEnd
|
||||
tmpcolor = Color.new(0,0,0,0)
|
||||
for y in yrange
|
||||
ypos = (y*@tileHeight)-@oyFlash
|
||||
for x in xrange
|
||||
xpos = (x*@tileWidth)-@oxFlash
|
||||
id = @flash_data[x, y, 0]
|
||||
r = (id>>8)&15
|
||||
g = (id>>4)&15
|
||||
b = (id)&15
|
||||
tmpcolor.set(r<<4,g<<4,b<<4)
|
||||
bitmap.fill_rect(xpos,ypos,@tileWidth,@tileHeight,tmpcolor)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_tileset
|
||||
i = 0; len = @regularTileInfo.length; while i<len
|
||||
if @regularTileInfo[i]
|
||||
@regularTileInfo[i].dispose
|
||||
@regularTileInfo[i] = nil
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
@regularTileInfo.clear
|
||||
@priotiles.clear
|
||||
ysize = @map_data.ysize
|
||||
xsize = @map_data.xsize
|
||||
zsize = @map_data.zsize
|
||||
if xsize>100 || ysize>100
|
||||
@fullyrefreshed = false
|
||||
else
|
||||
for z in 0...zsize
|
||||
for y in 0...ysize
|
||||
for x in 0...xsize
|
||||
id = @map_data[x, y, z]
|
||||
next if id==0
|
||||
next if @priorities[id]==0 && !PBTerrain.hasReflections?(@terrain_tags[id])
|
||||
@priotiles.push([x,y,z,id])
|
||||
end
|
||||
end
|
||||
end
|
||||
@fullyrefreshed = true
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_autotiles
|
||||
i = 0; len = @autotileInfo.length; while i<len
|
||||
if @autotileInfo[i]
|
||||
@autotileInfo[i].dispose
|
||||
@autotileInfo[i] = nil
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
i = 0; len = @autosprites.length; while i<len
|
||||
if @autosprites[i]
|
||||
@autosprites[i].dispose
|
||||
@autosprites[i] = nil
|
||||
end
|
||||
i += 2
|
||||
end
|
||||
@autosprites.clear
|
||||
@autotileInfo.clear
|
||||
@prioautotiles.clear
|
||||
@priorect = nil
|
||||
@priorectautos = nil
|
||||
hasanimated = false
|
||||
for i in 0...7
|
||||
numframes = autotileNumFrames(48*(i+1))
|
||||
hasanimated = true if numframes>=2
|
||||
@framecount[i] = numframes
|
||||
end
|
||||
if hasanimated
|
||||
ysize = @map_data.ysize
|
||||
xsize = @map_data.xsize
|
||||
zsize = @map_data.zsize
|
||||
if xsize>100 || ysize>100
|
||||
@fullyrefreshedautos = false
|
||||
else
|
||||
for y in 0...ysize
|
||||
for x in 0...xsize
|
||||
haveautotile = false
|
||||
for z in 0...zsize
|
||||
id = @map_data[x, y, z]
|
||||
next if id==0 || id>=384
|
||||
next if @priorities[id]!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
|
||||
next if @framecount[id/48-1]<2
|
||||
haveautotile = true
|
||||
break
|
||||
end
|
||||
@prioautotiles.push([x,y]) if haveautotile
|
||||
end
|
||||
end
|
||||
@fullyrefreshedautos = true
|
||||
end
|
||||
else
|
||||
@fullyrefreshedautos = true
|
||||
end
|
||||
end
|
||||
|
||||
def refreshLayer0(autotiles=false)
|
||||
return true if autotiles && !shown?
|
||||
ptX = @ox-@oxLayer0
|
||||
ptY = @oy-@oyLayer0
|
||||
if !autotiles && !@firsttime && !@usedsprites &&
|
||||
ptX>=0 && ptX+@viewport.rect.width<=@layer0.bitmap.width &&
|
||||
ptY>=0 && ptY+@viewport.rect.height<=@layer0.bitmap.height
|
||||
if @layer0clip && @viewport.ox==0 && @viewport.oy==0
|
||||
@layer0.ox = 0
|
||||
@layer0.oy = 0
|
||||
@layer0.src_rect.set(ptX.round,ptY.round,
|
||||
@viewport.rect.width,@viewport.rect.height)
|
||||
else
|
||||
@layer0.ox = ptX.round
|
||||
@layer0.oy = ptY.round
|
||||
@layer0.src_rect.set(0,0,@layer0.bitmap.width,@layer0.bitmap.height)
|
||||
end
|
||||
return true
|
||||
end
|
||||
width = @layer0.bitmap.width
|
||||
height = @layer0.bitmap.height
|
||||
bitmap = @layer0.bitmap
|
||||
ysize = @map_data.ysize
|
||||
xsize = @map_data.xsize
|
||||
zsize = @map_data.zsize
|
||||
twidth = @tileWidth
|
||||
theight = @tileHeight
|
||||
mapdata = @map_data
|
||||
if autotiles
|
||||
return true if @fullyrefreshedautos && @prioautotiles.length==0
|
||||
xStart = @oxLayer0/twidth
|
||||
xStart = 0 if xStart<0
|
||||
yStart = @oyLayer0/theight
|
||||
yStart = 0 if yStart<0
|
||||
xEnd = xStart+(width/twidth)+1
|
||||
yEnd = yStart+(height/theight)+1
|
||||
xEnd = xsize if xEnd>xsize
|
||||
yEnd = ysize if yEnd>ysize
|
||||
return true if xStart>=xEnd || yStart>=yEnd
|
||||
trans = Color.new(0,0,0,0)
|
||||
temprect = Rect.new(0,0,0,0)
|
||||
tilerect = Rect.new(0,0,twidth,theight)
|
||||
zrange = 0...zsize
|
||||
overallcount = 0
|
||||
count = 0
|
||||
if !@fullyrefreshedautos
|
||||
for y in yStart..yEnd
|
||||
for x in xStart..xEnd
|
||||
haveautotile = false
|
||||
for z in zrange
|
||||
id = mapdata[x, y, z]
|
||||
next if !id || id<48 || id>=384
|
||||
prioid = @priorities[id]
|
||||
next if prioid!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
|
||||
fcount = @framecount[id/48-1]
|
||||
next if !fcount || fcount<2
|
||||
if !haveautotile
|
||||
haveautotile = true
|
||||
overallcount += 1
|
||||
xpos = (x*twidth)-@oxLayer0
|
||||
ypos = (y*theight)-@oyLayer0
|
||||
bitmap.fill_rect(xpos,ypos,twidth,theight,trans) if overallcount<=2000
|
||||
break
|
||||
end
|
||||
end
|
||||
for z in zrange
|
||||
id = mapdata[x,y,z]
|
||||
next if !id || id<48
|
||||
prioid = @priorities[id]
|
||||
next if prioid!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
|
||||
if overallcount>2000
|
||||
xpos = (x*twidth)-@oxLayer0
|
||||
ypos = (y*theight)-@oyLayer0
|
||||
count = addTile(@autosprites,count,xpos,ypos,id)
|
||||
next
|
||||
elsif id>=384
|
||||
temprect.set(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
|
||||
@tileSrcWidth,@tileSrcHeight)
|
||||
xpos = (x*twidth)-@oxLayer0
|
||||
ypos = (y*theight)-@oyLayer0
|
||||
if @diffsizes
|
||||
bitmap.stretch_blt(Rect.new(xpos,ypos,twidth,theight),@tileset,temprect)
|
||||
else
|
||||
bitmap.blt(xpos,ypos,@tileset,temprect)
|
||||
end
|
||||
else
|
||||
tilebitmap = @autotileInfo[id]
|
||||
if !tilebitmap
|
||||
anim = autotileFrame(id)
|
||||
next if anim<0
|
||||
tilebitmap = Bitmap.new(twidth,theight)
|
||||
bltAutotile(tilebitmap,0,0,id,anim)
|
||||
@autotileInfo[id] = tilebitmap
|
||||
end
|
||||
xpos = (x*twidth)-@oxLayer0
|
||||
ypos = (y*theight)-@oyLayer0
|
||||
bitmap.blt(xpos,ypos,tilebitmap,tilerect)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Graphics.frame_reset
|
||||
else
|
||||
if !@priorect || !@priorectautos ||
|
||||
@priorect[0]!=xStart || @priorect[1]!=yStart ||
|
||||
@priorect[2]!=xEnd || @priorect[3]!=yEnd
|
||||
@priorectautos = @prioautotiles.find_all { |tile|
|
||||
x = tile[0]
|
||||
y = tile[1]
|
||||
# "next" means "return" here
|
||||
next !(x<xStart || x>xEnd || y<yStart || y>yEnd)
|
||||
}
|
||||
@priorect = [xStart,yStart,xEnd,yEnd]
|
||||
end
|
||||
# echoln ["autos",@priorect,@priorectautos.length,@prioautotiles.length]
|
||||
for tile in @priorectautos
|
||||
x = tile[0]
|
||||
y = tile[1]
|
||||
overallcount+=1
|
||||
xpos = (x*twidth)-@oxLayer0
|
||||
ypos = (y*theight)-@oyLayer0
|
||||
bitmap.fill_rect(xpos,ypos,twidth,theight,trans)
|
||||
z = 0
|
||||
while z<zsize
|
||||
id = mapdata[x,y,z]
|
||||
z += 1
|
||||
next if !id || id<48
|
||||
prioid = @priorities[id]
|
||||
next if prioid!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
|
||||
if id>=384
|
||||
temprect.set(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
|
||||
@tileSrcWidth,@tileSrcHeight)
|
||||
if @diffsizes
|
||||
bitmap.stretch_blt(Rect.new(xpos,ypos,twidth,theight),@tileset,temprect)
|
||||
else
|
||||
bitmap.blt(xpos,ypos,@tileset,temprect)
|
||||
end
|
||||
else
|
||||
tilebitmap = @autotileInfo[id]
|
||||
if !tilebitmap
|
||||
anim = autotileFrame(id)
|
||||
next if anim<0
|
||||
tilebitmap = Bitmap.new(twidth,theight)
|
||||
bltAutotile(tilebitmap,0,0,id,anim)
|
||||
@autotileInfo[id] = tilebitmap
|
||||
end
|
||||
bitmap.blt(xpos,ypos,tilebitmap,tilerect)
|
||||
end
|
||||
end
|
||||
end
|
||||
Graphics.frame_reset if overallcount>500
|
||||
end
|
||||
@usedsprites = false
|
||||
return true
|
||||
end
|
||||
return false if @usedsprites
|
||||
@firsttime = false
|
||||
@oxLayer0 = @ox-(width>>2)
|
||||
@oyLayer0 = @oy-(height>>2)
|
||||
if @layer0clip
|
||||
@layer0.ox = 0
|
||||
@layer0.oy = 0
|
||||
@layer0.src_rect.set(width>>2,height>>2,
|
||||
@viewport.rect.width,@viewport.rect.height)
|
||||
else
|
||||
@layer0.ox = (width>>2)
|
||||
@layer0.oy = (height>>2)
|
||||
end
|
||||
@layer0.bitmap.clear
|
||||
@oxLayer0 = @oxLayer0.round
|
||||
@oyLayer0 = @oyLayer0.round
|
||||
xStart = @oxLayer0/twidth
|
||||
xStart = 0 if xStart<0
|
||||
yStart = @oyLayer0/theight
|
||||
yStart = 0 if yStart<0
|
||||
xEnd = xStart+(width/twidth)+1
|
||||
yEnd = yStart+(height/theight)+1
|
||||
xEnd = xsize if xEnd>=xsize
|
||||
yEnd = ysize if yEnd>=ysize
|
||||
if xStart<xEnd && yStart<yEnd
|
||||
tmprect = Rect.new(0,0,0,0)
|
||||
yrange = yStart...yEnd
|
||||
xrange = xStart...xEnd
|
||||
for z in 0...zsize
|
||||
for y in yrange
|
||||
ypos = (y*theight)-@oyLayer0
|
||||
for x in xrange
|
||||
xpos = (x*twidth)-@oxLayer0
|
||||
id = mapdata[x, y, z]
|
||||
next if id==0 || @priorities[id]!=0 || PBTerrain.hasReflections?(@terrain_tags[id])
|
||||
if id>=384
|
||||
tmprect.set( ((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight,
|
||||
@tileSrcWidth,@tileSrcHeight)
|
||||
if @diffsizes
|
||||
bitmap.stretch_blt(Rect.new(xpos,ypos,twidth,theight),@tileset,tmprect)
|
||||
else
|
||||
bitmap.blt(xpos,ypos,@tileset,tmprect)
|
||||
end
|
||||
else
|
||||
frames = @framecount[id/48-1]
|
||||
if frames<=1
|
||||
frame = 0
|
||||
else
|
||||
frame = (Graphics.frame_count/Animated_Autotiles_Frames)%frames
|
||||
end
|
||||
bltAutotile(bitmap,xpos,ypos,id,frame)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Graphics.frame_reset
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def refresh(autotiles=false)
|
||||
@oldOx = @ox
|
||||
@oldOy = @oy
|
||||
usesprites = false
|
||||
if @layer0
|
||||
@layer0.visible = @visible
|
||||
usesprites = !refreshLayer0(autotiles)
|
||||
return if autotiles && !usesprites
|
||||
else
|
||||
usesprites = true
|
||||
end
|
||||
refreshFlashSprite
|
||||
vpx = @viewport.rect.x
|
||||
vpy = @viewport.rect.y
|
||||
vpr = @viewport.rect.width+vpx
|
||||
vpb = @viewport.rect.height+vpy
|
||||
xsize = @map_data.xsize
|
||||
ysize = @map_data.ysize
|
||||
minX = (@ox/@tileWidth)-1
|
||||
minX = 0 if minX<0
|
||||
minX = xsize-1 if minX>=xsize
|
||||
maxX = ((@ox+@viewport.rect.width)/@tileWidth)+1
|
||||
maxX = 0 if maxX<0
|
||||
maxX = xsize-1 if maxX>=xsize
|
||||
minY = (@oy/@tileHeight)-1
|
||||
minY = 0 if minY<0
|
||||
minY = ysize-1 if minY>=ysize
|
||||
maxY = ((@oy+@viewport.rect.height)/@tileHeight)+1
|
||||
maxY = 0 if maxY<0
|
||||
maxY = ysize-1 if maxY>=ysize
|
||||
count = 0
|
||||
if minX<maxX && minY<maxY
|
||||
@usedsprites = usesprites || @usedsprites
|
||||
if @layer0
|
||||
@layer0.visible = false if usesprites
|
||||
end
|
||||
if @fullyrefreshed
|
||||
if !@priotilesrect || !@priotilesfast ||
|
||||
@priotilesrect[0]!=minX ||
|
||||
@priotilesrect[1]!=minY ||
|
||||
@priotilesrect[2]!=maxX ||
|
||||
@priotilesrect[3]!=maxY
|
||||
@priotilesfast = @priotiles.find_all { |tile|
|
||||
x = tile[0]
|
||||
y = tile[1]
|
||||
# "next" means "return" here
|
||||
next !(x<minX || x>maxX || y<minY || y>maxY)
|
||||
}
|
||||
@priotilesrect = [minX,minY,maxX,maxY]
|
||||
end
|
||||
# echoln [minX,minY,maxX,maxY,@priotilesfast.length,@priotiles.length]
|
||||
for prio in @priotilesfast
|
||||
xpos = (prio[0]*@tileWidth)-@ox
|
||||
ypos = (prio[1]*@tileHeight)-@oy
|
||||
count = addTile(@tiles,count,xpos,ypos,prio[3])
|
||||
end
|
||||
else
|
||||
if !@priotilesrect || !@priotilesfast ||
|
||||
@priotilesrect[0]!=minX ||
|
||||
@priotilesrect[1]!=minY ||
|
||||
@priotilesrect[2]!=maxX ||
|
||||
@priotilesrect[3]!=maxY
|
||||
@priotilesfast=[]
|
||||
for z in 0...@map_data.zsize
|
||||
for y in minY..maxY
|
||||
for x in minX..maxX
|
||||
id = @map_data[x, y, z]
|
||||
next if id==0
|
||||
next if @priorities[id]==0 && !PBTerrain.hasReflections?(@terrain_tags[id])
|
||||
@priotilesfast.push([x,y,z,id])
|
||||
end
|
||||
end
|
||||
end
|
||||
@priotilesrect = [minX,minY,maxX,maxY]
|
||||
end
|
||||
for prio in @priotilesfast
|
||||
xpos = (prio[0]*@tileWidth)-@ox
|
||||
ypos = (prio[1]*@tileHeight)-@oy
|
||||
count = addTile(@tiles,count,xpos,ypos,prio[3])
|
||||
end
|
||||
end
|
||||
end
|
||||
if count<@tiles.length
|
||||
bigchange = (count<=(@tiles.length*2/3)) && (@tiles.length*2/3)>25
|
||||
j = count; len = @tiles.length; while j<len
|
||||
sprite = @tiles[j]
|
||||
@tiles[j+1] = -1
|
||||
if bigchange
|
||||
sprite.dispose
|
||||
@tiles[j] = nil
|
||||
@tiles[j+1] = nil
|
||||
elsif !@tiles[j].disposed?
|
||||
sprite.visible = false if sprite.visible
|
||||
end
|
||||
j += 2
|
||||
end
|
||||
@tiles.compact! if bigchange
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @haveGraphicsWH
|
||||
@graphicsWidth = Graphics.width
|
||||
@graphicsHeight = Graphics.height
|
||||
end
|
||||
# Update tone
|
||||
if @oldtone!=@tone
|
||||
@layer0.tone = @tone
|
||||
@flash.tone = @tone if @flash
|
||||
for sprite in @autosprites
|
||||
sprite.tone = @tone if sprite.is_a?(Sprite)
|
||||
end
|
||||
for sprite in @tiles
|
||||
sprite.tone = @tone if sprite.is_a?(Sprite)
|
||||
end
|
||||
@oldtone = @tone.clone
|
||||
end
|
||||
# Update color
|
||||
if @oldcolor!=@color
|
||||
@layer0.color = @color
|
||||
@flash.color = @color if @flash
|
||||
for sprite in @autosprites
|
||||
sprite.color = @color if sprite.is_a?(Sprite)
|
||||
end
|
||||
for sprite in @tiles
|
||||
sprite.color = @color if sprite.is_a?(Sprite)
|
||||
end
|
||||
@oldcolor = @color.clone
|
||||
end
|
||||
# Refresh anything that has changed
|
||||
if @autotiles.changed
|
||||
refresh_autotiles
|
||||
repaintAutotiles
|
||||
end
|
||||
if @flashChanged
|
||||
refresh_flash
|
||||
end
|
||||
if @tilesetChanged
|
||||
refresh_tileset
|
||||
end
|
||||
if @flash
|
||||
@flash.opacity = FlashOpacity[(Graphics.frame_count/2) % 6]
|
||||
end
|
||||
mustrefresh = (@oldOx!=@ox || @oldOy!=@oy || @tilesetChanged || @autotiles.changed)
|
||||
if @viewport.ox!=@oldViewportOx || @viewport.oy!=@oldViewportOy
|
||||
mustrefresh = true
|
||||
@oldViewportOx = @viewport.ox
|
||||
@oldViewportOy = @viewport.oy
|
||||
end
|
||||
refresh if mustrefresh
|
||||
if (Graphics.frame_count % Animated_Autotiles_Frames == 0) || @nowshown
|
||||
repaintAutotiles
|
||||
refresh(true)
|
||||
end
|
||||
@nowshown = false
|
||||
@autotiles.changed = false
|
||||
@tilesetChanged = false
|
||||
end
|
||||
end
|
||||
447
Data/Scripts/005_Map renderer/002_Tilemap_Perspective.rb
Normal file
447
Data/Scripts/005_Map renderer/002_Tilemap_Perspective.rb
Normal file
@@ -0,0 +1,447 @@
|
||||
def bltMinimapAutotile(dstBitmap,x,y,srcBitmap,id)
|
||||
return if id>=48 || !srcBitmap || srcBitmap.disposed?
|
||||
anim=0
|
||||
cxTile=3
|
||||
cyTile=3
|
||||
tiles = TileDrawingHelper::Autotiles[id>>3][id&7]
|
||||
src=Rect.new(0,0,0,0)
|
||||
for i in 0...4
|
||||
tile_position = tiles[i] - 1
|
||||
src.set(
|
||||
tile_position % 6 * cxTile + anim,
|
||||
tile_position / 6 * cyTile, cxTile, cyTile)
|
||||
dstBitmap.blt(i%2*cxTile+x,i/2*cyTile+y, srcBitmap, src)
|
||||
end
|
||||
end
|
||||
|
||||
def passable?(passages,tile_id)
|
||||
return false if tile_id == nil
|
||||
passage = passages[tile_id]
|
||||
return (passage && passage<15)
|
||||
end
|
||||
|
||||
def getPassabilityMinimap(mapid)
|
||||
map = load_data(sprintf("Data/Map%03d.rxdata",mapid))
|
||||
tileset = $data_tilesets[map.tileset_id]
|
||||
minimap = AnimatedBitmap.new("Graphics/Pictures/minimap_tiles")
|
||||
ret = Bitmap.new(map.width*6,map.height*6)
|
||||
passtable = Table.new(map.width,map.height)
|
||||
passages = tileset.passages
|
||||
for i in 0...map.width
|
||||
for j in 0...map.height
|
||||
pass=true
|
||||
for z in [2,1,0]
|
||||
if !passable?(passages,map.data[i,j,z])
|
||||
pass=false
|
||||
break
|
||||
end
|
||||
end
|
||||
passtable[i,j]=pass ? 1 : 0
|
||||
end
|
||||
end
|
||||
neighbors=TileDrawingHelper::NeighborsToTiles
|
||||
for i in 0...map.width
|
||||
for j in 0...map.height
|
||||
if passtable[i,j]==0
|
||||
nb=TileDrawingHelper.tableNeighbors(passtable,i,j)
|
||||
tile=neighbors[nb]
|
||||
bltMinimapAutotile(ret,i*6,j*6,minimap.bitmap,tile)
|
||||
end
|
||||
end
|
||||
end
|
||||
minimap.disposes
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
|
||||
module ScreenPosHelper
|
||||
def self.pbScreenZoomX(ch)
|
||||
zoom=1.0
|
||||
if $PokemonSystem.tilemap==2
|
||||
zoom=((ch.screen_y - 16) - (Graphics.height / 2)) *
|
||||
(Draw_Tilemap::Pitch*1.0 / (Graphics.height * 25)) + 1
|
||||
end
|
||||
return zoom*Game_Map::TILE_WIDTH/32.0
|
||||
end
|
||||
|
||||
def self.pbScreenZoomY(ch)
|
||||
zoom=1.0
|
||||
if $PokemonSystem.tilemap==2
|
||||
zoom=((ch.screen_y - 16) - (Graphics.height / 2)) *
|
||||
(Draw_Tilemap::Pitch*1.0 / (Graphics.height * 25)) + 1
|
||||
end
|
||||
return zoom*Game_Map::TILE_HEIGHT/32.0
|
||||
end
|
||||
|
||||
def self.pbScreenX(ch)
|
||||
ret=ch.screen_x
|
||||
if $PokemonSystem.tilemap==2
|
||||
widthdiv2=(Graphics.width / 2)
|
||||
ret=widthdiv2+(ret-widthdiv2)*pbScreenZoomX(ch)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def self.pbScreenY(ch)
|
||||
ret=ch.screen_y
|
||||
if $PokemonSystem.tilemap==2 && Draw_Tilemap::Curve && Draw_Tilemap::Pitch != 0
|
||||
zoomy=pbScreenZoomY(ch)
|
||||
oneMinusZoomY=1-zoomy
|
||||
ret += (8 * oneMinusZoomY * (oneMinusZoomY /
|
||||
(2 * ((Draw_Tilemap::Pitch*1.0 / 100) / (Graphics.height*1.0 / 16.0))) + 0.5))
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
@heightcache={}
|
||||
|
||||
def self.bmHeight(bm)
|
||||
h=@heightcache[bm]
|
||||
if !h
|
||||
bmap=AnimatedBitmap.new("Graphics/Characters/"+bm,0)
|
||||
h=bmap.height
|
||||
@heightcache[bm]=h
|
||||
bmap.dispose
|
||||
end
|
||||
return h
|
||||
end
|
||||
|
||||
def self.pbScreenZ(ch,height=nil)
|
||||
if height==nil
|
||||
height=0
|
||||
if ch.tile_id > 0
|
||||
height=32
|
||||
elsif ch.character_name!=""
|
||||
height=bmHeight(ch.character_name)/4
|
||||
end
|
||||
end
|
||||
ret=ch.screen_z(height)
|
||||
if $PokemonSystem.tilemap==2
|
||||
ret-=(pbScreenZoomY(ch) < 0.5 ? 1000 : 0)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
###############################################
|
||||
|
||||
|
||||
|
||||
class Draw_Tilemap # This class controls a set of sprites, with
|
||||
attr_reader :tileset # different Z values, arranged into horizontal bars
|
||||
attr_reader :map_data
|
||||
attr_reader :flash_data
|
||||
attr_reader :priorities
|
||||
attr_reader :terrain_tags
|
||||
attr_reader :autotiles
|
||||
attr_accessor :bitmaps
|
||||
attr_accessor :pitch
|
||||
attr_accessor :ox
|
||||
attr_accessor :oy
|
||||
attr_accessor :visible
|
||||
attr_reader :viewport
|
||||
attr_accessor :color
|
||||
attr_accessor :tone
|
||||
StripSize = 16
|
||||
Curve = true
|
||||
Pitch = 3
|
||||
FlashOpacity = [100,90,80,70,80,90]
|
||||
|
||||
def initialize(viewport=nil)
|
||||
@tileset=nil
|
||||
@map_data=nil
|
||||
@priorities=nil
|
||||
@terrain_tags=nil
|
||||
@autotiles=[nil,nil,nil,nil,nil,nil,nil]
|
||||
@viewport=viewport
|
||||
@visible=true
|
||||
@helper=TileDrawingHelper.new(nil,@autotiles)
|
||||
@drawnstrips=[]
|
||||
@contentstrips=[]
|
||||
@disposed=false
|
||||
@bitmaps=[]
|
||||
@sprites=[]
|
||||
@ox=0
|
||||
@oy=0
|
||||
@tone=Tone.new(0,0,0,0)
|
||||
@color=Color.new(0,0,0,0)
|
||||
@flash_data=nil
|
||||
@numsprites=0
|
||||
end
|
||||
|
||||
def tileset=(value)
|
||||
@tileset=value
|
||||
@helper.tileset=value
|
||||
@doredraw=true
|
||||
end
|
||||
|
||||
def map_data=(value)
|
||||
@map_data=value
|
||||
@doredraw=true
|
||||
end
|
||||
|
||||
def flash_data=(value)
|
||||
@flash_data=value
|
||||
@doredraw=true
|
||||
end
|
||||
|
||||
def priorities=(value)
|
||||
@priorities=value
|
||||
@doredraw=true
|
||||
end
|
||||
|
||||
def terrain_tags=(value)
|
||||
@terrain_tags=value
|
||||
@doredraw=true
|
||||
end
|
||||
|
||||
def redrawmap
|
||||
# Provide blank data in proper object form
|
||||
self.clear
|
||||
xsize=@map_data.xsize
|
||||
ysize=@map_data.ysize
|
||||
# Bitmaps used for each priority's drawing. Priorities 2-5 are combined.
|
||||
@bitmaps = [Bitmap.new(xsize*32, ysize*32+StripSize),
|
||||
Bitmap.new(xsize*32, ysize*32+StripSize),
|
||||
Bitmap.new(xsize*32, ysize*32+StripSize)]
|
||||
for i in @bitmaps
|
||||
i.clear
|
||||
end
|
||||
if @flash_data
|
||||
@bitmaps.push(Bitmap.new(xsize*32, ysize*32+StripSize))
|
||||
end
|
||||
@drawnstrips.clear
|
||||
@contentstrips.clear
|
||||
# Generate blank sprites
|
||||
@sprites.clear
|
||||
@numsprites=ysize * (32 / StripSize)
|
||||
for i in 0...@map_data.zsize # For each layer
|
||||
@sprites.push([])
|
||||
@contentstrips.push([])
|
||||
end
|
||||
if @flash_data
|
||||
@sprites.push([])
|
||||
@contentstrips.push([])
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
oyunchanged=false
|
||||
if !@flash_data.nil? && @sprites.length>0
|
||||
flashindex=@sprites.length-1
|
||||
for j in 0...@numsprites
|
||||
sprite=@sprites[flashindex][j]
|
||||
next if !sprite.is_a?(Sprite)
|
||||
sprite.opacity=FlashOpacity[(Graphics.frame_count/2) % 6]
|
||||
end
|
||||
end
|
||||
for s in @sprites
|
||||
for sprite in s
|
||||
next if !sprite.is_a?(Sprite)
|
||||
# sprite.tone=@tone
|
||||
# sprite.color=@color
|
||||
end
|
||||
end
|
||||
if @doredraw
|
||||
@drawnstrips=[]
|
||||
redrawmap
|
||||
@doredraw=false
|
||||
elsif @oldOx==@ox && @oldOy==@oy
|
||||
return
|
||||
elsif @oldOy==@oy
|
||||
oyunchanged=true
|
||||
end
|
||||
@oldOx=@ox
|
||||
@oldOy=@oy
|
||||
@pitch = Pitch
|
||||
minvalue=[0, ((Graphics.height / 2) -
|
||||
((Graphics.height * 60) / @pitch) + @oy) / StripSize].max.to_i
|
||||
maxvalue=[@numsprites - 1,(@oy + Graphics.height) / StripSize].min.to_i
|
||||
return if minvalue>maxvalue
|
||||
for j in 0...@numsprites
|
||||
if j<minvalue || j>maxvalue
|
||||
for i in 0...@sprites.length
|
||||
sprite=@sprites[i][j]
|
||||
if sprite
|
||||
sprite.dispose if sprite.is_a?(Sprite)
|
||||
@sprites[i][j]=nil
|
||||
end
|
||||
end
|
||||
else
|
||||
drawStrip(j)
|
||||
end
|
||||
end
|
||||
vpy=@viewport.rect.y
|
||||
vpr=@viewport.rect.x+@viewport.rect.width
|
||||
vpb=@viewport.rect.y+@viewport.rect.height
|
||||
numsprites=0
|
||||
for i in @sprites
|
||||
numsprites+=i.compact.length
|
||||
end
|
||||
for j in minvalue..maxvalue
|
||||
# For each strip within the visible screen, update OX/Y
|
||||
x=Graphics.width/2
|
||||
sox=@ox+x
|
||||
y = (j * StripSize - @oy)
|
||||
zoom_x=1.0
|
||||
zoom_y=1.0
|
||||
unless @pitch == 0 # Apply X Zoom
|
||||
zoom_x = (y - Graphics.height*1.0 / 2) * (@pitch*1.0 / (Graphics.height * 25)) + 1
|
||||
if Curve # Zoom Y values same as X, and compensate
|
||||
zoom_y = zoom_x
|
||||
yadd = StripSize*1.0 * (1 - zoom_y) * ((1 - zoom_y) /
|
||||
(2 * ((@pitch*1.0 / 100) / (Graphics.height*1.0 / (StripSize * 2)))) + 0.5)
|
||||
y+=yadd
|
||||
end
|
||||
end
|
||||
xstart=(x-sox*zoom_x)
|
||||
yend=(y+(StripSize*2)*zoom_y)
|
||||
if xstart>vpr || yend<=vpy
|
||||
for i in 0...@sprites.length
|
||||
sprite=@sprites[i][j]
|
||||
if sprite.is_a?(Sprite)
|
||||
sprite.dispose
|
||||
@sprites[i][j]=nil
|
||||
end
|
||||
end
|
||||
else
|
||||
for i in 0...@sprites.length
|
||||
sprite=@sprites[i][j]
|
||||
next if !sprite
|
||||
if sprite==true
|
||||
sprite=newSprite(i,j)
|
||||
@sprites[i][j]=sprite
|
||||
end
|
||||
sprite.visible=@visible
|
||||
sprite.x = x
|
||||
sprite.ox = sox
|
||||
sprite.y = y
|
||||
sprite.zoom_x = zoom_x
|
||||
sprite.zoom_y = zoom_y
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def clear
|
||||
for i in @bitmaps
|
||||
i.dispose
|
||||
end
|
||||
@bitmaps.clear
|
||||
for i in 0...@sprites.length
|
||||
for j in 0...@sprites[i].length
|
||||
@sprites[i][j].dispose if @sprites[i][j].is_a?(Sprite)
|
||||
end
|
||||
@sprites[i].clear
|
||||
end
|
||||
@sprites.clear
|
||||
end
|
||||
|
||||
def dispose
|
||||
return if @disposed
|
||||
self.clear
|
||||
for i in 0...7
|
||||
self.autotiles[i]=nil
|
||||
end
|
||||
@helper=nil
|
||||
@sprites=nil
|
||||
@bitmaps=nil
|
||||
@disposed = true
|
||||
end
|
||||
|
||||
def disposed?
|
||||
return @disposed
|
||||
end
|
||||
|
||||
def newSprite(i,j)
|
||||
sprite=Sprite.new(@viewport)
|
||||
sprite.bitmap=@bitmaps[i]
|
||||
sprite.src_rect.set(0, j * StripSize, @map_data.xsize * 32, StripSize * 2)
|
||||
sprite.x = Graphics.width / 2
|
||||
sprite.y = -64
|
||||
sprite.z = (i * 32)
|
||||
sprite.tone=@tone
|
||||
sprite.color=@color
|
||||
if i==@bitmaps.length-1 && !@flash_data.nil?
|
||||
sprite.blend_type=1
|
||||
sprite.z=1
|
||||
sprite.opacity=FlashOpacity[(Graphics.frame_count/2) % 6]
|
||||
end
|
||||
return sprite
|
||||
end
|
||||
|
||||
def drawStrip(j)
|
||||
minY=(j*StripSize)/32
|
||||
maxY=(j*StripSize+StripSize*2)/32
|
||||
minY=0 if minY<0
|
||||
minY=@map_data.ysize-1 if minY>@map_data.ysize-1
|
||||
maxY=0 if maxY<0
|
||||
maxY=@map_data.ysize-1 if maxY>@map_data.ysize-1
|
||||
for y in minY..maxY
|
||||
if !@drawnstrips[y]
|
||||
for x in 0...@map_data.xsize
|
||||
draw_position(x, y)
|
||||
end
|
||||
@drawnstrips[y]=true
|
||||
end
|
||||
end
|
||||
for i in 0...@sprites.length # For each priority
|
||||
sprite=@sprites[i][j]
|
||||
if !sprite || (sprite!=true && sprite.disposed?)
|
||||
havecontent=false
|
||||
for y in minY..maxY
|
||||
havecontent=havecontent||@contentstrips[i][y]
|
||||
end
|
||||
sprite=(havecontent) ? true : nil
|
||||
@sprites[i][j]=sprite
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def draw_position(x, y)
|
||||
for layer in 0...@map_data.zsize
|
||||
pos = @map_data[x, y, layer]
|
||||
priopos=@priorities[pos]
|
||||
priopos=0 if !priopos
|
||||
prio=(2<priopos) ? 2 : priopos
|
||||
@contentstrips[prio][y]=true if pos>0
|
||||
@helper.bltTile(@bitmaps[prio],x*32,y*32,pos,0)
|
||||
end
|
||||
if !@flash_data.nil?
|
||||
lastlayer=@bitmaps.length-1
|
||||
id=@flash_data[x,y,0]
|
||||
r=(id>>8)&15
|
||||
g=(id>>4)&15
|
||||
b=(id)&15
|
||||
@contentstrips[lastlayer][y]=true
|
||||
color=Color.new(r*16,g*16,b*16)
|
||||
@bitmaps[lastlayer].fill_rect(x*32,y*32,32,32,color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Sprite_Character
|
||||
alias perspectivetilemap_initialize initialize
|
||||
attr_accessor :character
|
||||
|
||||
def initialize(viewport, character = nil)
|
||||
@character = character
|
||||
perspectivetilemap_initialize(viewport,character)
|
||||
end
|
||||
|
||||
alias update_or :update
|
||||
|
||||
def update
|
||||
update_or
|
||||
if $PokemonSystem.tilemap==2
|
||||
self.zoom_y=ScreenPosHelper.pbScreenZoomY(@character)
|
||||
self.zoom_x=ScreenPosHelper.pbScreenZoomX(@character)
|
||||
self.x=ScreenPosHelper.pbScreenX(@character)
|
||||
self.y=ScreenPosHelper.pbScreenY(@character)
|
||||
self.z=ScreenPosHelper.pbScreenZ(@character,@ch)
|
||||
end
|
||||
end
|
||||
end
|
||||
118
Data/Scripts/005_Map renderer/003_Tilemap_Original.rb
Normal file
118
Data/Scripts/005_Map renderer/003_Tilemap_Original.rb
Normal file
@@ -0,0 +1,118 @@
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
class SynchronizedTilemapAutotilesInternal
|
||||
def initialize(oldat)
|
||||
@atdisposables = [[],[],[],[],[],[],[]]
|
||||
@atframes = [[],[],[],[],[],[],[]]
|
||||
@atframe = [-1,-1,-1,-1,-1,-1,-1]
|
||||
@autotiles = []
|
||||
@oldat = oldat
|
||||
end
|
||||
|
||||
def dispose
|
||||
for i in 0...7
|
||||
for bitmap in @atdisposables[i]
|
||||
bitmap.dispose
|
||||
end
|
||||
@atdisposables[i].clear
|
||||
@atframes[i].clear
|
||||
end
|
||||
end
|
||||
|
||||
def [](i)
|
||||
return @autotiles[i]
|
||||
end
|
||||
|
||||
def []=(i,value)
|
||||
for frame in @atdisposables[i]
|
||||
frame.dispose
|
||||
end
|
||||
@atframe[i] = -1
|
||||
@atframes[i].clear
|
||||
@atdisposables[i].clear
|
||||
if value && !value.disposed?
|
||||
if value.height==32
|
||||
frames = value.width/32
|
||||
for j in 0...frames
|
||||
@atdisposables[i][j] = Bitmap.new(32,32)
|
||||
@atdisposables[i][j].blt(0,0,value,Rect.new(j*32,0,32,32))
|
||||
@atframes[i][j] = @atdisposables[i][j]
|
||||
end
|
||||
elsif value.height==128
|
||||
frames = value.width/96
|
||||
for j in 0...frames
|
||||
@atdisposables[i][j] = Bitmap.new(96,128)
|
||||
@atdisposables[i][j].blt(0,0,value,Rect.new(j*96,0,96,128))
|
||||
@atframes[i][j] = @atdisposables[i][j]
|
||||
end
|
||||
else
|
||||
@atframes[i][0] = value
|
||||
end
|
||||
else
|
||||
@atframes[i][0] = value
|
||||
end
|
||||
@autotiles[i] = value
|
||||
sync
|
||||
end
|
||||
|
||||
def sync
|
||||
frameused = []
|
||||
for i in 0...7
|
||||
frames = [1,@atframes[i].length].max
|
||||
frame = (Graphics.frame_count/15)%frames
|
||||
if frames>1 && @atframe[i]!=frame
|
||||
@oldat[i] = @atframes[i][frame]
|
||||
@atframe[i] = frame
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class SynchronizedTilemapAutotiles
|
||||
def initialize(autotiles)
|
||||
@autotiles = autotiles
|
||||
end
|
||||
|
||||
def [](i)
|
||||
return @autotiles[i]
|
||||
end
|
||||
|
||||
def []=(i,value)
|
||||
@autotiles[i] = value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class SynchronizedTilemap < Tilemap
|
||||
# This class derives from Tilemap just to synchronize
|
||||
# the tilemap animation.
|
||||
attr_accessor :numupdates
|
||||
|
||||
def initialize(viewport=nil)
|
||||
super(viewport)
|
||||
@updating = true
|
||||
@autotiles = SynchronizedTilemapAutotilesInternal.new(self.autotiles)
|
||||
@autos = SynchronizedTilemapAutotiles.new(@autotiles)
|
||||
@updating = false
|
||||
end
|
||||
|
||||
def dispose
|
||||
@autotiles.dispose
|
||||
super
|
||||
end
|
||||
|
||||
def autotiles
|
||||
return @autos if !@updating
|
||||
super
|
||||
end
|
||||
|
||||
def update
|
||||
return if disposed?
|
||||
@autotiles.sync
|
||||
super
|
||||
end
|
||||
end
|
||||
70
Data/Scripts/005_Map renderer/004_TilemapLoader.rb
Normal file
70
Data/Scripts/005_Map renderer/004_TilemapLoader.rb
Normal file
@@ -0,0 +1,70 @@
|
||||
class TilemapLoader
|
||||
def initialize(viewport)
|
||||
@viewport = viewport
|
||||
@tilemap = nil
|
||||
@color = Color.new(0,0,0,0)
|
||||
@tone = Tone.new(0,0,0,0)
|
||||
updateClass
|
||||
end
|
||||
|
||||
def updateClass
|
||||
case $PokemonSystem.tilemap
|
||||
when 1 # Custom (recommended)
|
||||
setClass(CustomTilemap)
|
||||
when 2 # Perspective
|
||||
setClass(Draw_Tilemap)
|
||||
else # Original (SynchronizedTilemap) or custom (CustomTilemap)
|
||||
if Tilemap.method_defined?(:passages)
|
||||
setClass(CustomTilemap)
|
||||
else
|
||||
setClass(($ResizeFactor==1.0) ? SynchronizedTilemap : CustomTilemap)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setClass(cls)
|
||||
newtilemap = cls.new(@viewport)
|
||||
if @tilemap
|
||||
newtilemap.tileset = @tilemap.tileset
|
||||
newtilemap.map_data = @tilemap.map_data
|
||||
newtilemap.flash_data = @tilemap.flash_data
|
||||
newtilemap.priorities = @tilemap.priorities
|
||||
newtilemap.terrain_tags = @tilemap.terrain_tags
|
||||
newtilemap.visible = @tilemap.visible
|
||||
newtilemap.ox = @tilemap.ox
|
||||
newtilemap.oy = @tilemap.oy
|
||||
for i in 0...7
|
||||
newtilemap.autotiles[i] = @tilemap.autotiles[i]
|
||||
end
|
||||
@tilemap.dispose
|
||||
@tilemap = newtilemap
|
||||
newtilemap.update if cls!=SynchronizedTilemap
|
||||
else
|
||||
@tilemap = newtilemap
|
||||
end
|
||||
end
|
||||
|
||||
def dispose; @tilemap.dispose; end
|
||||
def disposed?; @tilemap && @tilemap.disposed?; end
|
||||
def update; @tilemap.update; end
|
||||
def viewport; @tilemap.viewport; end
|
||||
def autotiles; @tilemap.autotiles; end
|
||||
def tileset; @tilemap.tileset; end
|
||||
def tileset=(v); @tilemap.tileset = v; end
|
||||
def map_data; @tilemap.map_data; end
|
||||
def map_data=(v); @tilemap.map_data = v; end
|
||||
def flash_data; @tilemap.flash_data; end
|
||||
def flash_data=(v); @tilemap.flash_data = v; end
|
||||
def priorities; @tilemap.priorities; end
|
||||
def priorities=(v); @tilemap.priorities = v; end
|
||||
def terrain_tags; (@tilemap.terrain_tags rescue nil); end
|
||||
def terrain_tags=(v); (@tilemap.terrain_tags = v rescue nil); end
|
||||
def visible; @tilemap.visible; end
|
||||
def visible=(v); @tilemap.visible = v; end
|
||||
def tone; (@tilemap.tone rescue @tone); end
|
||||
def tone=(value); (@tilemap.tone = value rescue nil); end
|
||||
def ox; @tilemap.ox; end
|
||||
def ox=(v); @tilemap.ox = v; end
|
||||
def oy; @tilemap.oy; end
|
||||
def oy=(v); @tilemap.oy = v; end
|
||||
end
|
||||
136
Data/Scripts/005_Map renderer/005_TileDrawingHelper.rb
Normal file
136
Data/Scripts/005_Map renderer/005_TileDrawingHelper.rb
Normal file
@@ -0,0 +1,136 @@
|
||||
class TileDrawingHelper
|
||||
attr_accessor :tileset
|
||||
attr_accessor :autotiles
|
||||
|
||||
Autotiles = [
|
||||
[ [27, 28, 33, 34], [ 5, 28, 33, 34], [27, 6, 33, 34], [ 5, 6, 33, 34],
|
||||
[27, 28, 33, 12], [ 5, 28, 33, 12], [27, 6, 33, 12], [ 5, 6, 33, 12] ],
|
||||
[ [27, 28, 11, 34], [ 5, 28, 11, 34], [27, 6, 11, 34], [ 5, 6, 11, 34],
|
||||
[27, 28, 11, 12], [ 5, 28, 11, 12], [27, 6, 11, 12], [ 5, 6, 11, 12] ],
|
||||
[ [25, 26, 31, 32], [25, 6, 31, 32], [25, 26, 31, 12], [25, 6, 31, 12],
|
||||
[15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
|
||||
[ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
|
||||
[39, 40, 45, 46], [ 5, 40, 45, 46], [39, 6, 45, 46], [ 5, 6, 45, 46] ],
|
||||
[ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
|
||||
[17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
|
||||
[ [37, 38, 43, 44], [37, 6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
|
||||
[37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1, 2, 7, 8] ]
|
||||
]
|
||||
|
||||
# converts neighbors returned from tableNeighbors to tile indexes
|
||||
NeighborsToTiles = [
|
||||
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
|
||||
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
|
||||
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
|
||||
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
|
||||
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
|
||||
37, 27, 37, 27, 23, 15, 23, 13, 37, 27, 37, 27, 22, 11, 22, 9,
|
||||
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
|
||||
36, 26, 36, 26, 21, 7, 21, 5, 36, 26, 36, 26, 20, 3, 20, 1,
|
||||
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
|
||||
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
|
||||
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
|
||||
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
|
||||
45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
|
||||
37, 25, 37, 25, 23, 14, 23, 12, 37, 25, 37, 25, 22, 10, 22, 8,
|
||||
45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
|
||||
36, 24, 36, 24, 21, 6, 21, 4, 36, 24, 36, 24, 20, 2, 20, 0
|
||||
]
|
||||
|
||||
def self.tableNeighbors(data,x,y)
|
||||
return 0 if x < 0 || x >= data.xsize
|
||||
return 0 if y < 0 || y >= data.ysize
|
||||
t = data[x,y]
|
||||
xp1 = [x + 1, data.xsize - 1].min
|
||||
yp1 = [y + 1, data.ysize - 1].min
|
||||
xm1 = [x - 1, 0].max
|
||||
ym1 = [y - 1, 0].max
|
||||
i = 0
|
||||
i |= 0x01 if data[ x, ym1] == t # N
|
||||
i |= 0x02 if data[xp1, ym1] == t # NE
|
||||
i |= 0x04 if data[xp1, y] == t # E
|
||||
i |= 0x08 if data[xp1, yp1] == t # SE
|
||||
i |= 0x10 if data[ x, yp1] == t # S
|
||||
i |= 0x20 if data[xm1, yp1] == t # SW
|
||||
i |= 0x40 if data[xm1, y] == t # W
|
||||
i |= 0x80 if data[xm1, ym1] == t # NW
|
||||
return i
|
||||
end
|
||||
|
||||
def self.fromTileset(tileset)
|
||||
bmtileset=pbGetTileset(tileset.tileset_name)
|
||||
bmautotiles=[]
|
||||
for i in 0...7
|
||||
bmautotiles.push(pbGetAutotile(tileset.autotile_names[i]))
|
||||
end
|
||||
return self.new(bmtileset,bmautotiles)
|
||||
end
|
||||
|
||||
def initialize(tileset,autotiles)
|
||||
@tileset = tileset
|
||||
@autotiles = autotiles
|
||||
end
|
||||
|
||||
def dispose
|
||||
@tileset.dispose if @tileset
|
||||
@tileset = nil
|
||||
for i in 0...@autotiles.length
|
||||
@autotiles[i].dispose
|
||||
@autotiles[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
def bltSmallAutotile(bitmap,x,y,cxTile,cyTile,id,frame)
|
||||
return if id >= 384 || frame < 0 || !@autotiles
|
||||
autotile = @autotiles[id / 48 - 1]
|
||||
return if !autotile || autotile.disposed?
|
||||
cxTile = [cxTile / 2, 1].max
|
||||
cyTile = [cyTile / 2, 1].max
|
||||
if autotile.height == 32
|
||||
anim = frame * 32
|
||||
src_rect = Rect.new(anim, 0, 32, 32)
|
||||
bitmap.stretch_blt(Rect.new(x, y, cxTile * 2, cyTile * 2), autotile, src_rect)
|
||||
else
|
||||
anim = frame * 96
|
||||
id %= 48
|
||||
tiles = TileDrawingHelper::Autotiles[id >> 3][id & 7]
|
||||
src = Rect.new(0, 0, 0, 0)
|
||||
for i in 0...4
|
||||
tile_position = tiles[i] - 1
|
||||
src.set(tile_position % 6 * 16 + anim, tile_position / 6 * 16, 16, 16)
|
||||
bitmap.stretch_blt(Rect.new(i % 2 * cxTile + x, i / 2 * cyTile + y, cxTile, cyTile),
|
||||
autotile, src)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def bltSmallRegularTile(bitmap,x,y,cxTile,cyTile,id)
|
||||
return if id < 384 || !@tileset || @tileset.disposed?
|
||||
rect = Rect.new((id - 384) % 8 * 32, (id - 384) / 8 * 32, 32, 32)
|
||||
bitmap.stretch_blt(Rect.new(x, y, cxTile, cyTile), @tileset, rect)
|
||||
end
|
||||
|
||||
def bltSmallTile(bitmap,x,y,cxTile,cyTile,id,frame=0)
|
||||
if id >= 384
|
||||
bltSmallRegularTile(bitmap, x, y, cxTile, cyTile, id)
|
||||
elsif id > 0
|
||||
bltSmallAutotile(bitmap, x, y, cxTile, cyTile, id, frame)
|
||||
end
|
||||
end
|
||||
|
||||
def bltAutotile(bitmap,x,y,id,frame)
|
||||
bltSmallAutotile(bitmap, x, y, 32, 32, id, frame)
|
||||
end
|
||||
|
||||
def bltRegularTile(bitmap,x,y,id)
|
||||
bltSmallRegularTile(bitmap, x, y, 32, 32, id)
|
||||
end
|
||||
|
||||
def bltTile(bitmap,x,y,id,frame=0)
|
||||
if id >= 384
|
||||
bltRegularTile(bitmap, x, y, id)
|
||||
elsif id > 0
|
||||
bltAutotile(bitmap, x, y, id, frame)
|
||||
end
|
||||
end
|
||||
end
|
||||
1469
Data/Scripts/006_Events and files/001_Interpreter.rb
Normal file
1469
Data/Scripts/006_Events and files/001_Interpreter.rb
Normal file
File diff suppressed because it is too large
Load Diff
172
Data/Scripts/006_Events and files/002_EventHandlers.rb
Normal file
172
Data/Scripts/006_Events and files/002_EventHandlers.rb
Normal file
@@ -0,0 +1,172 @@
|
||||
# Defines an event that procedures can subscribe to.
|
||||
class Event
|
||||
def initialize
|
||||
@callbacks = []
|
||||
end
|
||||
|
||||
# Sets an event handler for this event and removes all other event handlers.
|
||||
def set(method)
|
||||
@callbacks.clear
|
||||
@callbacks.push(method)
|
||||
end
|
||||
|
||||
# Removes an event handler procedure from the event.
|
||||
def -(method)
|
||||
for i in 0...@callbacks.length
|
||||
next if @callbacks[i]!=method
|
||||
@callbacks.delete_at(i)
|
||||
break
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
# Adds an event handler procedure from the event.
|
||||
def +(method)
|
||||
for i in 0...@callbacks.length
|
||||
return self if @callbacks[i]==method
|
||||
end
|
||||
@callbacks.push(method)
|
||||
return self
|
||||
end
|
||||
|
||||
# Clears the event of event handlers.
|
||||
def clear
|
||||
@callbacks.clear
|
||||
end
|
||||
|
||||
# Triggers the event and calls all its event handlers. Normally called only
|
||||
# by the code where the event occurred.
|
||||
# The first argument is the sender of the event, the second argument contains
|
||||
# the event's parameters. If three or more arguments are given, this method
|
||||
# supports the following callbacks:
|
||||
# proc{ |sender,params| } where params is an array of the other parameters, and
|
||||
# proc{ |sender,arg0,arg1,...| }
|
||||
def trigger(*arg)
|
||||
arglist = arg[1,arg.length]
|
||||
for callback in @callbacks
|
||||
if callback.arity>2 && arg.length==callback.arity
|
||||
# Retrofitted for callbacks that take three or more arguments
|
||||
callback.call(*arg)
|
||||
else
|
||||
callback.call(arg[0],arglist)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Triggers the event and calls all its event handlers. Normally called only
|
||||
# by the code where the event occurred. The first argument is the sender of
|
||||
# the event, the other arguments are the event's parameters.
|
||||
def trigger2(*arg)
|
||||
for callback in @callbacks
|
||||
callback.call(*arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class HandlerHash
|
||||
def initialize(mod)
|
||||
@mod = mod
|
||||
@hash = {}
|
||||
@addIfs = []
|
||||
@symbolCache = {}
|
||||
end
|
||||
|
||||
def fromSymbol(sym)
|
||||
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
|
||||
mod = Object.const_get(@mod) rescue nil
|
||||
return nil if !mod
|
||||
return mod.const_get(sym.to_sym) rescue nil
|
||||
end
|
||||
|
||||
def toSymbol(sym)
|
||||
return sym.to_sym if sym.is_a?(Symbol) || sym.is_a?(String)
|
||||
ret = @symbolCache[sym]
|
||||
return ret if ret
|
||||
mod = Object.const_get(@mod) rescue nil
|
||||
return nil if !mod
|
||||
for key in mod.constants
|
||||
next if mod.const_get(key)!=sym
|
||||
ret = key.to_sym
|
||||
@symbolCache[sym] = ret
|
||||
break
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def addIf(condProc,handler)
|
||||
@addIfs.push([condProc,handler])
|
||||
end
|
||||
|
||||
def add(sym,handler) # 'sym' can be an ID or symbol
|
||||
id = fromSymbol(sym)
|
||||
@hash[id] = handler if id
|
||||
symbol = toSymbol(sym)
|
||||
@hash[symbol] = handler if symbol
|
||||
end
|
||||
|
||||
def copy(src,*dests)
|
||||
handler = self[src]
|
||||
if handler
|
||||
for dest in dests
|
||||
self.add(dest,handler)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def [](sym) # 'sym' can be an ID or symbol
|
||||
id = fromSymbol(sym)
|
||||
ret = nil
|
||||
ret = @hash[id] if id && @hash[id] # Real ID from the item
|
||||
symbol = toSymbol(sym)
|
||||
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
|
||||
unless ret
|
||||
for addif in @addIfs
|
||||
return addif[1] if addif[0].call(id)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def trigger(sym,*args)
|
||||
handler = self[sym]
|
||||
return (handler) ? handler.call(fromSymbol(sym),*args) : nil
|
||||
end
|
||||
|
||||
def clear
|
||||
@hash.clear
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class SpeciesHandlerHash < HandlerHash
|
||||
def initialize
|
||||
super(:PBSpecies)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class AbilityHandlerHash < HandlerHash
|
||||
def initialize
|
||||
super(:PBAbilities)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class ItemHandlerHash < HandlerHash
|
||||
def initialize
|
||||
super(:PBItems)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class MoveHandlerHash < HandlerHash
|
||||
def initialize
|
||||
super(:PBMoves)
|
||||
end
|
||||
end
|
||||
163
Data/Scripts/006_Events and files/003_File_Mixins.rb
Normal file
163
Data/Scripts/006_Events and files/003_File_Mixins.rb
Normal file
@@ -0,0 +1,163 @@
|
||||
module FileInputMixin
|
||||
def fgetb
|
||||
x=0
|
||||
ret=0
|
||||
each_byte do |i|
|
||||
ret=i || 0
|
||||
break
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def fgetw
|
||||
x=0
|
||||
ret=0
|
||||
each_byte do |i|
|
||||
break if !i
|
||||
ret|=(i<<x)
|
||||
x+=8
|
||||
break if x==16
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def fgetdw
|
||||
x=0
|
||||
ret=0
|
||||
each_byte do |i|
|
||||
break if !i
|
||||
ret|=(i<<x)
|
||||
x+=8
|
||||
break if x==32
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def fgetsb
|
||||
ret=fgetb
|
||||
if (ret&0x80)!=0
|
||||
return ret-256
|
||||
else
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
def xfgetb(offset)
|
||||
self.pos=offset
|
||||
return fgetb
|
||||
end
|
||||
|
||||
def xfgetw(offset)
|
||||
self.pos=offset
|
||||
return fgetw
|
||||
end
|
||||
|
||||
def xfgetdw(offset)
|
||||
self.pos=offset
|
||||
return fgetdw
|
||||
end
|
||||
|
||||
def getOffset(index)
|
||||
self.binmode
|
||||
self.pos=0
|
||||
offset=fgetdw>>3
|
||||
return 0 if index>=offset
|
||||
self.pos=index*8
|
||||
return fgetdw
|
||||
end
|
||||
|
||||
def getLength(index)
|
||||
self.binmode
|
||||
self.pos=0
|
||||
offset=fgetdw>>3
|
||||
return 0 if index>=offset
|
||||
self.pos=index*8+4
|
||||
return fgetdw
|
||||
end
|
||||
|
||||
def readName(index)
|
||||
self.binmode
|
||||
self.pos=0
|
||||
offset=fgetdw>>3
|
||||
return "" if index>=offset
|
||||
self.pos=index<<3
|
||||
offset=fgetdw
|
||||
length=fgetdw
|
||||
return "" if length==0
|
||||
self.pos=offset
|
||||
return read(length)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module FileOutputMixin
|
||||
def fputb(b)
|
||||
b=b&0xFF
|
||||
write(b.chr)
|
||||
end
|
||||
|
||||
def fputw(w)
|
||||
2.times do
|
||||
b=w&0xFF
|
||||
write(b.chr)
|
||||
w>>=8
|
||||
end
|
||||
end
|
||||
|
||||
def fputdw(w)
|
||||
4.times do
|
||||
b=w&0xFF
|
||||
write(b.chr)
|
||||
w>>=8
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class File < IO
|
||||
=begin
|
||||
unless defined?(debugopen)
|
||||
class << self
|
||||
alias debugopen open
|
||||
end
|
||||
end
|
||||
|
||||
def open(f,m="r")
|
||||
debugopen("debug.txt","ab") { |file| file.write([f,m,Time.now.to_f].inspect+"\r\n") }
|
||||
if block_given?
|
||||
debugopen(f,m) { |file| yield file }
|
||||
else
|
||||
return debugopen(f,m)
|
||||
end
|
||||
end
|
||||
=end
|
||||
include FileInputMixin
|
||||
include FileOutputMixin
|
||||
end
|
||||
|
||||
|
||||
|
||||
class StringInput
|
||||
include FileInputMixin
|
||||
|
||||
def pos=(value)
|
||||
seek(value)
|
||||
end
|
||||
|
||||
def each_byte
|
||||
while !eof?
|
||||
yield getc
|
||||
end
|
||||
end
|
||||
|
||||
def binmode
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class StringOutput
|
||||
include FileOutputMixin
|
||||
end
|
||||
776
Data/Scripts/006_Events and files/004_Intl_Messages.rb
Normal file
776
Data/Scripts/006_Events and files/004_Intl_Messages.rb
Normal file
@@ -0,0 +1,776 @@
|
||||
def pbAddScriptTexts(items,script)
|
||||
script.scan(/(?:_I)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s|
|
||||
string=s[0]
|
||||
string.gsub!(/\\\"/,"\"")
|
||||
string.gsub!(/\\\\/,"\\")
|
||||
items.push(string)
|
||||
}
|
||||
end
|
||||
|
||||
def pbAddRgssScriptTexts(items,script)
|
||||
script.scan(/(?:_INTL|_ISPRINTF)\s*\(\s*\"((?:[^\\\"]*\\\"?)*[^\"]*)\"/) { |s|
|
||||
string=s[0]
|
||||
string.gsub!(/\\r/,"\r")
|
||||
string.gsub!(/\\n/,"\n")
|
||||
string.gsub!(/\\1/,"\1")
|
||||
string.gsub!(/\\\"/,"\"")
|
||||
string.gsub!(/\\\\/,"\\")
|
||||
items.push(string)
|
||||
}
|
||||
end
|
||||
|
||||
def pbSetTextMessages
|
||||
Graphics.update
|
||||
begin
|
||||
t = Time.now.to_i
|
||||
texts=[]
|
||||
for script in $RGSS_SCRIPTS
|
||||
if Time.now.to_i - t >= 5
|
||||
t = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
scr=Zlib::Inflate.inflate(script[2])
|
||||
pbAddRgssScriptTexts(texts,scr)
|
||||
end
|
||||
# Must add messages because this code is used by both game system and Editor
|
||||
MessageTypes.addMessagesAsHash(MessageTypes::ScriptTexts,texts)
|
||||
commonevents=pbLoadRxData("Data/CommonEvents")
|
||||
items=[]
|
||||
choices=[]
|
||||
for event in commonevents.compact
|
||||
if Time.now.to_i - t >= 5
|
||||
t = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
begin
|
||||
neednewline=false
|
||||
lastitem=""
|
||||
for j in 0...event.list.size
|
||||
list = event.list[j]
|
||||
if neednewline && list.code!=401
|
||||
if lastitem!=""
|
||||
lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1+" " }
|
||||
items.push(lastitem)
|
||||
lastitem=""
|
||||
end
|
||||
neednewline=false
|
||||
end
|
||||
if list.code == 101
|
||||
lastitem+="#{list.parameters[0]}" if !$RPGVX
|
||||
neednewline=true
|
||||
elsif list.code == 102
|
||||
for k in 0...list.parameters[0].length
|
||||
choices.push(list.parameters[0][k])
|
||||
end
|
||||
neednewline=false
|
||||
elsif list.code == 401
|
||||
lastitem+=" " if lastitem!=""
|
||||
lastitem+="#{list.parameters[0]}"
|
||||
neednewline=true
|
||||
elsif list.code == 355 || list.code == 655
|
||||
pbAddScriptTexts(items,list.parameters[0])
|
||||
elsif list.code == 111 && list.parameters[0]==12
|
||||
pbAddScriptTexts(items,list.parameters[1])
|
||||
elsif list.code == 209
|
||||
route=list.parameters[1]
|
||||
for k in 0...route.list.size
|
||||
if route.list[k].code == 45
|
||||
pbAddScriptTexts(items,route.list[k].parameters[0])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if neednewline
|
||||
if lastitem!=""
|
||||
items.push(lastitem)
|
||||
lastitem=""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if Time.now.to_i - t >= 5
|
||||
t = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
items|=[]
|
||||
choices|=[]
|
||||
items.concat(choices)
|
||||
MessageTypes.setMapMessagesAsHash(0,items)
|
||||
mapinfos = pbLoadRxData("Data/MapInfos")
|
||||
mapnames=[]
|
||||
for id in mapinfos.keys
|
||||
mapnames[id]=mapinfos[id].name
|
||||
end
|
||||
MessageTypes.setMessages(MessageTypes::MapNames,mapnames)
|
||||
for id in mapinfos.keys
|
||||
if Time.now.to_i - t >= 5
|
||||
t = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
filename=sprintf("Data/Map%03d.%s",id,$RPGVX ? "rvdata" : "rxdata")
|
||||
next if !pbRgssExists?(filename)
|
||||
map = load_data(filename)
|
||||
items=[]
|
||||
choices=[]
|
||||
for event in map.events.values
|
||||
if Time.now.to_i - t >= 5
|
||||
t = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
begin
|
||||
for i in 0...event.pages.size
|
||||
neednewline=false
|
||||
lastitem=""
|
||||
for j in 0...event.pages[i].list.size
|
||||
list = event.pages[i].list[j]
|
||||
if neednewline && list.code!=401
|
||||
if lastitem!=""
|
||||
lastitem.gsub!(/([^\.\!\?])\s\s+/) { |m| $1+" " }
|
||||
items.push(lastitem)
|
||||
lastitem=""
|
||||
end
|
||||
neednewline=false
|
||||
end
|
||||
if list.code == 101
|
||||
lastitem+="#{list.parameters[0]}" if !$RPGVX
|
||||
neednewline=true
|
||||
elsif list.code == 102
|
||||
for k in 0...list.parameters[0].length
|
||||
choices.push(list.parameters[0][k])
|
||||
end
|
||||
neednewline=false
|
||||
elsif list.code == 401
|
||||
lastitem+=" " if lastitem!=""
|
||||
lastitem+="#{list.parameters[0]}"
|
||||
neednewline=true
|
||||
elsif list.code == 355 || list.code==655
|
||||
pbAddScriptTexts(items,list.parameters[0])
|
||||
elsif list.code == 111 && list.parameters[0]==12
|
||||
pbAddScriptTexts(items,list.parameters[1])
|
||||
elsif list.code==209
|
||||
route=list.parameters[1]
|
||||
for k in 0...route.list.size
|
||||
if route.list[k].code==45
|
||||
pbAddScriptTexts(items,route.list[k].parameters[0])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if neednewline
|
||||
if lastitem!=""
|
||||
items.push(lastitem)
|
||||
lastitem=""
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if Time.now.to_i - t >= 5
|
||||
t = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
items|=[]
|
||||
choices|=[]
|
||||
items.concat(choices)
|
||||
MessageTypes.setMapMessagesAsHash(id,items)
|
||||
if Time.now.to_i - t >= 5
|
||||
t = Time.now.to_i
|
||||
Graphics.update
|
||||
end
|
||||
end
|
||||
rescue Hangup
|
||||
end
|
||||
Graphics.update
|
||||
end
|
||||
|
||||
def pbEachIntlSection(file)
|
||||
lineno=1
|
||||
re=/^\s*\[\s*([^\]]+)\s*\]\s*$/
|
||||
havesection=false
|
||||
sectionname=nil
|
||||
lastsection=[]
|
||||
file.each_line { |line|
|
||||
if lineno==1 && line[0]==0xEF && line[1]==0xBB && line[2]==0xBF
|
||||
line=line[3,line.length-3]
|
||||
end
|
||||
if !line[/^\#/] && !line[/^\s*$/]
|
||||
if line[re]
|
||||
if havesection
|
||||
yield lastsection,sectionname
|
||||
end
|
||||
lastsection.clear
|
||||
sectionname=$~[1]
|
||||
havesection=true
|
||||
else
|
||||
if sectionname==nil
|
||||
raise _INTL("Expected a section at the beginning of the file (line {1})",lineno)
|
||||
end
|
||||
lastsection.push(line.gsub(/\s+$/,""))
|
||||
end
|
||||
end
|
||||
lineno+=1
|
||||
if lineno%500==0
|
||||
Graphics.update
|
||||
end
|
||||
}
|
||||
if havesection
|
||||
yield lastsection,sectionname
|
||||
end
|
||||
end
|
||||
|
||||
def pbGetText(infile)
|
||||
begin
|
||||
file=File.open(infile,"rb")
|
||||
rescue
|
||||
raise _INTL("Can't find {1}",infile)
|
||||
end
|
||||
intldat=[]
|
||||
begin
|
||||
pbEachIntlSection(file) { |section,name|
|
||||
next if section.length==0
|
||||
index=name
|
||||
if !name[/^([Mm][Aa][Pp])?(\d+)$/]
|
||||
raise _INTL("Invalid section name {1}",name)
|
||||
end
|
||||
ismap=$~[1] && $~[1]!=""
|
||||
id=$~[2].to_i
|
||||
itemlength=0
|
||||
if section[0][/^\d+$/]
|
||||
intlhash=[]
|
||||
itemlength=3
|
||||
if ismap
|
||||
raise _INTL("Section {1} can't be an ordered list (section was recognized as an ordered list because its first line is a number)",name)
|
||||
end
|
||||
if section.length%3!=0
|
||||
raise _INTL("Section {1}'s line count is not divisible by 3 (section was recognized as an ordered list because its first line is a number)",name)
|
||||
end
|
||||
else
|
||||
intlhash=OrderedHash.new
|
||||
itemlength=2
|
||||
if section.length%2!=0
|
||||
raise _INTL("Section {1} has an odd number of entries (section was recognized as a hash because its first line is not a number)",name)
|
||||
end
|
||||
end
|
||||
i=0;loop do break unless i<section.length
|
||||
if itemlength==3
|
||||
if !section[i][/^\d+$/]
|
||||
raise _INTL("Expected a number in section {1}, got {2} instead",name,section[i])
|
||||
end
|
||||
key=section[i].to_i
|
||||
i+=1
|
||||
else
|
||||
key=MessageTypes.denormalizeValue(section[i])
|
||||
end
|
||||
intlhash[key]=MessageTypes.denormalizeValue(section[i+1])
|
||||
i+=2
|
||||
end
|
||||
if ismap
|
||||
intldat[0]=[] if !intldat[0]
|
||||
intldat[0][id]=intlhash
|
||||
else
|
||||
intldat[id]=intlhash
|
||||
end
|
||||
}
|
||||
ensure
|
||||
file.close
|
||||
end
|
||||
return intldat
|
||||
end
|
||||
|
||||
def pbCompileText
|
||||
outfile=File.open("intl.dat","wb")
|
||||
begin
|
||||
intldat=pbGetText("intl.txt")
|
||||
Marshal.dump(intldat,outfile)
|
||||
rescue
|
||||
raise
|
||||
ensure
|
||||
outfile.close
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class OrderedHash < Hash
|
||||
def initialize
|
||||
@keys=[]
|
||||
super
|
||||
end
|
||||
|
||||
def keys
|
||||
return @keys.clone
|
||||
end
|
||||
|
||||
def inspect
|
||||
str="{"
|
||||
for i in 0...@keys.length
|
||||
str+=", " if i>0
|
||||
str+=@keys[i].inspect+"=>"+self[@keys[i]].inspect
|
||||
end
|
||||
str+="}"
|
||||
return str
|
||||
end
|
||||
|
||||
alias :to_s :inspect
|
||||
|
||||
def []=(key,value)
|
||||
oldvalue=self[key]
|
||||
if !oldvalue && value
|
||||
@keys.push(key)
|
||||
elsif !value
|
||||
@keys|=[]
|
||||
@keys-=[key]
|
||||
end
|
||||
return super(key,value)
|
||||
end
|
||||
|
||||
def self._load(string)
|
||||
ret=self.new
|
||||
keysvalues=Marshal.load(string)
|
||||
keys=keysvalues[0]
|
||||
values=keysvalues[1]
|
||||
for i in 0...keys.length
|
||||
ret[keys[i]]=values[i]
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def _dump(depth=100)
|
||||
values=[]
|
||||
for key in @keys
|
||||
values.push(self[key])
|
||||
end
|
||||
return Marshal.dump([@keys,values])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Messages
|
||||
def initialize(filename=nil,delayLoad=false)
|
||||
@messages=nil
|
||||
@filename=filename
|
||||
if @filename && !delayLoad
|
||||
loadMessageFile(@filename)
|
||||
end
|
||||
end
|
||||
|
||||
def delayedLoad
|
||||
if @filename && !@messages
|
||||
loadMessageFile(@filename)
|
||||
@filename=nil
|
||||
end
|
||||
end
|
||||
|
||||
def self.stringToKey(str)
|
||||
if str && str[/[\r\n\t\1]|^\s+|\s+$|\s{2,}/]
|
||||
key=str.clone
|
||||
key.gsub!(/^\s+/,"")
|
||||
key.gsub!(/\s+$/,"")
|
||||
key.gsub!(/\s{2,}/," ")
|
||||
return key
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
def self.normalizeValue(value)
|
||||
if value[/[\r\n\t\x01]|^[\[\]]/]
|
||||
ret=value.clone
|
||||
ret.gsub!(/\r/,"<<r>>")
|
||||
ret.gsub!(/\n/,"<<n>>")
|
||||
ret.gsub!(/\t/,"<<t>>")
|
||||
ret.gsub!(/\[/,"<<[>>")
|
||||
ret.gsub!(/\]/,"<<]>>")
|
||||
ret.gsub!(/\x01/,"<<1>>")
|
||||
return ret
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
def self.denormalizeValue(value)
|
||||
if value[/<<[rnt1\[\]]>>/]
|
||||
ret=value.clone
|
||||
ret.gsub!(/<<1>>/,"\1")
|
||||
ret.gsub!(/<<r>>/,"\r")
|
||||
ret.gsub!(/<<n>>/,"\n")
|
||||
ret.gsub!(/<<\[>>/,"[")
|
||||
ret.gsub!(/<<\]>>/,"]")
|
||||
ret.gsub!(/<<t>>/,"\t")
|
||||
return ret
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
def self.writeObject(f,msgs,secname,origMessages=nil)
|
||||
return if !msgs
|
||||
if msgs.is_a?(Array)
|
||||
f.write("[#{secname}]\r\n")
|
||||
for j in 0...msgs.length
|
||||
next if msgs[j]==nil || msgs[j]==""
|
||||
value=Messages.normalizeValue(msgs[j])
|
||||
origValue=""
|
||||
if origMessages
|
||||
origValue=Messages.normalizeValue(origMessages.get(secname,j))
|
||||
else
|
||||
origValue=Messages.normalizeValue(MessageTypes.get(secname,j))
|
||||
end
|
||||
f.write("#{j}\r\n")
|
||||
f.write(origValue+"\r\n")
|
||||
f.write(value+"\r\n")
|
||||
end
|
||||
elsif msgs.is_a?(OrderedHash)
|
||||
f.write("[#{secname}]\r\n")
|
||||
keys=msgs.keys
|
||||
for key in keys
|
||||
next if msgs[key]==nil || msgs[key]==""
|
||||
value=Messages.normalizeValue(msgs[key])
|
||||
valkey=Messages.normalizeValue(key)
|
||||
# key is already serialized
|
||||
f.write(valkey+"\r\n")
|
||||
f.write(value+"\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def messages
|
||||
return @messages || []
|
||||
end
|
||||
|
||||
def extract(outfile)
|
||||
# return if !@messages
|
||||
origMessages=Messages.new("Data/messages.dat")
|
||||
File.open(outfile,"wb") { |f|
|
||||
f.write(0xef.chr)
|
||||
f.write(0xbb.chr)
|
||||
f.write(0xbf.chr)
|
||||
f.write("# To localize this text for a particular language, please\r\n")
|
||||
f.write("# translate every second line of this file.\r\n")
|
||||
if origMessages.messages[0]
|
||||
for i in 0...origMessages.messages[0].length
|
||||
msgs=origMessages.messages[0][i]
|
||||
Messages.writeObject(f,msgs,"Map#{i}",origMessages)
|
||||
end
|
||||
end
|
||||
for i in 1...origMessages.messages.length
|
||||
msgs=origMessages.messages[i]
|
||||
Messages.writeObject(f,msgs,i,origMessages)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def setMessages(type,array)
|
||||
@messages=[] if !@messages
|
||||
arr=[]
|
||||
for i in 0...array.length
|
||||
arr[i]=(array[i]) ? array[i] : ""
|
||||
end
|
||||
@messages[type]=arr
|
||||
end
|
||||
|
||||
def addMessages(type,array)
|
||||
@messages=[] if !@messages
|
||||
arr=(@messages[type]) ? @messages[type] : []
|
||||
for i in 0...array.length
|
||||
arr[i]=(array[i]) ? array[i] : (arr[i]) ? arr[i] : ""
|
||||
end
|
||||
@messages[type]=arr
|
||||
end
|
||||
|
||||
def self.createHash(type,array)
|
||||
arr=OrderedHash.new
|
||||
for i in 0...array.length
|
||||
if array[i]
|
||||
key=Messages.stringToKey(array[i])
|
||||
arr[key]=array[i]
|
||||
end
|
||||
end
|
||||
return arr
|
||||
end
|
||||
|
||||
def self.addToHash(type,array,hash)
|
||||
if !hash
|
||||
hash=OrderedHash.new
|
||||
end
|
||||
for i in 0...array.length
|
||||
if array[i]
|
||||
key=Messages.stringToKey(array[i])
|
||||
hash[key]=array[i]
|
||||
end
|
||||
end
|
||||
return hash
|
||||
end
|
||||
|
||||
def setMapMessagesAsHash(type,array)
|
||||
@messages=[] if !@messages
|
||||
@messages[0]=[] if !@messages[0]
|
||||
@messages[0][type]=Messages.createHash(type,array)
|
||||
end
|
||||
|
||||
def addMapMessagesAsHash(type,array)
|
||||
@messages=[] if !@messages
|
||||
@messages[0]=[] if !@messages[0]
|
||||
@messages[0][type]=Messages.addToHash(type,array,@messages[0][type])
|
||||
end
|
||||
|
||||
def setMessagesAsHash(type,array)
|
||||
@messages=[] if !@messages
|
||||
@messages[type]=Messages.createHash(type,array)
|
||||
end
|
||||
|
||||
def addMessagesAsHash(type,array)
|
||||
@messages=[] if !@messages
|
||||
@messages[type]=Messages.addToHash(type,array,@messages[type])
|
||||
end
|
||||
|
||||
def saveMessages(filename=nil)
|
||||
filename="Data/messages.dat" if !filename
|
||||
File.open(filename,"wb") { |f| Marshal.dump(@messages,f) }
|
||||
end
|
||||
|
||||
def loadMessageFile(filename)
|
||||
begin
|
||||
pbRgssOpen(filename,"rb") { |f| @messages=Marshal.load(f) }
|
||||
if !@messages.is_a?(Array)
|
||||
@messages=nil
|
||||
raise "Corrupted data"
|
||||
end
|
||||
return @messages
|
||||
rescue
|
||||
@messages=nil
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def set(type,id,value)
|
||||
delayedLoad
|
||||
return if !@messages
|
||||
return if !@messages[type]
|
||||
@messages[type][id]=value
|
||||
end
|
||||
|
||||
def getCount(type)
|
||||
delayedLoad
|
||||
return 0 if !@messages
|
||||
return 0 if !@messages[type]
|
||||
return @messages[type].length
|
||||
end
|
||||
|
||||
def get(type,id)
|
||||
delayedLoad
|
||||
return "" if !@messages
|
||||
return "" if !@messages[type]
|
||||
return "" if !@messages[type][id]
|
||||
return @messages[type][id]
|
||||
end
|
||||
|
||||
def getFromHash(type,key)
|
||||
delayedLoad
|
||||
return key if !@messages || !@messages[type] || !key
|
||||
id=Messages.stringToKey(key)
|
||||
return key if !@messages[type][id]
|
||||
return @messages[type][id]
|
||||
end
|
||||
|
||||
def getFromMapHash(type,key)
|
||||
delayedLoad
|
||||
return key if !@messages
|
||||
return key if !@messages[0]
|
||||
return key if !@messages[0][type] && !@messages[0][0]
|
||||
id=Messages.stringToKey(key)
|
||||
if @messages[0][type] && @messages[0][type][id]
|
||||
return @messages[0][type][id]
|
||||
elsif @messages[0][0] && @messages[0][0][id]
|
||||
return @messages[0][0][id]
|
||||
end
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module MessageTypes
|
||||
# Value 0 is used for common event and map event text
|
||||
Species = 1
|
||||
Kinds = 2
|
||||
Entries = 3
|
||||
FormNames = 4
|
||||
Moves = 5
|
||||
MoveDescriptions = 6
|
||||
Items = 7
|
||||
ItemPlurals = 8
|
||||
ItemDescriptions = 9
|
||||
Abilities = 10
|
||||
AbilityDescs = 11
|
||||
Types = 12
|
||||
TrainerTypes = 13
|
||||
TrainerNames = 14
|
||||
BeginSpeech = 15
|
||||
EndSpeechWin = 16
|
||||
EndSpeechLose = 17
|
||||
RegionNames = 18
|
||||
PlaceNames = 19
|
||||
PlaceDescriptions = 20
|
||||
MapNames = 21
|
||||
PhoneMessages = 22
|
||||
TrainerLoseText = 23
|
||||
ScriptTexts = 24
|
||||
@@messages = Messages.new
|
||||
@@messagesFallback = Messages.new("Data/messages.dat",true)
|
||||
|
||||
def self.stringToKey(str)
|
||||
return Messages.stringToKey(str)
|
||||
end
|
||||
|
||||
def self.normalizeValue(value)
|
||||
return Messages.normalizeValue(value)
|
||||
end
|
||||
|
||||
def self.denormalizeValue(value)
|
||||
Messages.denormalizeValue(value)
|
||||
end
|
||||
|
||||
def self.writeObject(f,msgs,secname)
|
||||
Messages.denormalizeValue(str)
|
||||
end
|
||||
|
||||
def self.extract(outfile)
|
||||
@@messages.extract(outfile)
|
||||
end
|
||||
|
||||
def self.setMessages(type,array)
|
||||
@@messages.setMessages(type,array)
|
||||
end
|
||||
|
||||
def self.addMessages(type,array)
|
||||
@@messages.addMessages(type,array)
|
||||
end
|
||||
|
||||
def self.createHash(type,array)
|
||||
Messages.createHash(type,array)
|
||||
end
|
||||
|
||||
def self.addMapMessagesAsHash(type,array)
|
||||
@@messages.addMapMessagesAsHash(type,array)
|
||||
end
|
||||
|
||||
def self.setMapMessagesAsHash(type,array)
|
||||
@@messages.setMapMessagesAsHash(type,array)
|
||||
end
|
||||
|
||||
def self.addMessagesAsHash(type,array)
|
||||
@@messages.addMessagesAsHash(type,array)
|
||||
end
|
||||
|
||||
def self.setMessagesAsHash(type,array)
|
||||
@@messages.setMessagesAsHash(type,array)
|
||||
end
|
||||
|
||||
def self.saveMessages(filename=nil)
|
||||
@@messages.saveMessages(filename)
|
||||
end
|
||||
|
||||
def self.loadMessageFile(filename)
|
||||
@@messages.loadMessageFile(filename)
|
||||
end
|
||||
|
||||
def self.get(type,id)
|
||||
ret=@@messages.get(type,id)
|
||||
if ret==""
|
||||
ret=@@messagesFallback.get(type,id)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def self.getCount(type)
|
||||
c1=@@messages.getCount(type)
|
||||
c2=@@messagesFallback.getCount(type)
|
||||
return c1>c2 ? c1 : c2
|
||||
end
|
||||
|
||||
def self.getOriginal(type,id)
|
||||
return @@messagesFallback.get(type,id)
|
||||
end
|
||||
|
||||
def self.getFromHash(type,key)
|
||||
@@messages.getFromHash(type,key)
|
||||
end
|
||||
|
||||
def self.getFromMapHash(type,key)
|
||||
@@messages.getFromMapHash(type,key)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def pbLoadMessages(file)
|
||||
return MessageTypes.loadMessageFile(file)
|
||||
end
|
||||
|
||||
def pbGetMessageCount(type)
|
||||
return MessageTypes.getCount(type)
|
||||
end
|
||||
|
||||
def pbGetMessage(type,id)
|
||||
return MessageTypes.get(type,id)
|
||||
end
|
||||
|
||||
def pbGetMessageFromHash(type,id)
|
||||
return MessageTypes.getFromHash(type,id)
|
||||
end
|
||||
|
||||
# Replaces first argument with a localized version and formats the other
|
||||
# parameters by replacing {1}, {2}, etc. with those placeholders.
|
||||
def _INTL(*arg)
|
||||
begin
|
||||
string=MessageTypes.getFromHash(MessageTypes::ScriptTexts,arg[0])
|
||||
rescue
|
||||
string=arg[0]
|
||||
end
|
||||
string=string.clone
|
||||
for i in 1...arg.length
|
||||
string.gsub!(/\{#{i}\}/,"#{arg[i]}")
|
||||
end
|
||||
return string
|
||||
end
|
||||
|
||||
# Replaces first argument with a localized version and formats the other
|
||||
# parameters by replacing {1}, {2}, etc. with those placeholders.
|
||||
# This version acts more like sprintf, supports e.g. {1:d} or {2:s}
|
||||
def _ISPRINTF(*arg)
|
||||
begin
|
||||
string=MessageTypes.getFromHash(MessageTypes::ScriptTexts,arg[0])
|
||||
rescue
|
||||
string=arg[0]
|
||||
end
|
||||
string=string.clone
|
||||
for i in 1...arg.length
|
||||
string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m|
|
||||
next sprintf("%"+$1,arg[i])
|
||||
}
|
||||
end
|
||||
return string
|
||||
end
|
||||
|
||||
def _I(str)
|
||||
return _MAPINTL($game_map.map_id,str)
|
||||
end
|
||||
|
||||
def _MAPINTL(mapid,*arg)
|
||||
string=MessageTypes.getFromMapHash(mapid,arg[0])
|
||||
string=string.clone
|
||||
for i in 1...arg.length
|
||||
string.gsub!(/\{#{i}\}/,"#{arg[i]}")
|
||||
end
|
||||
return string
|
||||
end
|
||||
|
||||
def _MAPISPRINTF(mapid,*arg)
|
||||
string=MessageTypes.getFromMapHash(mapid,arg[0])
|
||||
string=string.clone
|
||||
for i in 1...arg.length
|
||||
string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m|
|
||||
next sprintf("%"+$1,arg[i])
|
||||
}
|
||||
end
|
||||
return string
|
||||
end
|
||||
40
Data/Scripts/006_Events and files/005_PBDebug.rb
Normal file
40
Data/Scripts/006_Events and files/005_PBDebug.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
module PBDebug
|
||||
@@log = []
|
||||
|
||||
def self.logonerr
|
||||
begin
|
||||
yield
|
||||
rescue
|
||||
PBDebug.log("")
|
||||
PBDebug.log("**Exception: #{$!.message}")
|
||||
PBDebug.log("#{$!.backtrace.inspect}")
|
||||
PBDebug.log("")
|
||||
# if $INTERNAL
|
||||
pbPrintException($!)
|
||||
# end
|
||||
PBDebug.flush
|
||||
end
|
||||
end
|
||||
|
||||
def self.flush
|
||||
if $DEBUG && $INTERNAL && @@log.length>0
|
||||
File.open("Data/debuglog.txt", "a+b") { |f| f.write("#{@@log}") }
|
||||
end
|
||||
@@log.clear
|
||||
end
|
||||
|
||||
def self.log(msg)
|
||||
if $DEBUG && $INTERNAL
|
||||
@@log.push("#{msg}\r\n")
|
||||
# if @@log.length>1024
|
||||
PBDebug.flush
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
def self.dump(msg)
|
||||
if $DEBUG && $INTERNAL
|
||||
File.open("Data/dumplog.txt", "a+b") { |f| f.write("#{msg}\r\n") }
|
||||
end
|
||||
end
|
||||
end
|
||||
376
Data/Scripts/007_Audio/001_Audio.rb
Normal file
376
Data/Scripts/007_Audio/001_Audio.rb
Normal file
@@ -0,0 +1,376 @@
|
||||
class Thread
|
||||
def Thread.exclusive
|
||||
_old = Thread.critical
|
||||
begin
|
||||
Thread.critical = true
|
||||
return yield
|
||||
ensure
|
||||
Thread.critical = _old
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def getPlayMusic
|
||||
return MiniRegistry.get(MiniRegistry::HKEY_CURRENT_USER,
|
||||
"SOFTWARE\\Enterbrain\\RGSS","PlayMusic",true)
|
||||
end
|
||||
|
||||
def getPlaySound
|
||||
return MiniRegistry.get(MiniRegistry::HKEY_CURRENT_USER,
|
||||
"SOFTWARE\\Enterbrain\\RGSS","PlaySound",true)
|
||||
end
|
||||
|
||||
|
||||
|
||||
class AudioContext
|
||||
attr_reader :context
|
||||
|
||||
def initialize
|
||||
init = Win32API.new("audio.dll", "AudioContextInitialize", '', 'l')
|
||||
@context=init.call()
|
||||
end
|
||||
|
||||
def dispose
|
||||
if @context!=0
|
||||
init = Win32API.new("audio.dll", "AudioContextFree", 'l', '')
|
||||
init.call(context)
|
||||
@context=0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#####################################
|
||||
# Needed because RGSS doesn't call at_exit procs on exit
|
||||
# Exit is not called when game is reset (using F12)
|
||||
$AtExitProcs=[] if !$AtExitProcs
|
||||
|
||||
def exit(code=0)
|
||||
for p in $AtExitProcs
|
||||
p.call
|
||||
end
|
||||
raise SystemExit.new(code)
|
||||
end
|
||||
|
||||
def at_exit(&block)
|
||||
$AtExitProcs.push(Proc.new(&block))
|
||||
end
|
||||
|
||||
#####################################
|
||||
# Works around a problem with FileTest.exist
|
||||
# if directory contains accent marks
|
||||
def safeExists?(f)
|
||||
ret=false
|
||||
File.open(f,"rb") { ret=true } rescue nil
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
|
||||
module AudioState
|
||||
w32_LL = Win32API.new("kernel32.dll", "LoadLibrary", 'p', 'l') # :nodoc:
|
||||
w32_FL = Win32API.new("kernel32.dll", "FreeLibrary", 'p', 'l')# :nodoc:
|
||||
|
||||
if safeExists?("audio.dll")
|
||||
@handle = w32_LL.call("audio.dll")
|
||||
at_exit { w32_FL.call(@handle) }
|
||||
AudioContextIsActive = Win32API.new("audio.dll","AudioContextIsActive","l","l")# :nodoc:
|
||||
AudioContextPlay = Win32API.new("audio.dll","AudioContextPlay","lpllll","")# :nodoc:
|
||||
AudioContextStop = Win32API.new("audio.dll","AudioContextStop","l","")# :nodoc:
|
||||
AudioContextFadeOut = Win32API.new("audio.dll","AudioContextFadeOut","ll","")# :nodoc:
|
||||
AudioContextGetPosition = Win32API.new("audio.dll","AudioContextGetPosition","l","l")# :nodoc:
|
||||
AudioContextFadeIn = Win32API.new("audio.dll","AudioContextFadeIn","ll","")# :nodoc:
|
||||
AudioContextSetVolume = Win32API.new("audio.dll","AudioContextSetVolume","ll","")# :nodoc:
|
||||
AudioContextSEPlay = Win32API.new("audio.dll","AudioContextSEPlay","lplll","")# :nodoc:
|
||||
if !@MEContext
|
||||
@MEContext=AudioContext.new
|
||||
at_exit { @MEContext.dispose }
|
||||
end
|
||||
if !@BGMContext
|
||||
@BGMContext=AudioContext.new
|
||||
at_exit { @BGMContext.dispose }
|
||||
end
|
||||
if !@BGSContext
|
||||
@BGSContext=AudioContext.new
|
||||
at_exit { @BGSContext.dispose }
|
||||
end
|
||||
if !@SEContext
|
||||
@SEContext=AudioContext.new
|
||||
at_exit { @SEContext.dispose }
|
||||
end
|
||||
else
|
||||
AudioContextIsActive = nil # :nodoc:
|
||||
AudioContextPlay = nil # :nodoc:
|
||||
AudioContextStop = nil # :nodoc:
|
||||
AudioContextFadeOut = nil # :nodoc:
|
||||
AudioContextGetPosition = nil # :nodoc:
|
||||
AudioContextFadeIn = nil # :nodoc:
|
||||
AudioContextSetVolume = nil # :nodoc:
|
||||
AudioContextSEPlay = nil # :nodoc:
|
||||
end
|
||||
|
||||
@channel = nil
|
||||
@bgm = nil
|
||||
@name = ""
|
||||
@pitch = 100
|
||||
@bgmVolume = 100.0
|
||||
@meVolume = 100.0
|
||||
@bgsVolume = 100.0
|
||||
@seVolume = 100.0
|
||||
|
||||
def self.setWaitingBGM(bgm,volume,pitch,position)
|
||||
@waitingBGM=[bgm,volume,pitch,position]
|
||||
end
|
||||
|
||||
def self.bgmActive?
|
||||
return !@BGMContext ? false : (AudioContextIsActive.call(@BGMContext.context)!=0)
|
||||
end
|
||||
|
||||
def self.meActive?
|
||||
return !@MEContext ? false : (AudioContextIsActive.call(@MEContext.context)!=0)
|
||||
end
|
||||
|
||||
def self.waitingBGM; @waitingBGM; end
|
||||
def self.context; @BGMContext ? @BGMContext.context : nil; end
|
||||
def self.meContext; @MEContext ? @MEContext.context : nil; end
|
||||
def self.bgsContext; @BGSContext ? @BGSContext.context : nil; end
|
||||
def self.seContext; @SEContext ? @SEContext.context : nil; end
|
||||
def self.system; @system; end
|
||||
def self.bgm; @bgm; end
|
||||
def self.name; @name; end
|
||||
def self.pitch; @pitch; end
|
||||
def self.volume; @volume; end
|
||||
|
||||
def self.waitingBGM=(value);
|
||||
Thread.exclusive { @waitingBGM=value; }
|
||||
end
|
||||
|
||||
def self.volume=(value); @volume=value; end
|
||||
def self.bgm=(value); @bgm=value; end
|
||||
def self.name=(value); @name=value; end
|
||||
def self.pitch=(value); @pitch=value; end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def Audio_bgm_playing?
|
||||
AudioState.channel!=nil
|
||||
end
|
||||
|
||||
def Audio_bgm_name
|
||||
AudioState.name
|
||||
end
|
||||
|
||||
def Audio_bgm_pitch
|
||||
AudioState.pitch
|
||||
end
|
||||
|
||||
def Audio_bgm_play(name, volume, pitch, position = 0)
|
||||
volume=0 if !getPlayMusic()
|
||||
begin
|
||||
filename = canonicalize(RTP.getAudioPath(name))
|
||||
if AudioState.meActive?
|
||||
AudioState.setWaitingBGM(filename,volume,pitch,position)
|
||||
return
|
||||
end
|
||||
AudioState::AudioContextPlay.call(AudioState.context,filename,volume,pitch,position,1)
|
||||
AudioState.name=filename
|
||||
AudioState.volume=volume
|
||||
AudioState.pitch=pitch
|
||||
rescue Hangup
|
||||
rescue
|
||||
p $!.message,$!.backtrace
|
||||
end
|
||||
end
|
||||
|
||||
def Audio_bgm_fadein(ms)
|
||||
AudioState::AudioContextFadeIn.call(AudioState.context,ms.to_i)
|
||||
end
|
||||
|
||||
def Audio_bgm_fade(ms)
|
||||
AudioState::AudioContextFadeOut.call(AudioState.context,ms.to_i)
|
||||
end
|
||||
|
||||
def Audio_bgm_stop()
|
||||
begin
|
||||
AudioState::AudioContextStop.call(AudioState.context)
|
||||
AudioState.waitingBGM=nil
|
||||
AudioState.name = ""
|
||||
rescue
|
||||
p $!.message,$!.backtrace
|
||||
end
|
||||
end
|
||||
|
||||
def Audio_bgm_get_position
|
||||
return AudioState::AudioContextGetPosition.call(AudioState.context)
|
||||
end
|
||||
|
||||
def Audio_bgm_get_volume
|
||||
return 0 if !AudioState.bgmActive?
|
||||
return AudioState.volume
|
||||
end
|
||||
|
||||
def Audio_bgm_set_volume(volume)
|
||||
return if !AudioState.bgmActive?
|
||||
AudioState.volume = volume * 1.0
|
||||
AudioState::AudioContextSetVolume.call(AudioState.context,volume.to_i)
|
||||
end
|
||||
|
||||
def Audio_me_play(name, volume, pitch, position = 0)
|
||||
volume=0 if !getPlayMusic()
|
||||
begin
|
||||
filename = canonicalize(RTP.getAudioPath(name))
|
||||
if AudioState.bgmActive?
|
||||
bgmPosition=Audio_bgm_get_position
|
||||
AudioState.setWaitingBGM(
|
||||
AudioState.name,
|
||||
AudioState.volume,
|
||||
AudioState.pitch,
|
||||
bgmPosition
|
||||
)
|
||||
AudioState::AudioContextStop.call(AudioState.context)
|
||||
end
|
||||
AudioState::AudioContextPlay.call(AudioState.meContext,filename,
|
||||
volume,pitch,position,0)
|
||||
rescue
|
||||
p $!.message,$!.backtrace
|
||||
end
|
||||
end
|
||||
|
||||
def Audio_me_fade(ms)
|
||||
AudioState::AudioContextFadeOut.call(AudioState.meContext,ms)
|
||||
end
|
||||
|
||||
def Audio_me_stop()
|
||||
AudioState::AudioContextStop.call(AudioState.meContext)
|
||||
end
|
||||
|
||||
def Audio_bgs_play(name, volume, pitch, position = 0)
|
||||
volume=0 if !getPlaySound()
|
||||
begin
|
||||
filename = canonicalize(RTP.getAudioPath(name))
|
||||
AudioState::AudioContextPlay.call(AudioState.bgsContext,filename,
|
||||
volume,pitch,position,0)
|
||||
rescue
|
||||
p $!.message,$!.backtrace
|
||||
end
|
||||
end
|
||||
|
||||
def Audio_bgs_fade(ms)
|
||||
AudioState::AudioContextFadeOut.call(AudioState.bgsContext,ms)
|
||||
end
|
||||
|
||||
def Audio_bgs_stop()
|
||||
AudioState::AudioContextStop.call(AudioState.bgsContext)
|
||||
end
|
||||
|
||||
def Audio_se_play(name, volume, pitch, position = 0)
|
||||
volume=0 if !getPlaySound()
|
||||
begin
|
||||
filename = canonicalize(RTP.getAudioPath(name))
|
||||
AudioState::AudioContextSEPlay.call(AudioState.seContext,filename,
|
||||
volume,pitch,position)
|
||||
rescue
|
||||
p $!.message,$!.backtrace
|
||||
end
|
||||
end
|
||||
|
||||
def Audio_se_stop()
|
||||
AudioState::AudioContextStop.call(AudioState.seContext)
|
||||
end
|
||||
|
||||
|
||||
|
||||
####################################################
|
||||
if safeExists?("audio.dll")
|
||||
module Graphics
|
||||
if !defined?(audiomodule_update)
|
||||
class << self
|
||||
alias audiomodule_update update
|
||||
end
|
||||
end
|
||||
|
||||
def self.update
|
||||
Audio.update
|
||||
audiomodule_update
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module Audio
|
||||
@@musicstate = nil
|
||||
@@soundstate = nil
|
||||
|
||||
def self.update
|
||||
return if Graphics.frame_count%10!=0
|
||||
if AudioState.waitingBGM && !AudioState.meActive?
|
||||
waitbgm=AudioState.waitingBGM
|
||||
AudioState.waitingBGM=nil
|
||||
bgm_play(waitbgm[0],waitbgm[1],waitbgm[2],waitbgm[3])
|
||||
end
|
||||
end
|
||||
|
||||
def self.bgm_play(name,volume=80,pitch=100,position=nil)
|
||||
begin
|
||||
if position==nil || position==0
|
||||
Audio_bgm_play(name,volume,pitch,0)
|
||||
else
|
||||
Audio_bgm_play(name,volume,pitch,position)
|
||||
Audio_bgm_fadein(500)
|
||||
end
|
||||
rescue Hangup
|
||||
bgm_play(name,volume,pitch,position)
|
||||
end
|
||||
end
|
||||
|
||||
def self.bgm_fade(ms)
|
||||
Audio_bgm_fade(ms)
|
||||
end
|
||||
|
||||
def self.bgm_stop
|
||||
Audio_bgm_stop()
|
||||
end
|
||||
|
||||
def self.bgm_position
|
||||
return Audio_bgm_get_position
|
||||
end
|
||||
|
||||
def self.me_play(name,volume=80,pitch=100)
|
||||
Audio_me_play(name,volume,pitch,0)
|
||||
end
|
||||
|
||||
def self.me_fade(ms)
|
||||
Audio_me_fade(ms)
|
||||
end
|
||||
|
||||
def self.me_stop
|
||||
Audio_me_stop()
|
||||
end
|
||||
|
||||
def self.bgs_play(name,volume=80,pitch=100)
|
||||
Audio_bgs_play(name,volume,pitch,0)
|
||||
end
|
||||
|
||||
def self.bgs_fade(ms)
|
||||
Audio_bgs_fade(ms)
|
||||
end
|
||||
|
||||
def self.bgs_stop
|
||||
Audio_bgs_stop()
|
||||
end
|
||||
|
||||
=begin
|
||||
def self.se_play(name,volume=80,pitch=100)
|
||||
Audio_se_play(name,volume,pitch,0)
|
||||
end
|
||||
|
||||
def self.se_stop
|
||||
Audio_se_stop()
|
||||
end
|
||||
=end
|
||||
end
|
||||
end # safeExists?("audio.dll")
|
||||
292
Data/Scripts/007_Audio/002_AudioPlay.rb
Normal file
292
Data/Scripts/007_Audio/002_AudioPlay.rb
Normal file
@@ -0,0 +1,292 @@
|
||||
def pbStringToAudioFile(str)
|
||||
if str[/^(.*)\:\s*(\d+)\s*\:\s*(\d+)\s*$/] # Of the format "XXX: ###: ###"
|
||||
file = $1
|
||||
volume = $2.to_i
|
||||
pitch = $3.to_i
|
||||
return RPG::AudioFile.new(file,volume,pitch)
|
||||
elsif str[/^(.*)\:\s*(\d+)\s*$/] # Of the format "XXX: ###"
|
||||
file = $1
|
||||
volume = $2.to_i
|
||||
return RPG::AudioFile.new(file,volume,100)
|
||||
else
|
||||
return RPG::AudioFile.new(str,100,100)
|
||||
end
|
||||
end
|
||||
|
||||
# Converts an object to an audio file.
|
||||
# str -- Either a string showing the filename or an RPG::AudioFile object.
|
||||
# Possible formats for _str_:
|
||||
# filename volume and pitch 100
|
||||
# filename:volume pitch 100
|
||||
# filename:volume:pitch
|
||||
# volume -- Volume of the file, up to 100
|
||||
# pitch -- Pitch of the file, normally 100
|
||||
def pbResolveAudioFile(str,volume=nil,pitch=nil)
|
||||
if str.is_a?(String)
|
||||
str = pbStringToAudioFile(str)
|
||||
str.volume = volume || 100
|
||||
str.pitch = pitch || 100
|
||||
end
|
||||
if str.is_a?(RPG::AudioFile)
|
||||
if volume || pitch
|
||||
return RPG::AudioFile.new(str.name,volume || str.volume || 100 ,
|
||||
pitch || str.pitch || 100)
|
||||
else
|
||||
return str
|
||||
end
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
# Plays a BGM file.
|
||||
# param -- Either a string showing the filename
|
||||
# (relative to Audio/BGM/) or an RPG::AudioFile object.
|
||||
# Possible formats for _param_:
|
||||
# filename volume and pitch 100
|
||||
# filename:volume pitch 100
|
||||
# filename:volume:pitch
|
||||
# volume -- Volume of the file, up to 100
|
||||
# pitch -- Pitch of the file, normally 100
|
||||
def pbBGMPlay(param,volume=nil,pitch=nil)
|
||||
return if !param
|
||||
param=pbResolveAudioFile(param,volume,pitch)
|
||||
if param.name && param.name!=""
|
||||
if $game_system && $game_system.respond_to?("bgm_play")
|
||||
$game_system.bgm_play(param)
|
||||
return
|
||||
elsif (RPG.const_defined?(:BGM) rescue false)
|
||||
b=RPG::BGM.new(param.name,param.volume,param.pitch)
|
||||
if b && b.respond_to?("play")
|
||||
b.play
|
||||
return
|
||||
end
|
||||
end
|
||||
Audio.bgm_play(canonicalize("Audio/BGM/"+param.name),param.volume,param.pitch)
|
||||
end
|
||||
end
|
||||
|
||||
# Fades out or stops BGM playback. 'x' is the time in seconds to fade out.
|
||||
def pbBGMFade(x=0.0); pbBGMStop(x);end
|
||||
|
||||
# Fades out or stops BGM playback. 'x' is the time in seconds to fade out.
|
||||
def pbBGMStop(timeInSeconds=0.0)
|
||||
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("bgm_fade")
|
||||
$game_system.bgm_fade(timeInSeconds)
|
||||
return
|
||||
elsif $game_system && $game_system.respond_to?("bgm_stop")
|
||||
$game_system.bgm_stop
|
||||
return
|
||||
elsif (RPG.const_defined?(:BGM) rescue false)
|
||||
begin
|
||||
(timeInSeconds>0.0) ? RPG::BGM.fade((timeInSeconds*1000).floor) : RPG::BGM.stop
|
||||
return
|
||||
rescue
|
||||
end
|
||||
end
|
||||
(timeInSeconds>0.0) ? Audio.bgm_fade((timeInSeconds*1000).floor) : Audio.bgm_stop
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
# Plays an ME file.
|
||||
# param -- Either a string showing the filename
|
||||
# (relative to Audio/ME/) or an RPG::AudioFile object.
|
||||
# Possible formats for _param_:
|
||||
# filename volume and pitch 100
|
||||
# filename:volume pitch 100
|
||||
# filename:volume:pitch
|
||||
# volume -- Volume of the file, up to 100
|
||||
# pitch -- Pitch of the file, normally 100
|
||||
def pbMEPlay(param,volume=nil,pitch=nil)
|
||||
return if !param
|
||||
param=pbResolveAudioFile(param,volume,pitch)
|
||||
if param.name && param.name!=""
|
||||
if $game_system && $game_system.respond_to?("me_play")
|
||||
$game_system.me_play(param)
|
||||
return
|
||||
elsif (RPG.const_defined?(:ME) rescue false)
|
||||
b=RPG::ME.new(param.name,param.volume,param.pitch)
|
||||
if b && b.respond_to?("play")
|
||||
b.play; return
|
||||
end
|
||||
end
|
||||
Audio.me_play(canonicalize("Audio/ME/"+param.name),param.volume,param.pitch)
|
||||
end
|
||||
end
|
||||
|
||||
# Fades out or stops ME playback. 'x' is the time in seconds to fade out.
|
||||
def pbMEFade(x=0.0); pbMEStop(x);end
|
||||
|
||||
# Fades out or stops ME playback. 'x' is the time in seconds to fade out.
|
||||
def pbMEStop(timeInSeconds=0.0)
|
||||
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("me_fade")
|
||||
$game_system.me_fade(timeInSeconds)
|
||||
return
|
||||
elsif $game_system && $game_system.respond_to?("me_stop")
|
||||
$game_system.me_stop(nil)
|
||||
return
|
||||
elsif (RPG.const_defined?(:ME) rescue false)
|
||||
begin
|
||||
(timeInSeconds>0.0) ? RPG::ME.fade((timeInSeconds*1000).floor) : RPG::ME.stop
|
||||
return
|
||||
rescue
|
||||
end
|
||||
end
|
||||
(timeInSeconds>0.0) ? Audio.me_fade((timeInSeconds*1000).floor) : Audio.me_stop
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
# Plays a BGS file.
|
||||
# param -- Either a string showing the filename
|
||||
# (relative to Audio/BGS/) or an RPG::AudioFile object.
|
||||
# Possible formats for _param_:
|
||||
# filename volume and pitch 100
|
||||
# filename:volume pitch 100
|
||||
# filename:volume:pitch
|
||||
# volume -- Volume of the file, up to 100
|
||||
# pitch -- Pitch of the file, normally 100
|
||||
def pbBGSPlay(param,volume=nil,pitch=nil)
|
||||
return if !param
|
||||
param=pbResolveAudioFile(param,volume,pitch)
|
||||
if param.name && param.name!=""
|
||||
if $game_system && $game_system.respond_to?("bgs_play")
|
||||
$game_system.bgs_play(param)
|
||||
return
|
||||
elsif (RPG.const_defined?(:BGS) rescue false)
|
||||
b=RPG::BGS.new(param.name,param.volume,param.pitch)
|
||||
if b && b.respond_to?("play")
|
||||
b.play; return
|
||||
end
|
||||
end
|
||||
Audio.bgs_play(canonicalize("Audio/BGS/"+param.name),param.volume,param.pitch)
|
||||
end
|
||||
end
|
||||
|
||||
# Fades out or stops BGS playback. 'x' is the time in seconds to fade out.
|
||||
def pbBGSFade(x=0.0); pbBGSStop(x);end
|
||||
|
||||
# Fades out or stops BGS playback. 'x' is the time in seconds to fade out.
|
||||
def pbBGSStop(timeInSeconds=0.0)
|
||||
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("bgs_fade")
|
||||
$game_system.bgs_fade(timeInSeconds)
|
||||
return
|
||||
elsif $game_system && $game_system.respond_to?("bgs_play")
|
||||
$game_system.bgs_play(nil)
|
||||
return
|
||||
elsif (RPG.const_defined?(:BGS) rescue false)
|
||||
begin
|
||||
(timeInSeconds>0.0) ? RPG::BGS.fade((timeInSeconds*1000).floor) : RPG::BGS.stop
|
||||
return
|
||||
rescue
|
||||
end
|
||||
end
|
||||
(timeInSeconds>0.0) ? Audio.bgs_fade((timeInSeconds*1000).floor) : Audio.bgs_stop
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
# Plays an SE file.
|
||||
# param -- Either a string showing the filename
|
||||
# (relative to Audio/SE/) or an RPG::AudioFile object.
|
||||
# Possible formats for _param_:
|
||||
# filename volume and pitch 100
|
||||
# filename:volume pitch 100
|
||||
# filename:volume:pitch
|
||||
# volume -- Volume of the file, up to 100
|
||||
# pitch -- Pitch of the file, normally 100
|
||||
def pbSEPlay(param,volume=nil,pitch=nil)
|
||||
return if !param
|
||||
param = pbResolveAudioFile(param,volume,pitch)
|
||||
if param.name && param.name!=""
|
||||
if $game_system && $game_system.respond_to?("se_play")
|
||||
$game_system.se_play(param)
|
||||
return
|
||||
end
|
||||
if (RPG.const_defined?(:SE) rescue false)
|
||||
b = RPG::SE.new(param.name,param.volume,param.pitch)
|
||||
if b && b.respond_to?("play")
|
||||
b.play
|
||||
return
|
||||
end
|
||||
end
|
||||
Audio.se_play(canonicalize("Audio/SE/"+param.name),param.volume,param.pitch)
|
||||
end
|
||||
end
|
||||
|
||||
# Stops SE playback.
|
||||
def pbSEFade(x=0.0); pbSEStop(x);end
|
||||
|
||||
# Stops SE playback.
|
||||
def pbSEStop(timeInSeconds=0.0)
|
||||
if $game_system
|
||||
$game_system.se_stop
|
||||
elsif (RPG.const_defined?(:SE) rescue false)
|
||||
RPG::SE.stop rescue nil
|
||||
else
|
||||
Audio.se_stop
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
||||
# Plays a sound effect that plays when the player moves the cursor.
|
||||
def pbPlayCursorSE
|
||||
if $data_system && $data_system.respond_to?("cursor_se") &&
|
||||
$data_system.cursor_se && $data_system.cursor_se.name!=""
|
||||
pbSEPlay($data_system.cursor_se)
|
||||
elsif $data_system && $data_system.respond_to?("sounds") &&
|
||||
$data_system.sounds && $data_system.sounds[0] && $data_system.sounds[0].name!=""
|
||||
pbSEPlay($data_system.sounds[0])
|
||||
elsif FileTest.audio_exist?("Audio/SE/GUI sel cursor")
|
||||
pbSEPlay("GUI sel cursor",80)
|
||||
end
|
||||
end
|
||||
|
||||
# Plays a sound effect that plays when a decision is confirmed or a choice is made.
|
||||
def pbPlayDecisionSE
|
||||
if $data_system && $data_system.respond_to?("decision_se") &&
|
||||
$data_system.decision_se && $data_system.decision_se.name!=""
|
||||
pbSEPlay($data_system.decision_se)
|
||||
elsif $data_system && $data_system.respond_to?("sounds") &&
|
||||
$data_system.sounds && $data_system.sounds[1] && $data_system.sounds[1].name!=""
|
||||
pbSEPlay($data_system.sounds[1])
|
||||
elsif FileTest.audio_exist?("Audio/SE/GUI sel decision")
|
||||
pbSEPlay("GUI sel decision",80)
|
||||
end
|
||||
end
|
||||
|
||||
# Plays a sound effect that plays when a choice is canceled.
|
||||
def pbPlayCancelSE
|
||||
if $data_system && $data_system.respond_to?("cancel_se") &&
|
||||
$data_system.cancel_se && $data_system.cancel_se.name!=""
|
||||
pbSEPlay($data_system.cancel_se)
|
||||
elsif $data_system && $data_system.respond_to?("sounds") &&
|
||||
$data_system.sounds && $data_system.sounds[2] && $data_system.sounds[2].name!=""
|
||||
pbSEPlay($data_system.sounds[2])
|
||||
elsif FileTest.audio_exist?("Audio/SE/GUI sel cancel")
|
||||
pbSEPlay("GUI sel cancel",80)
|
||||
end
|
||||
end
|
||||
|
||||
# Plays a buzzer sound effect.
|
||||
def pbPlayBuzzerSE
|
||||
if $data_system && $data_system.respond_to?("buzzer_se") &&
|
||||
$data_system.buzzer_se && $data_system.buzzer_se.name!=""
|
||||
pbSEPlay($data_system.buzzer_se)
|
||||
elsif $data_system && $data_system.respond_to?("sounds") &&
|
||||
$data_system.sounds && $data_system.sounds[3] && $data_system.sounds[3].name!=""
|
||||
pbSEPlay($data_system.sounds[3])
|
||||
elsif FileTest.audio_exist?("Audio/SE/GUI sel buzzer")
|
||||
pbSEPlay("GUI sel buzzer",80)
|
||||
end
|
||||
end
|
||||
|
||||
# Plays a sound effect that plays when the player moves the cursor.
|
||||
def pbPlayCloseMenuSE
|
||||
if FileTest.audio_exist?("Audio/SE/GUI menu close")
|
||||
pbSEPlay("GUI menu close",80)
|
||||
end
|
||||
end
|
||||
1350
Data/Scripts/007_Audio/003_AudioUtilities.rb
Normal file
1350
Data/Scripts/007_Audio/003_AudioUtilities.rb
Normal file
File diff suppressed because it is too large
Load Diff
657
Data/Scripts/008_Objects and windows/001_FileTests.rb
Normal file
657
Data/Scripts/008_Objects and windows/001_FileTests.rb
Normal file
@@ -0,0 +1,657 @@
|
||||
#===============================================================================
|
||||
# Checking for files and directories
|
||||
#===============================================================================
|
||||
# Works around a problem with FileTest.directory if directory contains accent marks
|
||||
def safeIsDirectory?(f)
|
||||
ret = false
|
||||
Dir.chdir(f) { ret = true } rescue nil
|
||||
return ret
|
||||
end
|
||||
|
||||
# Works around a problem with FileTest.exist if path contains accent marks
|
||||
def safeExists?(f)
|
||||
return FileTest.exist?(f) if f[/\A[\x20-\x7E]*\z/]
|
||||
ret = false
|
||||
begin
|
||||
File.open(f,"rb") { ret = true }
|
||||
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
|
||||
ret = false
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
# Similar to "Dir.glob", but designed to work around a problem with accessing
|
||||
# files if a path contains accent marks.
|
||||
# "dir" is the directory path, "wildcard" is the filename pattern to match.
|
||||
def safeGlob(dir,wildcard)
|
||||
ret = []
|
||||
afterChdir = false
|
||||
begin
|
||||
Dir.chdir(dir) {
|
||||
afterChdir = true
|
||||
Dir.glob(wildcard) { |f| ret.push(dir+"/"+f) }
|
||||
}
|
||||
rescue Errno::ENOENT
|
||||
raise if afterChdir
|
||||
end
|
||||
if block_given?
|
||||
ret.each { |f| yield(f) }
|
||||
end
|
||||
return (block_given?) ? nil : ret
|
||||
end
|
||||
|
||||
# Finds the real path for an image file. This includes paths in encrypted
|
||||
# archives. Returns nil if the path can't be found.
|
||||
def pbResolveBitmap(x)
|
||||
return nil if !x
|
||||
noext = x.gsub(/\.(bmp|png|gif|jpg|jpeg)$/,"")
|
||||
filename = nil
|
||||
# RTP.eachPathFor(x) { |path|
|
||||
# filename = pbTryString(path) if !filename
|
||||
# filename = pbTryString(path+".gif") if !filename
|
||||
# }
|
||||
RTP.eachPathFor(noext) { |path|
|
||||
filename = pbTryString(path+".png") if !filename
|
||||
filename = pbTryString(path+".gif") if !filename
|
||||
# filename = pbTryString(path+".jpg") if !filename
|
||||
# filename = pbTryString(path+".jpeg") if !filename
|
||||
# filename = pbTryString(path+".bmp") if !filename
|
||||
}
|
||||
return filename
|
||||
end
|
||||
|
||||
# Finds the real path for an image file. This includes paths in encrypted
|
||||
# archives. Returns _x_ if the path can't be found.
|
||||
def pbBitmapName(x)
|
||||
ret = pbResolveBitmap(x)
|
||||
return (ret) ? ret : x
|
||||
end
|
||||
|
||||
def getUnicodeString(addr)
|
||||
return "" if addr==0
|
||||
rtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
|
||||
ret = ""
|
||||
data = "xx"
|
||||
index = (addr.is_a?(String)) ? 0 : addr
|
||||
loop do
|
||||
if addr.is_a?(String)
|
||||
data = addr[index,2]
|
||||
else
|
||||
rtlMoveMemory_pi.call(data, index, 2)
|
||||
end
|
||||
codepoint = data.unpack("v")[0]
|
||||
break if codepoint==0
|
||||
index += 2
|
||||
if codepoint<=0x7F
|
||||
ret += codepoint.chr
|
||||
elsif codepoint<=0x7FF
|
||||
ret += (0xC0|((codepoint>>6)&0x1F)).chr
|
||||
ret += (0x80|(codepoint &0x3F)).chr
|
||||
elsif codepoint<=0xFFFF
|
||||
ret += (0xE0|((codepoint>>12)&0x0F)).chr
|
||||
ret += (0x80|((codepoint>>6)&0x3F)).chr
|
||||
ret += (0x80|(codepoint &0x3F)).chr
|
||||
elsif codepoint<=0x10FFFF
|
||||
ret += (0xF0|((codepoint>>18)&0x07)).chr
|
||||
ret += (0x80|((codepoint>>12)&0x3F)).chr
|
||||
ret += (0x80|((codepoint>>6)&0x3F)).chr
|
||||
ret += (0x80|(codepoint &0x3F)).chr
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def getUnicodeStringFromAnsi(addr)
|
||||
return "" if addr==0
|
||||
rtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
|
||||
ret = ""
|
||||
data = "x"
|
||||
index = (addr.is_a?(String)) ? 0 : addr
|
||||
loop do
|
||||
if addr.is_a?(String)
|
||||
data = addr[index,1]
|
||||
else
|
||||
rtlMoveMemory_pi.call(data, index, 1)
|
||||
end
|
||||
index += 1
|
||||
codepoint = data.unpack("C")[0]
|
||||
break if codepoint==0 || !codepoint
|
||||
break if codepoint==0
|
||||
if codepoint<=0x7F
|
||||
ret += codepoint.chr
|
||||
else
|
||||
ret += (0xC0|((codepoint>>6)&0x1F)).chr
|
||||
ret += (0x80|(codepoint &0x3F)).chr
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def getKnownFolder(guid)
|
||||
packedGuid = guid.pack("VvvC*")
|
||||
shGetKnownFolderPath = Win32API.new("shell32.dll","SHGetKnownFolderPath","pllp","i") rescue nil
|
||||
coTaskMemFree = Win32API.new("ole32.dll","CoTaskMemFree","i","") rescue nil
|
||||
return "" if !(shGetKnownFolderPath && coTaskMemFree)
|
||||
path = "\0"*4
|
||||
ret = shGetKnownFolderPath.call(packedGuid,0,0,path)
|
||||
path = path.unpack("V")[0]
|
||||
ret = getUnicodeString(path)
|
||||
coTaskMemFree.call(path)
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
|
||||
module RTP
|
||||
@rtpPaths = nil
|
||||
|
||||
def self.exists?(filename,extensions=[])
|
||||
return false if !filename || filename==""
|
||||
eachPathFor(filename) { |path|
|
||||
return true if safeExists?(path)
|
||||
for ext in extensions
|
||||
return true if safeExists?(path+ext)
|
||||
end
|
||||
}
|
||||
return false
|
||||
end
|
||||
|
||||
def self.getImagePath(filename)
|
||||
return self.getPath(filename,["",".png",".gif"]) # ".jpg",".bmp",".jpeg"
|
||||
end
|
||||
|
||||
def self.getAudioPath(filename)
|
||||
return self.getPath(filename,["",".mp3",".wav",".wma",".mid",".ogg",".midi"])
|
||||
end
|
||||
|
||||
def self.getPath(filename,extensions=[])
|
||||
return filename if !filename || filename==""
|
||||
eachPathFor(filename) { |path|
|
||||
return path if safeExists?(path)
|
||||
for ext in extensions
|
||||
file = path+ext
|
||||
return file if safeExists?(file)
|
||||
end
|
||||
}
|
||||
return filename
|
||||
end
|
||||
|
||||
# Gets the absolute RGSS paths for the given file name
|
||||
def self.eachPathFor(filename)
|
||||
return if !filename
|
||||
if filename[/^[A-Za-z]\:[\/\\]/] || filename[/^[\/\\]/]
|
||||
# filename is already absolute
|
||||
yield filename
|
||||
else
|
||||
# relative path
|
||||
RTP.eachPath { |path|
|
||||
if path=="./"
|
||||
yield filename
|
||||
else
|
||||
yield path+filename
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Gets all RGSS search paths
|
||||
def self.eachPath
|
||||
# XXX: Use "." instead of Dir.pwd because of problems retrieving files if
|
||||
# the current directory contains an accent mark
|
||||
yield ".".gsub(/[\/\\]/,"/").gsub(/[\/\\]$/,"")+"/"
|
||||
if !@rtpPaths
|
||||
tmp = Sprite.new
|
||||
isRgss2 = tmp.respond_to?("wave_amp")
|
||||
tmp.dispose
|
||||
@rtpPaths = []
|
||||
if isRgss2
|
||||
rtp = getGameIniValue("Game","RTP")
|
||||
if rtp!=""
|
||||
rtp = MiniRegistry.get(MiniRegistry::HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Enterbrain\\RGSS2\\RTP",rtp,nil)
|
||||
if rtp && safeIsDirectory?(rtp)
|
||||
@rtpPaths.push(rtp.sub(/[\/\\]$/,"")+"/")
|
||||
end
|
||||
end
|
||||
else
|
||||
%w( RTP1 RTP2 RTP3 ).each { |v|
|
||||
rtp = getGameIniValue("Game",v)
|
||||
if rtp!=""
|
||||
rtp = MiniRegistry.get(MiniRegistry::HKEY_LOCAL_MACHINE,
|
||||
"SOFTWARE\\Enterbrain\\RGSS\\RTP",rtp,nil)
|
||||
if rtp && safeIsDirectory?(rtp)
|
||||
@rtpPaths.push(rtp.sub(/[\/\\]$/,"")+"/")
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
@rtpPaths.each { |x| yield x }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@folder = nil
|
||||
|
||||
def self.getGameIniValue(section,key)
|
||||
val = "\0"*256
|
||||
gps = Win32API.new('kernel32', 'GetPrivateProfileString',%w(p p p p l p), 'l')
|
||||
gps.call(section, key, "", val, 256, ".\\Game.ini")
|
||||
val.delete!("\0")
|
||||
return val
|
||||
end
|
||||
|
||||
def self.isDirWritable(dir)
|
||||
return false if !dir || dir==""
|
||||
loop do
|
||||
name = dir.gsub(/[\/\\]$/,"")+"/writetest"
|
||||
for i in 0...12
|
||||
name += sprintf("%02X",rand(256))
|
||||
end
|
||||
name += ".tmp"
|
||||
if !safeExists?(name)
|
||||
retval = false
|
||||
begin
|
||||
File.open(name,"wb") { retval = true }
|
||||
rescue Errno::EINVAL, Errno::EACCES, Errno::ENOENT
|
||||
ensure
|
||||
File.delete(name) rescue nil
|
||||
end
|
||||
return retval
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.ensureGameDir(dir)
|
||||
title = RTP.getGameIniValue("Game","Title")
|
||||
title = "RGSS Game" if title==""
|
||||
title = title.gsub(/[^\w ]/,"_")
|
||||
newdir = dir.gsub(/[\/\\]$/,"")+"/"
|
||||
# Convert to UTF-8 because of ANSI function
|
||||
newdir += getUnicodeStringFromAnsi(title)
|
||||
Dir.mkdir(newdir) rescue nil
|
||||
ret = safeIsDirectory?(newdir) ? newdir : dir
|
||||
return ret
|
||||
end
|
||||
|
||||
def self.getSaveFileName(fileName)
|
||||
return getSaveFolder().gsub(/[\/\\]$/,"")+"/"+fileName
|
||||
end
|
||||
|
||||
def self.getSaveFolder
|
||||
if !@@folder
|
||||
# XXX: Use "." instead of Dir.pwd because of problems retrieving files if
|
||||
# the current directory contains an accent mark
|
||||
pwd = "."
|
||||
# Get the known folder path for saved games
|
||||
savedGames = getKnownFolder([
|
||||
0x4c5c32ff,0xbb9d,0x43b0,0xb5,0xb4,0x2d,0x72,0xe5,0x4e,0xaa,0xa4])
|
||||
if savedGames && savedGames!="" && isDirWritable(savedGames)
|
||||
pwd = ensureGameDir(savedGames)
|
||||
end
|
||||
if isDirWritable(pwd)
|
||||
@@folder = pwd
|
||||
else
|
||||
appdata = ENV["LOCALAPPDATA"]
|
||||
if isDirWritable(appdata)
|
||||
appdata = ensureGameDir(appdata)
|
||||
else
|
||||
appdata = ENV["APPDATA"]
|
||||
if isDirWritable(appdata)
|
||||
appdata = ensureGameDir(appdata)
|
||||
elsif isDirWritable(pwd)
|
||||
appdata = pwd
|
||||
else
|
||||
appdata = "."
|
||||
end
|
||||
end
|
||||
@@folder = appdata
|
||||
end
|
||||
end
|
||||
return @@folder
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module FileTest
|
||||
Image_ext = ['.bmp', '.png', '.jpg', '.jpeg', '.gif']
|
||||
Audio_ext = ['.mp3', '.mid', '.midi', '.ogg', '.wav', '.wma']
|
||||
|
||||
def self.audio_exist?(filename)
|
||||
return RTP.exists?(filename,Audio_ext)
|
||||
end
|
||||
|
||||
def self.image_exist?(filename)
|
||||
return RTP.exists?(filename,Image_ext)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# Used to determine whether a data file exists (rather than a graphics or
|
||||
# audio file). Doesn't check RTP, but does check encrypted archives.
|
||||
def pbRgssExists?(filename)
|
||||
filename = canonicalize(filename)
|
||||
if safeExists?("./Game.rgssad") || safeExists?("./Game.rgss2a")
|
||||
return pbGetFileChar(filename)!=nil
|
||||
else
|
||||
return safeExists?(filename)
|
||||
end
|
||||
end
|
||||
|
||||
# Opens an IO, even if the file is in an encrypted archive.
|
||||
# Doesn't check RTP for the file.
|
||||
def pbRgssOpen(file,mode=nil)
|
||||
#File.open("debug.txt","ab") { |fw| fw.write([file,mode,Time.now.to_f].inspect+"\r\n") }
|
||||
if !safeExists?("./Game.rgssad") && !safeExists?("./Game.rgss2a")
|
||||
if block_given?
|
||||
File.open(file,mode) { |f| yield f }
|
||||
return nil
|
||||
else
|
||||
return File.open(file,mode)
|
||||
end
|
||||
end
|
||||
file = canonicalize(file)
|
||||
Marshal.neverload = true
|
||||
begin
|
||||
str = load_data(file)
|
||||
ensure
|
||||
Marshal.neverload = false
|
||||
end
|
||||
if block_given?
|
||||
StringInput.open(str) { |f| yield f }
|
||||
return nil
|
||||
else
|
||||
return StringInput.open(str)
|
||||
end
|
||||
end
|
||||
|
||||
# Gets at least the first byte of a file. Doesn't check RTP, but does check
|
||||
# encrypted archives.
|
||||
def pbGetFileChar(file)
|
||||
file = canonicalize(file)
|
||||
if !safeExists?("./Game.rgssad") && !safeExists?("./Game.rgss2a")
|
||||
return nil if !safeExists?(file)
|
||||
begin
|
||||
File.open(file,"rb") { |f| return f.read(1) } # read one byte
|
||||
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
|
||||
return nil
|
||||
end
|
||||
end
|
||||
Marshal.neverload = true
|
||||
str = nil
|
||||
begin
|
||||
str = load_data(file)
|
||||
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, RGSSError
|
||||
str = nil
|
||||
ensure
|
||||
Marshal.neverload = false
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
def pbTryString(x)
|
||||
ret = pbGetFileChar(x)
|
||||
return (ret!=nil && ret!="") ? x : nil
|
||||
end
|
||||
|
||||
# Gets the contents of a file. Doesn't check RTP, but does check
|
||||
# encrypted archives.
|
||||
def pbGetFileString(file)
|
||||
file = canonicalize(file)
|
||||
if !(safeExists?("./Game.rgssad") || safeExists?("./Game.rgss2a"))
|
||||
return nil if !safeExists?(file)
|
||||
begin
|
||||
File.open(file,"rb") { |f| return f.read } # read all data
|
||||
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
|
||||
return nil
|
||||
end
|
||||
end
|
||||
Marshal.neverload = true
|
||||
str = nil
|
||||
begin
|
||||
str = load_data(file)
|
||||
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, RGSSError
|
||||
str = nil
|
||||
ensure
|
||||
Marshal.neverload = false
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
#
|
||||
#===============================================================================
|
||||
module MiniRegistry
|
||||
HKEY_CLASSES_ROOT = 0x80000000
|
||||
HKEY_CURRENT_USER = 0x80000001
|
||||
HKEY_LOCAL_MACHINE = 0x80000002
|
||||
HKEY_USERS = 0x80000003
|
||||
FormatMessageA = Win32API.new("kernel32","FormatMessageA","LPLLPLP","L")
|
||||
RegOpenKeyExA = Win32API.new("advapi32","RegOpenKeyExA","LPLLP","L")
|
||||
RegCloseKey = Win32API.new("advapi32","RegCloseKey","L","L")
|
||||
RegQueryValueExA = Win32API.new("advapi32","RegQueryValueExA","LPLPPP","L")
|
||||
|
||||
def self.open(hkey,subkey,bit64=false)
|
||||
key = 0.chr*4
|
||||
flag = bit64 ? 0x20119 : 0x20019
|
||||
rg = RegOpenKeyExA.call(hkey, subkey, 0, flag, key)
|
||||
return nil if rg!=0
|
||||
key = key.unpack("V")[0]
|
||||
if block_given?
|
||||
begin
|
||||
yield(key)
|
||||
ensure
|
||||
check(RegCloseKey.call(key))
|
||||
end
|
||||
else
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
def self.close(hkey); check(RegCloseKey.call(hkey)) if hkey; end
|
||||
|
||||
def self.get(hkey,subkey,name,defaultValue=nil,bit64=false)
|
||||
self.open(hkey,subkey,bit64) { |key|
|
||||
return self.read(key,name) rescue defaultValue
|
||||
}
|
||||
return defaultValue
|
||||
end
|
||||
|
||||
def self.read(hkey,name)
|
||||
hkey = 0 if !hkey
|
||||
type = 0.chr*4
|
||||
size = 0.chr*4
|
||||
check(RegQueryValueExA.call(hkey,name,0,type,0,size))
|
||||
data = " "*size.unpack("V")[0]
|
||||
check(RegQueryValueExA.call(hkey,name,0,type,data,size))
|
||||
type = type.unpack("V")[0]
|
||||
data = data[0,size.unpack("V")[0]]
|
||||
case type
|
||||
when 1; return data.chop # REG_SZ
|
||||
when 2; return data.gsub(/%([^%]+)%/) { ENV[$1] || $& } # REG_EXPAND_SZ
|
||||
when 3; return data # REG_BINARY
|
||||
when 4; return data.unpack("V")[0] # REG_DWORD
|
||||
when 5; return data.unpack("V")[0] # REG_DWORD_BIG_ENDIAN
|
||||
when 11; qw = data.unpack("VV"); return (data[1]<<32|data[0]) # REG_QWORD
|
||||
else; raise "Type #{type} not supported."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.check(code)
|
||||
if code!=0
|
||||
msg = "\0"*1024
|
||||
len = FormatMessageA.call(0x1200, 0, code, 0, msg, 1024, 0)
|
||||
raise msg[0, len].tr("\r", '').chomp
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class StringInput
|
||||
include Enumerable
|
||||
|
||||
class << self
|
||||
def new( str )
|
||||
if block_given?
|
||||
begin
|
||||
f = super
|
||||
yield f
|
||||
ensure
|
||||
f.close if f
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
alias open new
|
||||
end
|
||||
|
||||
def initialize( str )
|
||||
@string = str
|
||||
@pos = 0
|
||||
@closed = false
|
||||
@lineno = 0
|
||||
end
|
||||
|
||||
attr_reader :lineno,:string
|
||||
|
||||
def inspect
|
||||
return "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@string[0,30].inspect}>"
|
||||
end
|
||||
|
||||
def close
|
||||
raise IOError, 'closed stream' if @closed
|
||||
@pos = nil
|
||||
@closed = true
|
||||
end
|
||||
|
||||
def closed?; @closed; end
|
||||
|
||||
def pos
|
||||
raise IOError, 'closed stream' if @closed
|
||||
[@pos, @string.size].min
|
||||
end
|
||||
|
||||
alias tell pos
|
||||
|
||||
def rewind; seek(0); end
|
||||
|
||||
def pos=(value); seek(value); end
|
||||
|
||||
def seek(offset, whence=IO::SEEK_SET)
|
||||
raise IOError, 'closed stream' if @closed
|
||||
case whence
|
||||
when IO::SEEK_SET; @pos = offset
|
||||
when IO::SEEK_CUR; @pos += offset
|
||||
when IO::SEEK_END; @pos = @string.size - offset
|
||||
else
|
||||
raise ArgumentError, "unknown seek flag: #{whence}"
|
||||
end
|
||||
@pos = 0 if @pos < 0
|
||||
@pos = [@pos, @string.size + 1].min
|
||||
offset
|
||||
end
|
||||
|
||||
def eof?
|
||||
raise IOError, 'closed stream' if @closed
|
||||
@pos > @string.size
|
||||
end
|
||||
|
||||
def each( &block )
|
||||
raise IOError, 'closed stream' if @closed
|
||||
begin
|
||||
@string.each(&block)
|
||||
ensure
|
||||
@pos = 0
|
||||
end
|
||||
end
|
||||
|
||||
def gets
|
||||
raise IOError, 'closed stream' if @closed
|
||||
if idx = @string.index(?\n, @pos)
|
||||
idx += 1 # "\n".size
|
||||
line = @string[ @pos ... idx ]
|
||||
@pos = idx
|
||||
@pos += 1 if @pos == @string.size
|
||||
else
|
||||
line = @string[ @pos .. -1 ]
|
||||
@pos = @string.size + 1
|
||||
end
|
||||
@lineno += 1
|
||||
line
|
||||
end
|
||||
|
||||
def getc
|
||||
raise IOError, 'closed stream' if @closed
|
||||
ch = @string[@pos]
|
||||
@pos += 1
|
||||
@pos += 1 if @pos == @string.size
|
||||
ch
|
||||
end
|
||||
|
||||
def read( len = nil )
|
||||
raise IOError, 'closed stream' if @closed
|
||||
if !len
|
||||
return nil if eof?
|
||||
rest = @string[@pos ... @string.size]
|
||||
@pos = @string.size + 1
|
||||
return rest
|
||||
end
|
||||
str = @string[@pos, len]
|
||||
@pos += len
|
||||
@pos += 1 if @pos == @string.size
|
||||
str
|
||||
end
|
||||
|
||||
def read_all; read(); end
|
||||
|
||||
alias sysread read
|
||||
end
|
||||
|
||||
|
||||
|
||||
module ::Marshal
|
||||
class << self
|
||||
if !@oldloadAliased
|
||||
alias oldload load
|
||||
@oldloadAliased = true
|
||||
end
|
||||
|
||||
@@neverload = false
|
||||
|
||||
def neverload
|
||||
return @@neverload
|
||||
end
|
||||
|
||||
def neverload=(value)
|
||||
@@neverload = value
|
||||
end
|
||||
|
||||
def load(port,*arg)
|
||||
if @@neverload
|
||||
if port.is_a?(IO)
|
||||
return port.read
|
||||
end
|
||||
return port
|
||||
end
|
||||
oldpos = port.pos if port.is_a?(IO)
|
||||
begin
|
||||
oldload(port,*arg)
|
||||
rescue
|
||||
p [$!.class,$!.message,$!.backtrace]
|
||||
if port.is_a?(IO)
|
||||
port.pos = oldpos
|
||||
return port.read
|
||||
end
|
||||
return port
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
497
Data/Scripts/008_Objects and windows/002_BitmapCache.rb
Normal file
497
Data/Scripts/008_Objects and windows/002_BitmapCache.rb
Normal file
@@ -0,0 +1,497 @@
|
||||
class Hangup < Exception; end
|
||||
|
||||
|
||||
|
||||
def strsplit(str,re)
|
||||
ret=[]
|
||||
tstr=str
|
||||
while re=~tstr
|
||||
ret[ret.length]=$~.pre_match
|
||||
tstr=$~.post_match
|
||||
end
|
||||
ret[ret.length]=tstr if ret.length
|
||||
return ret
|
||||
end
|
||||
|
||||
def canonicalize(c)
|
||||
csplit = strsplit(c,/[\/\\]/)
|
||||
pos = -1
|
||||
ret = []
|
||||
retstr = ""
|
||||
for x in csplit
|
||||
if x=="."
|
||||
elsif x==".."
|
||||
if pos>=0
|
||||
ret.delete_at(pos)
|
||||
pos -= 1
|
||||
end
|
||||
else
|
||||
ret.push(x)
|
||||
pos += 1
|
||||
end
|
||||
end
|
||||
for i in 0...ret.length
|
||||
retstr += "/" if i>0
|
||||
retstr += ret[i]
|
||||
end
|
||||
return retstr
|
||||
end
|
||||
|
||||
|
||||
|
||||
#####################################################################
|
||||
class WeakRef
|
||||
@@id_map = {}
|
||||
@@id_rev_map = {}
|
||||
@@final = lambda { |id|
|
||||
__old_status = Thread.critical
|
||||
Thread.critical = true
|
||||
begin
|
||||
rids = @@id_map[id]
|
||||
if rids
|
||||
for rid in rids
|
||||
@@id_rev_map.delete(rid)
|
||||
end
|
||||
@@id_map.delete(id)
|
||||
end
|
||||
rid = @@id_rev_map[id]
|
||||
if rid
|
||||
@@id_rev_map.delete(id)
|
||||
@@id_map[rid].delete(id)
|
||||
@@id_map.delete(rid) if @@id_map[rid].empty?
|
||||
end
|
||||
ensure
|
||||
Thread.critical = __old_status
|
||||
end
|
||||
}
|
||||
|
||||
# Create a new WeakRef from +orig+.
|
||||
def initialize(orig)
|
||||
__setobj__(orig)
|
||||
end
|
||||
|
||||
def __getobj__
|
||||
unless @@id_rev_map[self.__id__] == @__id
|
||||
return nil
|
||||
end
|
||||
begin
|
||||
ObjectSpace._id2ref(@__id)
|
||||
rescue RangeError
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def __setobj__(obj)
|
||||
@__id = obj.__id__
|
||||
__old_status = Thread.critical
|
||||
begin
|
||||
Thread.critical = true
|
||||
unless @@id_rev_map.key?(self)
|
||||
ObjectSpace.define_finalizer obj, @@final
|
||||
ObjectSpace.define_finalizer self, @@final
|
||||
end
|
||||
@@id_map[@__id] = [] unless @@id_map[@__id]
|
||||
ensure
|
||||
Thread.critical = __old_status
|
||||
end
|
||||
@@id_map[@__id].push self.__id__
|
||||
@@id_rev_map[self.__id__] = @__id
|
||||
end
|
||||
|
||||
# Returns true if the referenced object still exists, and false if it has
|
||||
# been garbage collected.
|
||||
def weakref_alive?
|
||||
@@id_rev_map[self.__id__] == @__id
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class WeakHashtable
|
||||
include Enumerable
|
||||
|
||||
def initialize
|
||||
@hash={}
|
||||
end
|
||||
|
||||
def clear
|
||||
@hash.clear
|
||||
end
|
||||
|
||||
def delete(value)
|
||||
@hash.delete(value)
|
||||
end
|
||||
|
||||
def include?(value)
|
||||
@hash.include?(value)
|
||||
end
|
||||
|
||||
def each
|
||||
@hash.each { |i| yield i }
|
||||
end
|
||||
|
||||
def keys
|
||||
@hash.keys
|
||||
end
|
||||
|
||||
def values
|
||||
@hash.values
|
||||
end
|
||||
|
||||
def [](key)
|
||||
o=@hash[key]
|
||||
return o if !o
|
||||
if o.weakref_alive?
|
||||
o=o.__getobj__
|
||||
else
|
||||
@hash.delete(key)
|
||||
o=nil
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
def []=(key,o)
|
||||
if o!=nil
|
||||
o=WeakRef.new(o)
|
||||
end
|
||||
@hash[key]=o
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# Cache from RPG Maker VX library
|
||||
module Cache
|
||||
def self.system(x,hue=0)
|
||||
BitmapCache.load_bitmap("Graphics/System/"+x,hue, true)
|
||||
end
|
||||
|
||||
def self.character(x,hue=0)
|
||||
BitmapCache.load_bitmap("Graphics/Characters/"+x,hue, true)
|
||||
end
|
||||
|
||||
def self.picture(x,hue=0)
|
||||
BitmapCache.load_bitmap("Graphics/Pictures/"+x,hue, true)
|
||||
end
|
||||
|
||||
def self.animation(x,hue=0)
|
||||
BitmapCache.load_bitmap("Graphics/Animations/"+x,hue, true)
|
||||
end
|
||||
|
||||
def self.battler(x,hue=0)
|
||||
BitmapCache.load_bitmap("Graphics/Battlers/"+x,hue, true)
|
||||
end
|
||||
|
||||
def self.face(x,hue=0)
|
||||
BitmapCache.load_bitmap("Graphics/Faces/"+x,hue, true)
|
||||
end
|
||||
|
||||
def self.parallax(x,hue=0)
|
||||
BitmapCache.load_bitmap("Graphics/Parallaxes/"+x,hue, true)
|
||||
end
|
||||
|
||||
def self.clear
|
||||
BitmapCache.clear()
|
||||
end
|
||||
|
||||
def self.load_bitmap(dir,name,hue=0)
|
||||
BitmapCache.load_bitmap(dir+name,hue, true)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# RPG::Cache from RPG Maker XP library
|
||||
module RPG
|
||||
module Cache
|
||||
def self.load_bitmap(folder_name, filename, hue = 0)
|
||||
BitmapCache.load_bitmap(folder_name+filename.to_s,hue, true)
|
||||
end
|
||||
|
||||
def self.animation(filename, hue)
|
||||
self.load_bitmap("Graphics/Animations/", filename, hue)
|
||||
end
|
||||
|
||||
def self.autotile(filename)
|
||||
self.load_bitmap("Graphics/Autotiles/", filename)
|
||||
end
|
||||
|
||||
def self.battleback(filename)
|
||||
self.load_bitmap("Graphics/Battlebacks/", filename)
|
||||
end
|
||||
|
||||
def self.battler(filename, hue)
|
||||
self.load_bitmap("Graphics/Battlers/", filename, hue)
|
||||
end
|
||||
|
||||
def self.character(filename, hue)
|
||||
self.load_bitmap("Graphics/Characters/", filename, hue)
|
||||
end
|
||||
|
||||
def self.fog(filename, hue)
|
||||
self.load_bitmap("Graphics/Fogs/", filename, hue)
|
||||
end
|
||||
|
||||
def self.gameover(filename)
|
||||
self.load_bitmap("Graphics/Gameovers/", filename)
|
||||
end
|
||||
|
||||
def self.icon(filename)
|
||||
self.load_bitmap("Graphics/Icons/", filename)
|
||||
end
|
||||
|
||||
def self.panorama(filename, hue)
|
||||
self.load_bitmap("Graphics/Panoramas/", filename, hue)
|
||||
end
|
||||
|
||||
def self.picture(filename)
|
||||
self.load_bitmap("Graphics/Pictures/", filename)
|
||||
end
|
||||
|
||||
def self.tileset(filename)
|
||||
self.load_bitmap("Graphics/Tilesets/", filename)
|
||||
end
|
||||
|
||||
def self.title(filename)
|
||||
self.load_bitmap("Graphics/Titles/", filename)
|
||||
end
|
||||
|
||||
def self.windowskin(filename)
|
||||
self.load_bitmap("Graphics/Windowskins/", filename)
|
||||
end
|
||||
|
||||
def self.tile(filename, tile_id, hue)
|
||||
BitmapCache.tile(filename,tile_id,hue)
|
||||
end
|
||||
|
||||
def self.clear
|
||||
BitmapCache.clear()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
# A safer version of RPG::Cache, this module loads bitmaps that keep an internal
|
||||
# reference count. Each call to dispose decrements the reference count and the
|
||||
# bitmap is freed when the reference count reaches 0.
|
||||
class Thread
|
||||
def Thread.exclusive
|
||||
_old = Thread.critical
|
||||
begin
|
||||
Thread.critical = true
|
||||
return yield
|
||||
ensure
|
||||
Thread.critical = _old
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class BitmapWrapper < Bitmap
|
||||
@@disposedBitmaps={}
|
||||
@@keys={}
|
||||
=begin
|
||||
@@final = lambda { |id|
|
||||
Thread.exclusive {
|
||||
if @@disposedBitmaps[id]!=true
|
||||
File.open("debug.txt","ab") { |f|
|
||||
f.write("Bitmap finalized without being disposed: #{@@keys[id]}\r\n")
|
||||
}
|
||||
end
|
||||
@@disposedBitmaps[id]=nil
|
||||
}
|
||||
}
|
||||
=end
|
||||
attr_reader :refcount
|
||||
|
||||
def dispose
|
||||
return if self.disposed?
|
||||
@refcount-=1
|
||||
if @refcount==0
|
||||
super
|
||||
#Thread.exclusive { @@disposedBitmaps[__id__]=true }
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(*arg)
|
||||
super
|
||||
@refcount=1
|
||||
#Thread.exclusive { @@keys[__id__]=arg.inspect+caller(1).inspect }
|
||||
#ObjectSpace.define_finalizer(self,@@final)
|
||||
end
|
||||
|
||||
def resetRef # internal
|
||||
@refcount=1
|
||||
end
|
||||
|
||||
def copy
|
||||
bm=self.clone
|
||||
bm.resetRef
|
||||
return bm
|
||||
end
|
||||
|
||||
def addRef
|
||||
@refcount+=1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module BitmapCache
|
||||
@cache = WeakHashtable.new
|
||||
|
||||
def self.fromCache(i)
|
||||
return nil if !@cache.include?(i)
|
||||
obj=@cache[i]
|
||||
return nil if obj && obj.disposed?
|
||||
return obj
|
||||
end
|
||||
|
||||
def self.setKey(key,obj)
|
||||
@cache[key]=obj
|
||||
end
|
||||
|
||||
def self.debug
|
||||
File.open("bitmapcache2.txt","wb") { |f|
|
||||
for i in @cache.keys
|
||||
k = fromCache(i)
|
||||
if !k
|
||||
f.write("#{i} (nil)\r\n")
|
||||
elsif k.disposed?
|
||||
f.write("#{i} (disposed)\r\n")
|
||||
else
|
||||
f.write("#{i} (#{k.refcount}, #{k.width}x#{k.height})\r\n")
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def self.load_bitmap(path, hue = 0, failsafe = false)
|
||||
cached = true
|
||||
path = canonicalize(path)
|
||||
objPath = fromCache(path)
|
||||
if !objPath
|
||||
@cleancounter = ((@cleancounter || 0) + 1)%10
|
||||
if @cleancounter == 0
|
||||
for i in @cache.keys
|
||||
@cache.delete(i) if !fromCache(i)
|
||||
end
|
||||
end
|
||||
begin
|
||||
bm = BitmapWrapper.new(path)
|
||||
rescue Hangup
|
||||
begin
|
||||
bm = BitmapWrapper.new(path)
|
||||
rescue
|
||||
raise _INTL("Failed to load the bitmap located at: {1}",path) if !failsafe
|
||||
bm = BitmapWrapper.new(32,32)
|
||||
end
|
||||
rescue
|
||||
raise _INTL("Failed to load the bitmap located at: {1}",path) if !failsafe
|
||||
bm = BitmapWrapper.new(32,32)
|
||||
end
|
||||
objPath = bm
|
||||
@cache[path] = objPath
|
||||
cached=false
|
||||
end
|
||||
if hue == 0
|
||||
objPath.addRef if cached
|
||||
return objPath
|
||||
else
|
||||
key = [path, hue]
|
||||
objKey = fromCache(key)
|
||||
if !objKey
|
||||
bitmap = objPath.copy
|
||||
bitmap.hue_change(hue) if hue!=0
|
||||
objKey = bitmap
|
||||
@cache[key] = objKey
|
||||
else
|
||||
objKey.addRef
|
||||
end
|
||||
return objKey
|
||||
end
|
||||
end
|
||||
|
||||
def self.animation(filename, hue)
|
||||
self.load_bitmap("Graphics/Animations/"+filename, hue)
|
||||
end
|
||||
|
||||
def self.autotile(filename)
|
||||
self.load_bitmap("Graphics/Autotiles/"+ filename)
|
||||
end
|
||||
|
||||
def self.battleback(filename)
|
||||
self.load_bitmap("Graphics/Battlebacks/"+ filename)
|
||||
end
|
||||
|
||||
def self.battler(filename, hue)
|
||||
self.load_bitmap("Graphics/Battlers/"+ filename, hue)
|
||||
end
|
||||
|
||||
def self.character(filename, hue)
|
||||
self.load_bitmap("Graphics/Characters/"+ filename, hue)
|
||||
end
|
||||
|
||||
def self.fog(filename, hue)
|
||||
self.load_bitmap("Graphics/Fogs/"+ filename, hue)
|
||||
end
|
||||
|
||||
def self.gameover(filename)
|
||||
self.load_bitmap("Graphics/Gameovers/"+ filename)
|
||||
end
|
||||
|
||||
def self.icon(filename)
|
||||
self.load_bitmap("Graphics/Icons/"+ filename)
|
||||
end
|
||||
|
||||
def self.panorama(filename, hue)
|
||||
self.load_bitmap("Graphics/Panoramas/"+ filename, hue)
|
||||
end
|
||||
|
||||
def self.picture(filename)
|
||||
self.load_bitmap("Graphics/Pictures/"+ filename)
|
||||
end
|
||||
|
||||
def self.tileset(filename)
|
||||
self.load_bitmap("Graphics/Tilesets/"+ filename)
|
||||
end
|
||||
|
||||
def self.title(filename)
|
||||
self.load_bitmap("Graphics/Titles/"+ filename)
|
||||
end
|
||||
|
||||
def self.windowskin(filename)
|
||||
self.load_bitmap("Graphics/Windowskins/"+ filename)
|
||||
end
|
||||
|
||||
def self.tileEx(filename, tile_id, hue)
|
||||
key = [filename, tile_id, hue]
|
||||
objKey=fromCache(key)
|
||||
if !objKey
|
||||
bitmap=BitmapWrapper.new(32, 32)
|
||||
x = (tile_id - 384) % 8 * 32
|
||||
y = (tile_id - 384) / 8 * 32
|
||||
rect = Rect.new(x, y, 32, 32)
|
||||
tileset = yield(filename)
|
||||
bitmap.blt(0, 0, tileset, rect)
|
||||
tileset.dispose
|
||||
bitmap.hue_change(hue) if hue!=0
|
||||
objKey=bitmap
|
||||
@cache[key]=objKey
|
||||
else
|
||||
objKey.addRef
|
||||
end
|
||||
objKey
|
||||
end
|
||||
|
||||
def self.tile(filename, tile_id, hue)
|
||||
return self.tileEx(filename, tile_id,hue) { |f| self.tileset(f) }
|
||||
end
|
||||
|
||||
def self.clear
|
||||
@cache = {}
|
||||
GC.start
|
||||
end
|
||||
end
|
||||
608
Data/Scripts/008_Objects and windows/003_Window.rb
Normal file
608
Data/Scripts/008_Objects and windows/003_Window.rb
Normal file
@@ -0,0 +1,608 @@
|
||||
class WindowCursorRect < Rect
|
||||
def initialize(window)
|
||||
@window=window
|
||||
@x=0
|
||||
@y=0
|
||||
@width=0
|
||||
@height=0
|
||||
end
|
||||
|
||||
attr_reader :x,:y,:width,:height
|
||||
|
||||
def empty
|
||||
needupdate=@x!=0 || @y!=0 || @width!=0 || @height!=0
|
||||
if needupdate
|
||||
@x=0
|
||||
@y=0
|
||||
@width=0
|
||||
@height=0
|
||||
@window.width=@window.width
|
||||
end
|
||||
end
|
||||
|
||||
def isEmpty?
|
||||
return @x==0 && @y==0 && @width==0 && @height==0
|
||||
end
|
||||
|
||||
def set(x,y,width,height)
|
||||
needupdate=@x!=x || @y!=y || @width!=width || @height!=height
|
||||
if needupdate
|
||||
@x=x
|
||||
@y=y
|
||||
@width=width
|
||||
@height=height
|
||||
@window.width=@window.width
|
||||
end
|
||||
end
|
||||
|
||||
def height=(value)
|
||||
@height=value; @window.width=@window.width
|
||||
end
|
||||
|
||||
def width=(value)
|
||||
@width=value; @window.width=@window.width
|
||||
end
|
||||
|
||||
def x=(value)
|
||||
@x=value; @window.width=@window.width
|
||||
end
|
||||
|
||||
def y=(value)
|
||||
@y=value; @window.width=@window.width
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Window
|
||||
attr_reader :tone
|
||||
attr_reader :color
|
||||
attr_reader :blend_type
|
||||
attr_reader :contents_blend_type
|
||||
attr_reader :viewport
|
||||
attr_reader :contents
|
||||
attr_reader :ox
|
||||
attr_reader :oy
|
||||
attr_reader :x
|
||||
attr_reader :y
|
||||
attr_reader :z
|
||||
attr_reader :width
|
||||
attr_reader :active
|
||||
attr_reader :pause
|
||||
attr_reader :height
|
||||
attr_reader :opacity
|
||||
attr_reader :back_opacity
|
||||
attr_reader :contents_opacity
|
||||
attr_reader :visible
|
||||
attr_reader :cursor_rect
|
||||
attr_reader :openness
|
||||
attr_reader :stretch
|
||||
|
||||
def windowskin
|
||||
@_windowskin
|
||||
end
|
||||
|
||||
def initialize(viewport=nil)
|
||||
@sprites={}
|
||||
@spritekeys=[
|
||||
"back",
|
||||
"corner0","side0","scroll0",
|
||||
"corner1","side1","scroll1",
|
||||
"corner2","side2","scroll2",
|
||||
"corner3","side3","scroll3",
|
||||
"cursor","contents","pause"
|
||||
]
|
||||
@sidebitmaps=[nil,nil,nil,nil]
|
||||
@cursorbitmap=nil
|
||||
@bgbitmap=nil
|
||||
@viewport=viewport
|
||||
for i in @spritekeys
|
||||
@sprites[i]=Sprite.new(@viewport)
|
||||
end
|
||||
@disposed=false
|
||||
@tone=Tone.new(0,0,0)
|
||||
@color=Color.new(0,0,0,0)
|
||||
@blankcontents=Bitmap.new(1,1) # RGSS2 requires this
|
||||
@contents=@blankcontents
|
||||
@_windowskin=nil
|
||||
@rpgvx=false # Set to true to emulate RPGVX windows
|
||||
@x=0
|
||||
@y=0
|
||||
@width=0
|
||||
@openness=255
|
||||
@height=0
|
||||
@ox=0
|
||||
@oy=0
|
||||
@z=0
|
||||
@stretch=true
|
||||
@visible=true
|
||||
@active=true
|
||||
@blend_type=0
|
||||
@contents_blend_type=0
|
||||
@opacity=255
|
||||
@back_opacity=255
|
||||
@contents_opacity=255
|
||||
@cursor_rect=WindowCursorRect.new(self)
|
||||
@cursorblink=0
|
||||
@cursoropacity=255
|
||||
@pause=false
|
||||
@pauseopacity=255
|
||||
@pauseframe=0
|
||||
privRefresh(true)
|
||||
end
|
||||
|
||||
def dispose
|
||||
if !self.disposed?
|
||||
for i in @sprites
|
||||
i[1].dispose if i[1]
|
||||
@sprites[i[0]]=nil
|
||||
end
|
||||
for i in 0...@sidebitmaps.length
|
||||
@sidebitmaps[i].dispose if @sidebitmaps[i]
|
||||
@sidebitmaps[i]=nil
|
||||
end
|
||||
@blankcontents.dispose
|
||||
@cursorbitmap.dispose if @cursorbitmap
|
||||
@backbitmap.dispose if @backbitmap
|
||||
@sprites.clear
|
||||
@sidebitmaps.clear
|
||||
@_windowskin=nil
|
||||
@_contents=nil
|
||||
@disposed=true
|
||||
end
|
||||
end
|
||||
|
||||
def openness=(value)
|
||||
@openness=value
|
||||
@openness=0 if @openness<0
|
||||
@openness=255 if @openness>255
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def stretch=(value)
|
||||
@stretch=value
|
||||
privRefresh(true)
|
||||
end
|
||||
|
||||
def visible=(value)
|
||||
@visible=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def viewport=(value)
|
||||
@viewport=value
|
||||
for i in @spritekeys
|
||||
@sprites[i].dispose
|
||||
if @sprites[i].is_a?(Sprite)
|
||||
@sprites[i]=Sprite.new(@viewport)
|
||||
else
|
||||
@sprites[i]=nil
|
||||
end
|
||||
end
|
||||
privRefresh(true)
|
||||
end
|
||||
|
||||
def z=(value)
|
||||
@z=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def disposed?
|
||||
return @disposed
|
||||
end
|
||||
|
||||
def contents=(value)
|
||||
@contents=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def windowskin=(value)
|
||||
@_windowskin=value
|
||||
if value && value.is_a?(Bitmap) && !value.disposed? && value.width==128
|
||||
@rpgvx=true
|
||||
else
|
||||
@rpgvx=false
|
||||
end
|
||||
privRefresh(true)
|
||||
end
|
||||
|
||||
def ox=(value)
|
||||
@ox=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def active=(value)
|
||||
@active=value
|
||||
privRefresh(true)
|
||||
end
|
||||
|
||||
def cursor_rect=(value)
|
||||
if !value
|
||||
@cursor_rect.empty
|
||||
else
|
||||
@cursor_rect.set(value.x,value.y,value.width,value.height)
|
||||
end
|
||||
end
|
||||
|
||||
def oy=(value)
|
||||
@oy=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def width=(value)
|
||||
@width=value
|
||||
privRefresh(true)
|
||||
end
|
||||
|
||||
def height=(value)
|
||||
@height=value
|
||||
privRefresh(true)
|
||||
end
|
||||
|
||||
def pause=(value)
|
||||
@pause=value
|
||||
@pauseopacity=0 if !value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def x=(value)
|
||||
@x=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def y=(value)
|
||||
@y=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def opacity=(value)
|
||||
@opacity=value
|
||||
@opacity=0 if @opacity<0
|
||||
@opacity=255 if @opacity>255
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def back_opacity=(value)
|
||||
@back_opacity=value
|
||||
@back_opacity=0 if @back_opacity<0
|
||||
@back_opacity=255 if @back_opacity>255
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def contents_opacity=(value)
|
||||
@contents_opacity=value
|
||||
@contents_opacity=0 if @contents_opacity<0
|
||||
@contents_opacity=255 if @contents_opacity>255
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def tone=(value)
|
||||
@tone=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def color=(value)
|
||||
@color=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def blend_type=(value)
|
||||
@blend_type=value
|
||||
privRefresh
|
||||
end
|
||||
|
||||
def flash(color,duration)
|
||||
return if disposed?
|
||||
for i in @sprites
|
||||
i[1].flash(color,duration)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
return if disposed?
|
||||
mustchange=false
|
||||
if @active
|
||||
if @cursorblink==0
|
||||
@cursoropacity-=8
|
||||
@cursorblink=1 if @cursoropacity<=128
|
||||
else
|
||||
@cursoropacity+=8
|
||||
@cursorblink=0 if @cursoropacity>=255
|
||||
end
|
||||
mustchange=true if !@cursor_rect.isEmpty?
|
||||
else
|
||||
mustchange=true if @cursoropacity!=128
|
||||
@cursoropacity=128
|
||||
end
|
||||
if @pause
|
||||
@pauseframe=(Graphics.frame_count / 8) % 4
|
||||
@pauseopacity=[@pauseopacity+64,255].min
|
||||
mustchange=true
|
||||
end
|
||||
privRefresh if mustchange
|
||||
for i in @sprites
|
||||
i[1].update
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensureBitmap(bitmap,dwidth,dheight)
|
||||
if !bitmap||bitmap.disposed?||bitmap.width<dwidth||bitmap.height<dheight
|
||||
bitmap.dispose if bitmap
|
||||
bitmap=Bitmap.new([1,dwidth].max,[1,dheight].max)
|
||||
end
|
||||
return bitmap
|
||||
end
|
||||
|
||||
def tileBitmap(dstbitmap,dstrect,srcbitmap,srcrect)
|
||||
return if !srcbitmap || srcbitmap.disposed?
|
||||
left=dstrect.x
|
||||
top=dstrect.y
|
||||
y=0;loop do break unless y<dstrect.height
|
||||
x=0;loop do break unless x<dstrect.width
|
||||
dstbitmap.blt(x+left,y+top,srcbitmap,srcrect)
|
||||
x+=srcrect.width
|
||||
end
|
||||
y+=srcrect.height
|
||||
end
|
||||
end
|
||||
|
||||
def privRefresh(changeBitmap=false)
|
||||
return if self.disposed?
|
||||
backopac=self.back_opacity*self.opacity/255
|
||||
contopac=self.contents_opacity
|
||||
cursoropac=@cursoropacity*contopac/255
|
||||
for i in 0...4
|
||||
@sprites["corner#{i}"].bitmap=@_windowskin
|
||||
@sprites["scroll#{i}"].bitmap=@_windowskin
|
||||
end
|
||||
@sprites["pause"].bitmap=@_windowskin
|
||||
@sprites["contents"].bitmap=@contents
|
||||
if @_windowskin && !@_windowskin.disposed?
|
||||
for i in 0...4
|
||||
@sprites["corner#{i}"].opacity=@opacity
|
||||
@sprites["corner#{i}"].tone=@tone
|
||||
@sprites["corner#{i}"].color=@color
|
||||
@sprites["corner#{i}"].blend_type=@blend_type
|
||||
@sprites["corner#{i}"].visible=@visible
|
||||
@sprites["side#{i}"].opacity=@opacity
|
||||
@sprites["side#{i}"].tone=@tone
|
||||
@sprites["side#{i}"].color=@color
|
||||
@sprites["side#{i}"].blend_type=@blend_type
|
||||
@sprites["side#{i}"].visible=@visible
|
||||
@sprites["scroll#{i}"].opacity=@opacity
|
||||
@sprites["scroll#{i}"].tone=@tone
|
||||
@sprites["scroll#{i}"].blend_type=@blend_type
|
||||
@sprites["scroll#{i}"].color=@color
|
||||
@sprites["scroll#{i}"].visible=@visible
|
||||
end
|
||||
for i in ["back","cursor","pause","contents"]
|
||||
@sprites[i].color=@color
|
||||
@sprites[i].tone=@tone
|
||||
@sprites[i].blend_type=@blend_type
|
||||
end
|
||||
@sprites["contents"].blend_type=@contents_blend_type
|
||||
@sprites["back"].opacity=backopac
|
||||
@sprites["contents"].opacity=contopac
|
||||
@sprites["cursor"].opacity=cursoropac
|
||||
@sprites["pause"].opacity=@pauseopacity
|
||||
@sprites["back"].visible=@visible
|
||||
@sprites["contents"].visible=@visible && @openness==255
|
||||
@sprites["pause"].visible=@visible && @pause
|
||||
@sprites["cursor"].visible=@visible && @openness==255
|
||||
hascontents=(@contents && !@contents.disposed?)
|
||||
@sprites["scroll0"].visible = @visible && hascontents && @oy > 0
|
||||
@sprites["scroll1"].visible = @visible && hascontents && @ox > 0
|
||||
@sprites["scroll2"].visible = @visible && hascontents &&
|
||||
(@contents.width - @ox) > @width-32
|
||||
@sprites["scroll3"].visible = @visible && hascontents &&
|
||||
(@contents.height - @oy) > @height-32
|
||||
else
|
||||
for i in 0...4
|
||||
@sprites["corner#{i}"].visible=false
|
||||
@sprites["side#{i}"].visible=false
|
||||
@sprites["scroll#{i}"].visible=false
|
||||
end
|
||||
@sprites["contents"].visible=@visible && @openness==255
|
||||
@sprites["contents"].color=@color
|
||||
@sprites["contents"].tone=@tone
|
||||
@sprites["contents"].blend_type=@contents_blend_type
|
||||
@sprites["contents"].opacity=contopac
|
||||
@sprites["back"].visible=false
|
||||
@sprites["pause"].visible=false
|
||||
@sprites["cursor"].visible=false
|
||||
end
|
||||
for i in @sprites
|
||||
i[1].z=@z
|
||||
end
|
||||
if @rpgvx
|
||||
@sprites["cursor"].z=@z # For Compatibility
|
||||
@sprites["contents"].z=@z # For Compatibility
|
||||
@sprites["pause"].z=@z # For Compatibility
|
||||
else
|
||||
@sprites["cursor"].z=@z+1 # For Compatibility
|
||||
@sprites["contents"].z=@z+2 # For Compatibility
|
||||
@sprites["pause"].z=@z+2 # For Compatibility
|
||||
end
|
||||
if @rpgvx
|
||||
trimX=64
|
||||
trimY=0
|
||||
backRect=Rect.new(0,0,64,64)
|
||||
blindsRect=Rect.new(0,64,64,64)
|
||||
else
|
||||
trimX=128
|
||||
trimY=0
|
||||
backRect=Rect.new(0,0,128,128)
|
||||
blindsRect=nil
|
||||
end
|
||||
@sprites["corner0"].src_rect.set(trimX,trimY+0,16,16);
|
||||
@sprites["corner1"].src_rect.set(trimX+48,trimY+0,16,16);
|
||||
@sprites["corner2"].src_rect.set(trimX,trimY+48,16,16);
|
||||
@sprites["corner3"].src_rect.set(trimX+48,trimY+48,16,16);
|
||||
@sprites["scroll0"].src_rect.set(trimX+24, trimY+16, 16, 8) # up
|
||||
@sprites["scroll3"].src_rect.set(trimX+24, trimY+40, 16, 8) # down
|
||||
@sprites["scroll1"].src_rect.set(trimX+16, trimY+24, 8, 16) # left
|
||||
@sprites["scroll2"].src_rect.set(trimX+40, trimY+24, 8, 16) # right
|
||||
cursorX=trimX
|
||||
cursorY=trimY+64
|
||||
sideRects=[
|
||||
Rect.new(trimX+16,trimY+0,32,16),
|
||||
Rect.new(trimX,trimY+16,16,32),
|
||||
Rect.new(trimX+48,trimY+16,16,32),
|
||||
Rect.new(trimX+16,trimY+48,32,16)
|
||||
]
|
||||
if @width>32 && @height>32
|
||||
@sprites["contents"].src_rect.set(@ox,@oy,@width-32,@height-32)
|
||||
else
|
||||
@sprites["contents"].src_rect.set(0,0,0,0)
|
||||
end
|
||||
pauseRects=[
|
||||
trimX+32,trimY+64,
|
||||
trimX+48,trimY+64,
|
||||
trimX+32,trimY+80,
|
||||
trimX+48,trimY+80,
|
||||
]
|
||||
pauseWidth=16
|
||||
pauseHeight=16
|
||||
@sprites["pause"].src_rect.set(
|
||||
pauseRects[@pauseframe*2],
|
||||
pauseRects[@pauseframe*2+1],
|
||||
pauseWidth,pauseHeight
|
||||
)
|
||||
@sprites["pause"].x=@x+(@width/2)-(pauseWidth/2)
|
||||
@sprites["pause"].y=@y+@height-16 # 16 refers to skin margin
|
||||
@sprites["contents"].x=@x+16
|
||||
@sprites["contents"].y=@y+16
|
||||
@sprites["corner0"].x=@x
|
||||
@sprites["corner0"].y=@y
|
||||
@sprites["corner1"].x=@x+@width-16
|
||||
@sprites["corner1"].y=@y
|
||||
@sprites["corner2"].x=@x
|
||||
@sprites["corner2"].y=@y+@height-16
|
||||
@sprites["corner3"].x=@x+@width-16
|
||||
@sprites["corner3"].y=@y+@height-16
|
||||
@sprites["side0"].x=@x+16
|
||||
@sprites["side0"].y=@y
|
||||
@sprites["side1"].x=@x
|
||||
@sprites["side1"].y=@y+16
|
||||
@sprites["side2"].x=@x+@width-16
|
||||
@sprites["side2"].y=@y+16
|
||||
@sprites["side3"].x=@x+16
|
||||
@sprites["side3"].y=@y+@height-16
|
||||
@sprites["scroll0"].x = @x+@width / 2 - 8
|
||||
@sprites["scroll0"].y = @y+8
|
||||
@sprites["scroll1"].x = @x+8
|
||||
@sprites["scroll1"].y = @y+@height / 2 - 8
|
||||
@sprites["scroll2"].x = @x+@width - 16
|
||||
@sprites["scroll2"].y = @y+@height / 2 - 8
|
||||
@sprites["scroll3"].x = @x+@width / 2 - 8
|
||||
@sprites["scroll3"].y = @y+@height - 16
|
||||
@sprites["back"].x=@x+2
|
||||
@sprites["back"].y=@y+2
|
||||
@sprites["cursor"].x=@x+16+@cursor_rect.x
|
||||
@sprites["cursor"].y=@y+16+@cursor_rect.y
|
||||
if changeBitmap && @_windowskin && !@_windowskin.disposed?
|
||||
width=@cursor_rect.width
|
||||
height=@cursor_rect.height
|
||||
if width > 0 && height > 0
|
||||
cursorrects=[
|
||||
# sides
|
||||
Rect.new(cursorX+2, cursorY+0, 28, 2),
|
||||
Rect.new(cursorX+0, cursorY+2, 2, 28),
|
||||
Rect.new(cursorX+30, cursorY+2, 2, 28),
|
||||
Rect.new(cursorX+2, cursorY+30, 28, 2),
|
||||
# corners
|
||||
Rect.new(cursorX+0, cursorY+0, 2, 2),
|
||||
Rect.new(cursorX+30, cursorY+0, 2, 2),
|
||||
Rect.new(cursorX+0, cursorY+30, 2, 2),
|
||||
Rect.new(cursorX+30, cursorY+30, 2, 2),
|
||||
# back
|
||||
Rect.new(cursorX+2, cursorY+2, 28, 28)
|
||||
]
|
||||
margin=2
|
||||
fullmargin=4
|
||||
@cursorbitmap = ensureBitmap(@cursorbitmap, width, height)
|
||||
@cursorbitmap.clear
|
||||
@sprites["cursor"].bitmap=@cursorbitmap
|
||||
@sprites["cursor"].src_rect.set(0,0,width,height)
|
||||
rect = Rect.new(margin,margin,
|
||||
width - fullmargin, height - fullmargin)
|
||||
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[8])
|
||||
@cursorbitmap.blt(0, 0, @_windowskin, cursorrects[4])# top left
|
||||
@cursorbitmap.blt(width-margin, 0, @_windowskin, cursorrects[5]) # top right
|
||||
@cursorbitmap.blt(0, height-margin, @_windowskin, cursorrects[6]) # bottom right
|
||||
@cursorbitmap.blt(width-margin, height-margin, @_windowskin, cursorrects[7]) # bottom left
|
||||
rect = Rect.new(margin, 0,
|
||||
width - fullmargin, margin)
|
||||
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[0])
|
||||
rect = Rect.new(0, margin,
|
||||
margin, height - fullmargin)
|
||||
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[1])
|
||||
rect = Rect.new(width - margin, margin,
|
||||
margin, height - fullmargin)
|
||||
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[2])
|
||||
rect = Rect.new(margin, height-margin,
|
||||
width - fullmargin, margin)
|
||||
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[3])
|
||||
else
|
||||
@sprites["cursor"].visible=false
|
||||
@sprites["cursor"].src_rect.set(0,0,0,0)
|
||||
end
|
||||
for i in 0...4
|
||||
dwidth = (i==0 || i==3) ? @width-32 : 16
|
||||
dheight = (i==0 || i==3) ? 16 : @height-32
|
||||
@sidebitmaps[i]=ensureBitmap(@sidebitmaps[i],dwidth,dheight)
|
||||
@sprites["side#{i}"].bitmap=@sidebitmaps[i]
|
||||
@sprites["side#{i}"].src_rect.set(0,0,dwidth,dheight)
|
||||
@sidebitmaps[i].clear
|
||||
if sideRects[i].width>0 && sideRects[i].height>0
|
||||
@sidebitmaps[i].stretch_blt(@sprites["side#{i}"].src_rect,
|
||||
@_windowskin,sideRects[i])
|
||||
end
|
||||
end
|
||||
backwidth=@width-4
|
||||
backheight=@height-4
|
||||
if backwidth>0 && backheight>0
|
||||
@backbitmap=ensureBitmap(@backbitmap,backwidth,backheight)
|
||||
@sprites["back"].bitmap=@backbitmap
|
||||
@sprites["back"].src_rect.set(0,0,backwidth,backheight)
|
||||
@backbitmap.clear
|
||||
if @stretch
|
||||
@backbitmap.stretch_blt(@sprites["back"].src_rect,@_windowskin,backRect)
|
||||
else
|
||||
tileBitmap(@backbitmap,@sprites["back"].src_rect,@_windowskin,backRect)
|
||||
end
|
||||
if blindsRect
|
||||
tileBitmap(@backbitmap,@sprites["back"].src_rect,@_windowskin,blindsRect)
|
||||
end
|
||||
else
|
||||
@sprites["back"].visible=false
|
||||
@sprites["back"].src_rect.set(0,0,0,0)
|
||||
end
|
||||
end
|
||||
if @openness!=255
|
||||
opn=@openness/255.0
|
||||
for k in @spritekeys
|
||||
sprite=@sprites[k]
|
||||
ratio=(@height<=0) ? 0 : (sprite.y-@y)*1.0/@height
|
||||
sprite.zoom_y=opn
|
||||
sprite.oy=0
|
||||
sprite.y=(@y+(@height/2.0)+(@height*ratio*opn)-(@height/2*opn)).floor
|
||||
end
|
||||
else
|
||||
for k in @spritekeys
|
||||
sprite=@sprites[k]
|
||||
sprite.zoom_y=1.0
|
||||
end
|
||||
end
|
||||
i=0
|
||||
# Ensure Z order
|
||||
for k in @spritekeys
|
||||
sprite=@sprites[k]
|
||||
y=sprite.y
|
||||
sprite.y=i
|
||||
sprite.oy=(sprite.zoom_y<=0) ? 0 : (i-y)/sprite.zoom_y
|
||||
end
|
||||
end
|
||||
end
|
||||
781
Data/Scripts/008_Objects and windows/004_SpriteWindow.rb
Normal file
781
Data/Scripts/008_Objects and windows/004_SpriteWindow.rb
Normal file
@@ -0,0 +1,781 @@
|
||||
module MessageConfig
|
||||
FontName = "Power Green"
|
||||
# in Graphics/Windowskins/ (specify empty string to use the default windowskin)
|
||||
TextSkinName = "speech hgss 1"
|
||||
ChoiceSkinName = "choice 1"
|
||||
WindowOpacity = 255
|
||||
TextSpeed = nil # can be positive to wait frames or negative to
|
||||
# show multiple characters in a single frame
|
||||
LIGHTTEXTBASE = Color.new(248,248,248)
|
||||
LIGHTTEXTSHADOW = Color.new(72,80,88)
|
||||
DARKTEXTBASE = Color.new(80,80,88)
|
||||
DARKTEXTSHADOW = Color.new(160,160,168)
|
||||
# 0 = Pause cursor is displayed at end of text
|
||||
# 1 = Pause cursor is displayed at bottom right
|
||||
# 2 = Pause cursor is displayed at lower middle side
|
||||
CURSORMODE = 1
|
||||
FontSubstitutes = {
|
||||
"Power Red and Blue" => "Pokemon RS",
|
||||
"Power Red and Green" => "Pokemon FireLeaf",
|
||||
"Power Green" => "Pokemon Emerald",
|
||||
"Power Green Narrow" => "Pokemon Emerald Narrow",
|
||||
"Power Green Small" => "Pokemon Emerald Small",
|
||||
"Power Clear" => "Pokemon DP"
|
||||
}
|
||||
@@systemFrame = nil
|
||||
@@defaultTextSkin = nil
|
||||
@@systemFont = nil
|
||||
@@textSpeed = nil
|
||||
|
||||
def self.pbTryFonts(*args)
|
||||
for a in args
|
||||
if a && a.is_a?(String)
|
||||
return a if Font.exist?(a)
|
||||
a=MessageConfig::FontSubstitutes[a] || a
|
||||
return a if Font.exist?(a)
|
||||
elsif a && a.is_a?(Array)
|
||||
for aa in a
|
||||
ret=MessageConfig.pbTryFonts(aa)
|
||||
return ret if ret!=""
|
||||
end
|
||||
end
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
def self.pbDefaultSystemFrame
|
||||
return "" if !MessageConfig::ChoiceSkinName
|
||||
return pbResolveBitmap("Graphics/Windowskins/"+MessageConfig::ChoiceSkinName) || ""
|
||||
end
|
||||
|
||||
def self.pbDefaultSpeechFrame
|
||||
return "" if !MessageConfig::TextSkinName
|
||||
return pbResolveBitmap("Graphics/Windowskins/"+MessageConfig::TextSkinName) || ""
|
||||
end
|
||||
|
||||
def self.pbDefaultSystemFontName
|
||||
return MessageConfig.pbTryFonts(MessageConfig::FontName,"Arial Narrow","Arial")
|
||||
end
|
||||
|
||||
def self.pbDefaultTextSpeed
|
||||
return (TextSpeed) ? TextSpeed : (Graphics.width>400) ? -2 : 1
|
||||
end
|
||||
|
||||
def self.pbDefaultWindowskin
|
||||
skin=load_data("Data/System.rxdata").windowskin_name rescue nil
|
||||
if skin && skin!=""
|
||||
skin=pbResolveBitmap("Graphics/Windowskins/"+skin) || ""
|
||||
end
|
||||
skin=pbResolveBitmap("Graphics/System/Window") if !skin || skin==""
|
||||
skin=pbResolveBitmap("Graphics/Windowskins/001-Blue01") if !skin || skin==""
|
||||
return skin || ""
|
||||
end
|
||||
|
||||
def self.pbGetSystemFrame
|
||||
if !@@systemFrame
|
||||
skin=MessageConfig.pbDefaultSystemFrame
|
||||
skin=MessageConfig.pbDefaultWindowskin if !skin || skin==""
|
||||
@@systemFrame=skin || ""
|
||||
end
|
||||
return @@systemFrame
|
||||
end
|
||||
|
||||
def self.pbGetSpeechFrame
|
||||
if !@@defaultTextSkin
|
||||
skin=MessageConfig.pbDefaultSpeechFrame
|
||||
skin=MessageConfig.pbDefaultWindowskin if !skin || skin==""
|
||||
@@defaultTextSkin=skin || ""
|
||||
end
|
||||
return @@defaultTextSkin
|
||||
end
|
||||
|
||||
def self.pbGetSystemFontName
|
||||
@@systemFont=pbDefaultSystemFontName if !@@systemFont
|
||||
return @@systemFont
|
||||
end
|
||||
|
||||
def self.pbGetTextSpeed
|
||||
@@textSpeed=pbDefaultTextSpeed if !@@textSpeed
|
||||
return @@textSpeed
|
||||
end
|
||||
|
||||
def self.pbSetSystemFrame(value)
|
||||
@@systemFrame=pbResolveBitmap(value) || ""
|
||||
end
|
||||
|
||||
def self.pbSetSpeechFrame(value)
|
||||
@@defaultTextSkin=pbResolveBitmap(value) || ""
|
||||
end
|
||||
|
||||
def self.pbSetSystemFontName(value)
|
||||
@@systemFont=MessageConfig.pbTryFonts(value,"Arial Narrow","Arial")
|
||||
end
|
||||
|
||||
def self.pbSetTextSpeed(value)
|
||||
@@textSpeed=value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Position a window
|
||||
#===============================================================================
|
||||
def pbBottomRight(window)
|
||||
window.x=Graphics.width-window.width
|
||||
window.y=Graphics.height-window.height
|
||||
end
|
||||
|
||||
def pbBottomLeft(window)
|
||||
window.x=0
|
||||
window.y=Graphics.height-window.height
|
||||
end
|
||||
|
||||
def pbBottomLeftLines(window,lines,width=nil)
|
||||
window.x=0
|
||||
window.width=width ? width : Graphics.width
|
||||
window.height=(window.borderY rescue 32)+lines*32
|
||||
window.y=Graphics.height-window.height
|
||||
end
|
||||
|
||||
def pbPositionFaceWindow(facewindow,msgwindow)
|
||||
return if !facewindow
|
||||
if msgwindow
|
||||
if facewindow.height<=msgwindow.height
|
||||
facewindow.y=msgwindow.y
|
||||
else
|
||||
facewindow.y=msgwindow.y+msgwindow.height-facewindow.height
|
||||
end
|
||||
facewindow.x=Graphics.width-facewindow.width
|
||||
msgwindow.x=0
|
||||
msgwindow.width=Graphics.width-facewindow.width
|
||||
else
|
||||
facewindow.height=Graphics.height if facewindow.height>Graphics.height
|
||||
facewindow.x=0
|
||||
facewindow.y=0
|
||||
end
|
||||
end
|
||||
|
||||
def pbPositionNearMsgWindow(cmdwindow,msgwindow,side)
|
||||
return if !cmdwindow
|
||||
if msgwindow
|
||||
height=[cmdwindow.height,Graphics.height-msgwindow.height].min
|
||||
if cmdwindow.height!=height
|
||||
cmdwindow.height=height
|
||||
end
|
||||
cmdwindow.y=msgwindow.y-cmdwindow.height
|
||||
if cmdwindow.y<0
|
||||
cmdwindow.y=msgwindow.y+msgwindow.height
|
||||
if cmdwindow.y+cmdwindow.height>Graphics.height
|
||||
cmdwindow.y=msgwindow.y-cmdwindow.height
|
||||
end
|
||||
end
|
||||
case side
|
||||
when :left
|
||||
cmdwindow.x=msgwindow.x
|
||||
when :right
|
||||
cmdwindow.x=msgwindow.x+msgwindow.width-cmdwindow.width
|
||||
else
|
||||
cmdwindow.x=msgwindow.x+msgwindow.width-cmdwindow.width
|
||||
end
|
||||
else
|
||||
cmdwindow.height=Graphics.height if cmdwindow.height>Graphics.height
|
||||
cmdwindow.x=0
|
||||
cmdwindow.y=0
|
||||
end
|
||||
end
|
||||
|
||||
# internal function
|
||||
def pbRepositionMessageWindow(msgwindow, linecount=2)
|
||||
msgwindow.height=32*linecount+msgwindow.borderY
|
||||
msgwindow.y=(Graphics.height)-(msgwindow.height)
|
||||
if $game_system && $game_system.respond_to?("message_position")
|
||||
case $game_system.message_position
|
||||
when 0 # up
|
||||
msgwindow.y=0
|
||||
when 1 # middle
|
||||
msgwindow.y=(Graphics.height/2)-(msgwindow.height/2)
|
||||
when 2
|
||||
msgwindow.y=(Graphics.height)-(msgwindow.height)
|
||||
end
|
||||
end
|
||||
if $game_system && $game_system.respond_to?("message_frame")
|
||||
if $game_system.message_frame != 0
|
||||
msgwindow.opacity = 0
|
||||
end
|
||||
end
|
||||
if $game_message
|
||||
case $game_message.background
|
||||
when 1; msgwindow.opacity=0 # dim
|
||||
when 2; msgwindow.opacity=0 # transparent
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# internal function
|
||||
def pbUpdateMsgWindowPos(msgwindow,event,eventChanged=false)
|
||||
if event
|
||||
if eventChanged
|
||||
msgwindow.resizeToFit2(msgwindow.text,Graphics.width*2/3,msgwindow.height)
|
||||
end
|
||||
msgwindow.y=event.screen_y-48-msgwindow.height
|
||||
if msgwindow.y<0
|
||||
msgwindow.y=event.screen_y+24
|
||||
end
|
||||
msgwindow.x=event.screen_x-(msgwindow.width/2)
|
||||
msgwindow.x=0 if msgwindow.x<0
|
||||
if msgwindow.x>Graphics.width-msgwindow.width
|
||||
msgwindow.x=Graphics.width-msgwindow.width
|
||||
end
|
||||
else
|
||||
curwidth=msgwindow.width
|
||||
if curwidth!=Graphics.width
|
||||
msgwindow.width=Graphics.width
|
||||
msgwindow.width=Graphics.width
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Determine the colour of a background
|
||||
#===============================================================================
|
||||
def isDarkBackground(background,rect=nil)
|
||||
return true if !background || background.disposed?
|
||||
rect = background.rect if !rect
|
||||
return true if rect.width<=0 || rect.height<=0
|
||||
xSeg = (rect.width/16)
|
||||
xLoop = (xSeg==0) ? 1 : 16
|
||||
xStart = (xSeg==0) ? rect.x+(rect.width/2) : rect.x+xSeg/2
|
||||
ySeg = (rect.height/16)
|
||||
yLoop = (ySeg==0) ? 1 : 16
|
||||
yStart = (ySeg==0) ? rect.y+(rect.height/2) : rect.y+ySeg/2
|
||||
count = 0
|
||||
y = yStart
|
||||
r = 0; g = 0; b = 0
|
||||
yLoop.times do
|
||||
x = xStart
|
||||
xLoop.times do
|
||||
clr = background.get_pixel(x,y)
|
||||
if clr.alpha!=0
|
||||
r += clr.red
|
||||
g += clr.green
|
||||
b += clr.blue
|
||||
count += 1
|
||||
end
|
||||
x += xSeg
|
||||
end
|
||||
y += ySeg
|
||||
end
|
||||
return true if count==0
|
||||
r /= count
|
||||
g /= count
|
||||
b /= count
|
||||
return (r*0.299+g*0.587+b*0.114)<160
|
||||
end
|
||||
|
||||
def isDarkWindowskin(windowskin)
|
||||
return true if !windowskin || windowskin.disposed?
|
||||
if windowskin.width==192 && windowskin.height==128
|
||||
return isDarkBackground(windowskin,Rect.new(0,0,128,128))
|
||||
elsif windowskin.width==128 && windowskin.height==128
|
||||
return isDarkBackground(windowskin,Rect.new(0,0,64,64))
|
||||
elsif windowskin.width==96 && windowskin.height==48
|
||||
return isDarkBackground(windowskin,Rect.new(32,16,16,16))
|
||||
else
|
||||
clr = windowskin.get_pixel(windowskin.width/2, windowskin.height/2)
|
||||
return (clr.red*0.299+clr.green*0.587+clr.blue*0.114)<160
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Determine which text colours to use based on the darkness of the background
|
||||
#===============================================================================
|
||||
def getSkinColor(windowskin,color,isDarkSkin)
|
||||
if !windowskin || windowskin.disposed? ||
|
||||
windowskin.width!=128 || windowskin.height!=128
|
||||
# Base color, shadow color (these are reversed on dark windowskins)
|
||||
textcolors = [
|
||||
"0070F8","78B8E8", # 1 Blue
|
||||
"E82010","F8A8B8", # 2 Red
|
||||
"60B048","B0D090", # 3 Green
|
||||
"48D8D8","A8E0E0", # 4 Cyan
|
||||
"D038B8","E8A0E0", # 5 Magenta
|
||||
"E8D020","F8E888", # 6 Yellow
|
||||
"A0A0A8","D0D0D8", # 7 Grey
|
||||
"F0F0F8","C8C8D0", # 8 White
|
||||
"9040E8","B8A8E0", # 9 Purple
|
||||
"F89818","F8C898", # 10 Orange
|
||||
colorToRgb32(MessageConfig::DARKTEXTBASE),
|
||||
colorToRgb32(MessageConfig::DARKTEXTSHADOW), # 11 Dark default
|
||||
colorToRgb32(MessageConfig::LIGHTTEXTBASE),
|
||||
colorToRgb32(MessageConfig::LIGHTTEXTSHADOW) # 12 Light default
|
||||
]
|
||||
if color==0 || color>textcolors.length/2 # No special colour, use default
|
||||
if isDarkSkin # Dark background, light text
|
||||
return shadowc3tag(MessageConfig::LIGHTTEXTBASE,MessageConfig::LIGHTTEXTSHADOW)
|
||||
end
|
||||
# Light background, dark text
|
||||
return shadowc3tag(MessageConfig::DARKTEXTBASE,MessageConfig::DARKTEXTSHADOW)
|
||||
end
|
||||
# Special colour as listed above
|
||||
if isDarkSkin && color!=12 # Dark background, light text
|
||||
return sprintf("<c3=%s,%s>",textcolors[2*(color-1)+1],textcolors[2*(color-1)])
|
||||
end
|
||||
# Light background, dark text
|
||||
return sprintf("<c3=%s,%s>",textcolors[2*(color-1)],textcolors[2*(color-1)+1])
|
||||
else # VX windowskin
|
||||
color = 0 if color>=32
|
||||
x = 64 + (color % 8) * 8
|
||||
y = 96 + (color / 8) * 8
|
||||
pixel = windowskin.get_pixel(x, y)
|
||||
return shadowctagFromColor(pixel)
|
||||
end
|
||||
end
|
||||
|
||||
def getDefaultTextColors(windowskin)
|
||||
if !windowskin || windowskin.disposed? ||
|
||||
windowskin.width!=128 || windowskin.height!=128
|
||||
if isDarkWindowskin(windowskin)
|
||||
return [MessageConfig::LIGHTTEXTBASE,MessageConfig::LIGHTTEXTSHADOW] # White
|
||||
else
|
||||
return [MessageConfig::DARKTEXTBASE,MessageConfig::DARKTEXTSHADOW] # Dark gray
|
||||
end
|
||||
else # VX windowskin
|
||||
color = windowskin.get_pixel(64, 96)
|
||||
shadow = nil
|
||||
isDark = (color.red+color.green+color.blue)/3 < 128
|
||||
if isDark
|
||||
shadow = Color.new(color.red+64,color.green+64,color.blue+64)
|
||||
else
|
||||
shadow = Color.new(color.red-64,color.green-64,color.blue-64)
|
||||
end
|
||||
return [color,shadow]
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Makes sure a bitmap exists
|
||||
#===============================================================================
|
||||
def pbDoEnsureBitmap(bitmap,dwidth,dheight)
|
||||
if !bitmap || bitmap.disposed? || bitmap.width<dwidth || bitmap.height<dheight
|
||||
oldfont = (bitmap && !bitmap.disposed?) ? bitmap.font : nil
|
||||
bitmap.dispose if bitmap
|
||||
bitmap = Bitmap.new([1,dwidth].max,[1,dheight].max)
|
||||
(oldfont) ? bitmap.font = oldfont : pbSetSystemFont(bitmap)
|
||||
bitmap.font.shadow = false if bitmap.font && bitmap.font.respond_to?("shadow")
|
||||
end
|
||||
return bitmap
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Set a bitmap's font
|
||||
#===============================================================================
|
||||
# Gets the name of the system small font.
|
||||
def pbSmallFontName
|
||||
return MessageConfig.pbTryFonts("Power Green Small","Pokemon Emerald Small",
|
||||
"Arial Narrow","Arial")
|
||||
end
|
||||
|
||||
# Gets the name of the system narrow font.
|
||||
def pbNarrowFontName
|
||||
return MessageConfig.pbTryFonts("Power Green Narrow","Pokemon Emerald Narrow",
|
||||
"Arial Narrow","Arial")
|
||||
end
|
||||
|
||||
# Sets a bitmap's font to the system font.
|
||||
def pbSetSystemFont(bitmap)
|
||||
fontname=MessageConfig.pbGetSystemFontName
|
||||
bitmap.font.name=fontname
|
||||
if fontname=="Pokemon FireLeaf" || fontname=="Power Red and Green"
|
||||
bitmap.font.size=29
|
||||
elsif fontname=="Pokemon Emerald Small" || fontname=="Power Green Small"
|
||||
bitmap.font.size=25
|
||||
else
|
||||
bitmap.font.size=31
|
||||
end
|
||||
end
|
||||
|
||||
# Sets a bitmap's font to the system small font.
|
||||
def pbSetSmallFont(bitmap)
|
||||
bitmap.font.name=pbSmallFontName
|
||||
bitmap.font.size=25
|
||||
end
|
||||
|
||||
# Sets a bitmap's font to the system narrow font.
|
||||
def pbSetNarrowFont(bitmap)
|
||||
bitmap.font.name=pbNarrowFontName
|
||||
bitmap.font.size=31
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Blend colours, set the colour of all bitmaps in a sprite hash
|
||||
#===============================================================================
|
||||
def pbAlphaBlend(dstColor,srcColor)
|
||||
r=(255*(srcColor.red-dstColor.red)/255)+dstColor.red
|
||||
g=(255*(srcColor.green-dstColor.green)/255)+dstColor.green
|
||||
b=(255*(srcColor.blue-dstColor.blue)/255)+dstColor.blue
|
||||
a=(255*(srcColor.alpha-dstColor.alpha)/255)+dstColor.alpha
|
||||
return Color.new(r,g,b,a)
|
||||
end
|
||||
|
||||
def pbSrcOver(dstColor,srcColor)
|
||||
er=srcColor.red*srcColor.alpha/255
|
||||
eg=srcColor.green*srcColor.alpha/255
|
||||
eb=srcColor.blue*srcColor.alpha/255
|
||||
iea=255-srcColor.alpha
|
||||
cr=dstColor.red*dstColor.alpha/255
|
||||
cg=dstColor.green*dstColor.alpha/255
|
||||
cb=dstColor.blue*dstColor.alpha/255
|
||||
ica=255-dstColor.alpha
|
||||
a=255-(iea*ica)/255
|
||||
r=(iea*cr)/255+er
|
||||
g=(iea*cg)/255+eg
|
||||
b=(iea*cb)/255+eb
|
||||
r=(a==0) ? 0 : r*255/a
|
||||
g=(a==0) ? 0 : g*255/a
|
||||
b=(a==0) ? 0 : b*255/a
|
||||
return Color.new(r,g,b,a)
|
||||
end
|
||||
|
||||
def pbSetSpritesToColor(sprites,color)
|
||||
return if !sprites || !color
|
||||
colors={}
|
||||
for i in sprites
|
||||
next if !i[1] || pbDisposed?(i[1])
|
||||
colors[i[0]]=i[1].color.clone
|
||||
i[1].color=pbSrcOver(i[1].color,color)
|
||||
end
|
||||
Graphics.update
|
||||
Input.update
|
||||
for i in colors
|
||||
next if !sprites[i[0]]
|
||||
sprites[i[0]].color=i[1]
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Update and dispose sprite hashes
|
||||
#===============================================================================
|
||||
def using(window)
|
||||
begin
|
||||
yield if block_given?
|
||||
ensure
|
||||
window.dispose
|
||||
end
|
||||
end
|
||||
|
||||
def pbUpdateSpriteHash(windows)
|
||||
for i in windows
|
||||
window=i[1]
|
||||
if window
|
||||
if window.is_a?(Sprite) || window.is_a?(Window)
|
||||
window.update if !pbDisposed?(window)
|
||||
elsif window.is_a?(Plane)
|
||||
begin
|
||||
window.update if !window.disposed?
|
||||
rescue NoMethodError
|
||||
end
|
||||
elsif window.respond_to?("update")
|
||||
begin
|
||||
window.update
|
||||
rescue RGSSError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Disposes all objects in the specified hash.
|
||||
def pbDisposeSpriteHash(sprites)
|
||||
return if !sprites
|
||||
for i in sprites.keys
|
||||
pbDisposeSprite(sprites,i)
|
||||
end
|
||||
sprites.clear
|
||||
end
|
||||
|
||||
# Disposes the specified graphics object within the specified hash. Basically
|
||||
# like: sprites[id].dispose
|
||||
def pbDisposeSprite(sprites,id)
|
||||
sprite = sprites[id]
|
||||
sprite.dispose if sprite && !pbDisposed?(sprite)
|
||||
sprites[id] = nil
|
||||
end
|
||||
|
||||
def pbDisposed?(x)
|
||||
return true if !x
|
||||
return x.disposed? if !x.is_a?(Viewport)
|
||||
begin
|
||||
x.rect = x.rect
|
||||
rescue
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Fades and window activations for sprite hashes
|
||||
#===============================================================================
|
||||
class Game_Temp
|
||||
attr_accessor :fadestate
|
||||
|
||||
def fadestate
|
||||
return (@fadestate) ? @fadestate : 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def pbPushFade
|
||||
$game_temp.fadestate = [$game_temp.fadestate+1,0].max if $game_temp
|
||||
end
|
||||
|
||||
def pbPopFade
|
||||
$game_temp.fadestate = [$game_temp.fadestate-1,0].max if $game_temp
|
||||
end
|
||||
|
||||
def pbIsFaded?
|
||||
return ($game_temp) ? $game_temp.fadestate>0 : false
|
||||
end
|
||||
|
||||
# pbFadeOutIn(z) { block }
|
||||
# Fades out the screen before a block is run and fades it back in after the
|
||||
# block exits. z indicates the z-coordinate of the viewport used for this effect
|
||||
def pbFadeOutIn(z=99999,nofadeout=false)
|
||||
col=Color.new(0,0,0,0)
|
||||
viewport=Viewport.new(0,0,Graphics.width,Graphics.height)
|
||||
viewport.z=z
|
||||
numFrames = (Graphics.frame_rate*0.4).floor
|
||||
alphaDiff = (255.0/numFrames).ceil
|
||||
for j in 0..numFrames
|
||||
col.set(0,0,0,j*alphaDiff)
|
||||
viewport.color=col
|
||||
Graphics.update
|
||||
Input.update
|
||||
end
|
||||
pbPushFade
|
||||
begin
|
||||
yield if block_given?
|
||||
ensure
|
||||
pbPopFade
|
||||
if !nofadeout
|
||||
for j in 0..numFrames
|
||||
col.set(0,0,0,(numFrames-j)*alphaDiff)
|
||||
viewport.color=col
|
||||
Graphics.update
|
||||
Input.update
|
||||
end
|
||||
end
|
||||
viewport.dispose
|
||||
end
|
||||
end
|
||||
|
||||
def pbFadeOutInWithUpdate(z,sprites,nofadeout=false)
|
||||
col=Color.new(0,0,0,0)
|
||||
viewport=Viewport.new(0,0,Graphics.width,Graphics.height)
|
||||
viewport.z=z
|
||||
numFrames = (Graphics.frame_rate*0.4).floor
|
||||
alphaDiff = (255.0/numFrames).ceil
|
||||
for j in 0..numFrames
|
||||
col.set(0,0,0,j*alphaDiff)
|
||||
viewport.color=col
|
||||
pbUpdateSpriteHash(sprites)
|
||||
Graphics.update
|
||||
Input.update
|
||||
end
|
||||
pbPushFade
|
||||
begin
|
||||
yield if block_given?
|
||||
ensure
|
||||
pbPopFade
|
||||
if !nofadeout
|
||||
for j in 0..numFrames
|
||||
col.set(0,0,0,(numFrames-j)*alphaDiff)
|
||||
viewport.color=col
|
||||
pbUpdateSpriteHash(sprites)
|
||||
Graphics.update
|
||||
Input.update
|
||||
end
|
||||
end
|
||||
viewport.dispose
|
||||
end
|
||||
end
|
||||
|
||||
def pbFadeOutAndHide(sprites)
|
||||
visiblesprites = {}
|
||||
numFrames = (Graphics.frame_rate*0.4).floor
|
||||
alphaDiff = (255.0/numFrames).ceil
|
||||
pbDeactivateWindows(sprites) {
|
||||
for j in 0..numFrames
|
||||
pbSetSpritesToColor(sprites,Color.new(0,0,0,j*alphaDiff))
|
||||
(block_given?) ? yield : pbUpdateSpriteHash(sprites)
|
||||
end
|
||||
}
|
||||
for i in sprites
|
||||
next if !i[1]
|
||||
next if pbDisposed?(i[1])
|
||||
visiblesprites[i[0]] = true if i[1].visible
|
||||
i[1].visible = false
|
||||
end
|
||||
return visiblesprites
|
||||
end
|
||||
|
||||
def pbFadeInAndShow(sprites,visiblesprites=nil)
|
||||
if visiblesprites
|
||||
for i in visiblesprites
|
||||
if i[1] && sprites[i[0]] && !pbDisposed?(sprites[i[0]])
|
||||
sprites[i[0]].visible = true
|
||||
end
|
||||
end
|
||||
end
|
||||
numFrames = (Graphics.frame_rate*0.4).floor
|
||||
alphaDiff = (255.0/numFrames).ceil
|
||||
pbDeactivateWindows(sprites) {
|
||||
for j in 0..numFrames
|
||||
pbSetSpritesToColor(sprites,Color.new(0,0,0,((numFrames-j)*alphaDiff)))
|
||||
(block_given?) ? yield : pbUpdateSpriteHash(sprites)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Restores which windows are active for the given sprite hash.
|
||||
# _activeStatuses_ is the result of a previous call to pbActivateWindows
|
||||
def pbRestoreActivations(sprites,activeStatuses)
|
||||
return if !sprites || !activeStatuses
|
||||
for k in activeStatuses.keys
|
||||
if sprites[k] && sprites[k].is_a?(Window) && !pbDisposed?(sprites[k])
|
||||
sprites[k].active=activeStatuses[k] ? true : false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Deactivates all windows. If a code block is given, deactivates all windows,
|
||||
# runs the code in the block, and reactivates them.
|
||||
def pbDeactivateWindows(sprites)
|
||||
if block_given?
|
||||
pbActivateWindow(sprites,nil) { yield }
|
||||
else
|
||||
pbActivateWindow(sprites,nil)
|
||||
end
|
||||
end
|
||||
|
||||
# Activates a specific window of a sprite hash. _key_ is the key of the window
|
||||
# in the sprite hash. If a code block is given, deactivates all windows except
|
||||
# the specified window, runs the code in the block, and reactivates them.
|
||||
def pbActivateWindow(sprites,key)
|
||||
return if !sprites
|
||||
activeStatuses={}
|
||||
for i in sprites
|
||||
if i[1] && i[1].is_a?(Window) && !pbDisposed?(i[1])
|
||||
activeStatuses[i[0]]=i[1].active
|
||||
i[1].active=(i[0]==key)
|
||||
end
|
||||
end
|
||||
if block_given?
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
pbRestoreActivations(sprites,activeStatuses)
|
||||
end
|
||||
return {}
|
||||
else
|
||||
return activeStatuses
|
||||
end
|
||||
end
|
||||
|
||||
#===============================================================================
|
||||
# Create background planes for a sprite hash
|
||||
#===============================================================================
|
||||
# Adds a background to the sprite hash.
|
||||
# _planename_ is the hash key of the background.
|
||||
# _background_ is a filename within the Graphics/Pictures/ folder and can be
|
||||
# an animated image.
|
||||
# _viewport_ is a viewport to place the background in.
|
||||
def addBackgroundPlane(sprites,planename,background,viewport=nil)
|
||||
sprites[planename]=AnimatedPlane.new(viewport)
|
||||
bitmapName=pbResolveBitmap("Graphics/Pictures/#{background}")
|
||||
if bitmapName==nil
|
||||
# Plane should exist in any case
|
||||
sprites[planename].bitmap=nil
|
||||
sprites[planename].visible=false
|
||||
else
|
||||
sprites[planename].setBitmap(bitmapName)
|
||||
for spr in sprites.values
|
||||
if spr.is_a?(Window)
|
||||
spr.windowskin=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Adds a background to the sprite hash.
|
||||
# _planename_ is the hash key of the background.
|
||||
# _background_ is a filename within the Graphics/Pictures/ folder and can be
|
||||
# an animated image.
|
||||
# _color_ is the color to use if the background can't be found.
|
||||
# _viewport_ is a viewport to place the background in.
|
||||
def addBackgroundOrColoredPlane(sprites,planename,background,color,viewport=nil)
|
||||
bitmapName=pbResolveBitmap("Graphics/Pictures/#{background}")
|
||||
if bitmapName==nil
|
||||
# Plane should exist in any case
|
||||
sprites[planename]=ColoredPlane.new(color,@viewport)
|
||||
else
|
||||
sprites[planename]=AnimatedPlane.new(viewport)
|
||||
sprites[planename].setBitmap(bitmapName)
|
||||
for spr in sprites.values
|
||||
if spr.is_a?(Window)
|
||||
spr.windowskin=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Ensure required method definitions
|
||||
#===============================================================================
|
||||
module Graphics
|
||||
if !self.respond_to?("width")
|
||||
def self.width; return 640; end
|
||||
end
|
||||
if !self.respond_to?("height")
|
||||
def self.height; return 480; end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
if !defined?(_INTL)
|
||||
def _INTL(*args)
|
||||
string=args[0].clone
|
||||
for i in 1...args.length
|
||||
string.gsub!(/\{#{i}\}/,"#{args[i]}")
|
||||
end
|
||||
return string
|
||||
end
|
||||
end
|
||||
|
||||
if !defined?(_ISPRINTF)
|
||||
def _ISPRINTF(*args)
|
||||
string=args[0].clone
|
||||
for i in 1...args.length
|
||||
string.gsub!(/\{#{i}\:([^\}]+?)\}/) { |m|
|
||||
next sprintf("%"+$1,args[i])
|
||||
}
|
||||
end
|
||||
return string
|
||||
end
|
||||
end
|
||||
|
||||
if !defined?(_MAPINTL)
|
||||
def _MAPINTL(*args)
|
||||
string=args[1].clone
|
||||
for i in 2...args.length
|
||||
string.gsub!(/\{#{i}\}/,"#{args[i+1]}")
|
||||
end
|
||||
return string
|
||||
end
|
||||
end
|
||||
2369
Data/Scripts/008_Objects and windows/005_SpriteWindow_text.rb
Normal file
2369
Data/Scripts/008_Objects and windows/005_SpriteWindow_text.rb
Normal file
File diff suppressed because it is too large
Load Diff
1085
Data/Scripts/008_Objects and windows/006_SpriteWindow_sprites.rb
Normal file
1085
Data/Scripts/008_Objects and windows/006_SpriteWindow_sprites.rb
Normal file
File diff suppressed because it is too large
Load Diff
1212
Data/Scripts/008_Objects and windows/007_DrawText.rb
Normal file
1212
Data/Scripts/008_Objects and windows/007_DrawText.rb
Normal file
File diff suppressed because it is too large
Load Diff
1431
Data/Scripts/008_Objects and windows/008_Messages.rb
Normal file
1431
Data/Scripts/008_Objects and windows/008_Messages.rb
Normal file
File diff suppressed because it is too large
Load Diff
1676
Data/Scripts/008_Objects and windows/009_TextEntry.rb
Normal file
1676
Data/Scripts/008_Objects and windows/009_TextEntry.rb
Normal file
File diff suppressed because it is too large
Load Diff
709
Data/Scripts/008_Objects and windows/010_EventScene.rb
Normal file
709
Data/Scripts/008_Objects and windows/010_EventScene.rb
Normal file
@@ -0,0 +1,709 @@
|
||||
def getCubicPoint2(src,t)
|
||||
x0 = src[0]; y0 = src[1]
|
||||
cx0 = src[2]; cy0 = src[3]
|
||||
cx1 = src[4]; cy1 = src[5]
|
||||
x1 = src[6]; y1 = src[7]
|
||||
|
||||
x1 = cx1+(x1-cx1)*t
|
||||
x0 = x0+(cx0-x0)*t
|
||||
cx0 = cx0+(cx1-cx0)*t
|
||||
cx1 = cx0+(x1-cx0)*t
|
||||
cx0 = x0+(cx0-x0)*t
|
||||
cx = cx0+(cx1-cx0)*t
|
||||
# a = x1 - 3 * cx1 + 3 * cx0 - x0
|
||||
# b = 3 * (cx1 - 2 * cx0 + x0)
|
||||
# c = 3 * (cx0 - x0)
|
||||
# d = x0
|
||||
# cx = a*t*t*t + b*t*t + c*t + d
|
||||
y1 = cy1+(y1-cy1)*t
|
||||
y0 = y0+(cy0-y0)*t
|
||||
cy0 = cy0+(cy1-cy0)*t
|
||||
cy1 = cy0+(y1-cy0)*t
|
||||
cy0 = y0+(cy0-y0)*t
|
||||
cy = cy0+(cy1-cy0)*t
|
||||
# a = y1 - 3 * cy1 + 3 * cy0 - y0
|
||||
# b = 3 * (cy1 - 2 * cy0 + y0)
|
||||
# c = 3 * (cy0 - y0)
|
||||
# d = y0
|
||||
# cy = a*t*t*t + b*t*t + c*t + d
|
||||
return [cx,cy]
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Processes
|
||||
XY = 0
|
||||
DeltaXY = 1
|
||||
Z = 2
|
||||
Curve = 3
|
||||
Zoom = 4
|
||||
Angle = 5
|
||||
Tone = 6
|
||||
Color = 7
|
||||
Hue = 8
|
||||
Opacity = 9
|
||||
Visible = 10
|
||||
BlendType = 11
|
||||
SE = 12
|
||||
Name = 13
|
||||
Origin = 14
|
||||
Src = 15
|
||||
SrcSize = 16
|
||||
CropBottom = 17
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PictureEx
|
||||
attr_accessor :x # x-coordinate
|
||||
attr_accessor :y # y-coordinate
|
||||
attr_accessor :z # z value
|
||||
attr_accessor :zoom_x # x directional zoom rate
|
||||
attr_accessor :zoom_y # y directional zoom rate
|
||||
attr_accessor :angle # rotation angle
|
||||
attr_accessor :tone # tone
|
||||
attr_accessor :color # color
|
||||
attr_accessor :hue # filename hue
|
||||
attr_accessor :opacity # opacity level
|
||||
attr_accessor :visible # visibility boolean
|
||||
attr_accessor :blend_type # blend method
|
||||
attr_accessor :name # file name
|
||||
attr_accessor :origin # starting point
|
||||
attr_reader :src_rect # source rect
|
||||
attr_reader :cropBottom # crops sprite to above this y-coordinate
|
||||
attr_reader :frameUpdates # Array of processes updated in a frame
|
||||
|
||||
def initialize(z)
|
||||
# process: [type, delay, total_duration, frame_counter, cb, etc.]
|
||||
@processes = []
|
||||
@x = 0.0
|
||||
@y = 0.0
|
||||
@z = z
|
||||
@zoom_x = 100.0
|
||||
@zoom_y = 100.0
|
||||
@angle = 0
|
||||
@rotate_speed = 0
|
||||
@tone = Tone.new(0, 0, 0, 0)
|
||||
@tone_duration = 0
|
||||
@color = Color.new(0, 0, 0, 0)
|
||||
@hue = 0
|
||||
@opacity = 255.0
|
||||
@visible = true
|
||||
@blend_type = 0
|
||||
@name = ""
|
||||
@origin = PictureOrigin::TopLeft
|
||||
@src_rect = Rect.new(0,0,-1,-1)
|
||||
@cropBottom = -1
|
||||
@frameUpdates = []
|
||||
end
|
||||
|
||||
def callback(cb)
|
||||
if cb.is_a?(Proc); proc.call(self)
|
||||
elsif cb.is_a?(Array); cb[0].method(cb[1]).call(self)
|
||||
elsif cb.is_a?(Method); cb.call(self)
|
||||
end
|
||||
end
|
||||
|
||||
def setCallback(delay, cb=nil)
|
||||
delay = ensureDelayAndDuration(delay)
|
||||
@processes.push([nil,delay,0,0,cb])
|
||||
end
|
||||
|
||||
def running?
|
||||
return @processes.length>0
|
||||
end
|
||||
|
||||
def totalDuration
|
||||
ret = 0
|
||||
for process in @processes
|
||||
dur = process[1]+process[2]
|
||||
ret = dur if dur>ret
|
||||
end
|
||||
ret *= 20.0/Graphics.frame_rate
|
||||
return ret.to_i
|
||||
end
|
||||
|
||||
def ensureDelayAndDuration(delay, duration=nil)
|
||||
delay = self.totalDuration if delay<0
|
||||
delay *= Graphics.frame_rate/20.0
|
||||
if !duration.nil?
|
||||
duration *= Graphics.frame_rate/20.0
|
||||
return delay.to_i, duration.to_i
|
||||
end
|
||||
return delay.to_i
|
||||
end
|
||||
|
||||
def ensureDelay(delay)
|
||||
return ensureDelayAndDuration(delay)
|
||||
end
|
||||
|
||||
# speed is the angle to change by in 1/20 of a second. @rotate_speed is the
|
||||
# angle to change by per frame.
|
||||
# NOTE: This is not compatible with manually changing the angle at a certain
|
||||
# point. If you make a sprite auto-rotate, you should not try to alter
|
||||
# the angle another way too.
|
||||
def rotate(speed)
|
||||
@rotate_speed = speed*20.0/Graphics.frame_rate
|
||||
while @rotate_speed<0; @rotate_speed += 360; end
|
||||
@rotate_speed %= 360
|
||||
end
|
||||
|
||||
def erase
|
||||
self.name = ""
|
||||
end
|
||||
|
||||
def clearProcesses
|
||||
@processes = []
|
||||
end
|
||||
|
||||
def adjustPosition(xOffset, yOffset)
|
||||
for process in @processes
|
||||
next if process[0]!=Processes::XY
|
||||
process[5] += xOffset
|
||||
process[6] += yOffset
|
||||
process[7] += xOffset
|
||||
process[8] += yOffset
|
||||
end
|
||||
end
|
||||
|
||||
def move(delay, duration, origin, x, y, zoom_x=100.0, zoom_y=100.0, opacity=255)
|
||||
setOrigin(delay,duration,origin)
|
||||
moveXY(delay,duration,x,y)
|
||||
moveZoomXY(delay,duration,zoom_x,zoom_y)
|
||||
moveOpacity(delay,duration,opacity)
|
||||
end
|
||||
|
||||
def moveXY(delay, duration, x, y, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
@processes.push([Processes::XY,delay,duration,0,cb,@x,@y,x,y])
|
||||
end
|
||||
|
||||
def setXY(delay, x, y, cb=nil)
|
||||
moveXY(delay,0,x,y,cb)
|
||||
end
|
||||
|
||||
def moveCurve(delay, duration, x1, y1, x2, y2, x3, y3, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
@processes.push([Processes::Curve,delay,duration,0,cb,[@x,@y,x1,y1,x2,y2,x3,y3]])
|
||||
end
|
||||
|
||||
def moveDelta(delay, duration, x, y, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
@processes.push([Processes::DeltaXY,delay,duration,0,cb,@x,@y,x,y])
|
||||
end
|
||||
|
||||
def setDelta(delay, x, y, cb=nil)
|
||||
moveDelta(delay,0,x,y,cb)
|
||||
end
|
||||
|
||||
def moveZ(delay, duration, z, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
@processes.push([Processes::Z,delay,duration,0,cb,@z,z])
|
||||
end
|
||||
|
||||
def setZ(delay, z, cb=nil)
|
||||
moveZ(delay,0,z,cb)
|
||||
end
|
||||
|
||||
def moveZoomXY(delay, duration, zoom_x, zoom_y, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
@processes.push([Processes::Zoom,delay,duration,0,cb,@zoom_x,@zoom_y,zoom_x,zoom_y])
|
||||
end
|
||||
|
||||
def setZoomXY(delay, zoom_x, zoom_y, cb=nil)
|
||||
moveZoomXY(delay,0,zoom_x,zoom_y,cb)
|
||||
end
|
||||
|
||||
def moveZoom(delay, duration, zoom, cb=nil)
|
||||
moveZoomXY(delay,duration,zoom,zoom,cb)
|
||||
end
|
||||
|
||||
def setZoom(delay, zoom, cb=nil)
|
||||
moveZoomXY(delay,0,zoom,zoom,cb)
|
||||
end
|
||||
|
||||
def moveAngle(delay, duration, angle, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
@processes.push([Processes::Angle,delay,duration,0,cb,@angle,angle])
|
||||
end
|
||||
|
||||
def setAngle(delay, angle, cb=nil)
|
||||
moveAngle(delay,0,angle,cb)
|
||||
end
|
||||
|
||||
def moveTone(delay, duration, tone, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
target = (tone) ? tone.clone : Tone.new(0,0,0,0)
|
||||
@processes.push([Processes::Tone,delay,duration,0,cb,@tone.clone,target])
|
||||
end
|
||||
|
||||
def setTone(delay, tone, cb=nil)
|
||||
moveTone(delay,0,tone,cb)
|
||||
end
|
||||
|
||||
def moveColor(delay, duration, color, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
target = (color) ? color.clone : Color.new(0,0,0,0)
|
||||
@processes.push([Processes::Color,delay,duration,0,cb,@color.clone,target])
|
||||
end
|
||||
|
||||
def setColor(delay, color, cb=nil)
|
||||
moveColor(delay,0,color,cb)
|
||||
end
|
||||
|
||||
# Hue changes don't actually work.
|
||||
def moveHue(delay, duration, hue, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
@processes.push([Processes::Hue,delay,duration,0,cb,@hue,hue])
|
||||
end
|
||||
|
||||
# Hue changes don't actually work.
|
||||
def setHue(delay, hue, cb=nil)
|
||||
moveHue(delay,0,hue,cb)
|
||||
end
|
||||
|
||||
def moveOpacity(delay, duration, opacity, cb=nil)
|
||||
delay, duration = ensureDelayAndDuration(delay,duration)
|
||||
@processes.push([Processes::Opacity,delay,duration,0,cb,@opacity,opacity])
|
||||
end
|
||||
|
||||
def setOpacity(delay, opacity, cb=nil)
|
||||
moveOpacity(delay,0,opacity,cb)
|
||||
end
|
||||
|
||||
def setVisible(delay, visible, cb=nil)
|
||||
delay = ensureDelay(delay)
|
||||
@processes.push([Processes::Visible,delay,0,0,cb,visible])
|
||||
end
|
||||
|
||||
# Only values of 0 (normal), 1 (additive) and 2 (subtractive) are allowed.
|
||||
def setBlendType(delay, blend, cb=nil)
|
||||
delay = ensureDelayAndDuration(delay)
|
||||
@processes.push([Processes::BlendType,delay,0,0,cb,blend])
|
||||
end
|
||||
|
||||
def setSE(delay, seFile, volume=nil, cb=nil)
|
||||
delay = ensureDelay(delay)
|
||||
@processes.push([Processes::SE,delay,0,0,cb,seFile,volume])
|
||||
end
|
||||
|
||||
def setName(delay, name, cb=nil)
|
||||
delay = ensureDelay(delay)
|
||||
@processes.push([Processes::Name,delay,0,0,cb,name])
|
||||
end
|
||||
|
||||
def setOrigin(delay, origin, cb=nil)
|
||||
delay = ensureDelay(delay)
|
||||
@processes.push([Processes::Origin,delay,0,0,cb,origin])
|
||||
end
|
||||
|
||||
def setSrc(delay, srcX, srcY, cb=nil)
|
||||
delay = ensureDelay(delay)
|
||||
@processes.push([Processes::Src,delay,0,0,cb,srcX,srcY])
|
||||
end
|
||||
|
||||
def setSrcSize(delay, srcWidth, srcHeight, cb=nil)
|
||||
delay = ensureDelay(delay)
|
||||
@processes.push([Processes::SrcSize,delay,0,0,cb,srcWidth,srcHeight])
|
||||
end
|
||||
|
||||
# Used to cut Pokémon sprites off when they faint and sink into the ground.
|
||||
def setCropBottom(delay, y, cb=nil)
|
||||
delay = ensureDelay(delay)
|
||||
@processes.push([Processes::CropBottom,delay,0,0,cb,y])
|
||||
end
|
||||
|
||||
def update
|
||||
procEnded = false
|
||||
@frameUpdates.clear
|
||||
for i in 0...@processes.length
|
||||
process = @processes[i]
|
||||
# Decrease delay of processes that are scheduled to start later
|
||||
if process[1]>=0
|
||||
# Set initial values if the process will start this frame
|
||||
if process[1]==0
|
||||
case process[0]
|
||||
when Processes::XY
|
||||
process[5] = @x
|
||||
process[6] = @y
|
||||
when Processes::DeltaXY
|
||||
process[5] = @x
|
||||
process[6] = @y
|
||||
process[7] += @x
|
||||
process[8] += @y
|
||||
when Processes::Curve
|
||||
process[5][0] = @x
|
||||
process[5][1] = @y
|
||||
when Processes::Z
|
||||
process[5] = @z
|
||||
when Processes::Zoom
|
||||
process[5] = @zoom_x
|
||||
process[6] = @zoom_y
|
||||
when Processes::Angle
|
||||
process[5] = @angle
|
||||
when Processes::Tone
|
||||
process[5] = @tone.clone
|
||||
when Processes::Color
|
||||
process[5] = @color.clone
|
||||
when Processes::Hue
|
||||
process[5] = @hue
|
||||
when Processes::Opacity
|
||||
process[5] = @opacity
|
||||
end
|
||||
end
|
||||
# Decrease delay counter
|
||||
process[1] -= 1
|
||||
# Process hasn't started yet, skip to the next one
|
||||
next if process[1]>=0
|
||||
end
|
||||
# Update process
|
||||
@frameUpdates.push(process[0]) if !@frameUpdates.include?(process[0])
|
||||
fra = (process[2]==0) ? 1 : process[3] # Frame counter
|
||||
dur = (process[2]==0) ? 1 : process[2] # Total duration of process
|
||||
case process[0]
|
||||
when Processes::XY, Processes::DeltaXY
|
||||
@x = process[5] + fra * (process[7] - process[5]) / dur
|
||||
@y = process[6] + fra * (process[8] - process[6]) / dur
|
||||
when Processes::Curve
|
||||
@x, @y = getCubicPoint2(process[5],fra.to_f/dur)
|
||||
when Processes::Z
|
||||
@z = process[5] + fra * (process[6] - process[5]) / dur
|
||||
when Processes::Zoom
|
||||
@zoom_x = process[5] + fra * (process[7] - process[5]) / dur
|
||||
@zoom_y = process[6] + fra * (process[8] - process[6]) / dur
|
||||
when Processes::Angle
|
||||
@angle = process[5] + fra * (process[6] - process[5]) / dur
|
||||
when Processes::Tone
|
||||
@tone.red = process[5].red + fra * (process[6].red - process[5].red) / dur
|
||||
@tone.green = process[5].green + fra * (process[6].green - process[5].green) / dur
|
||||
@tone.blue = process[5].blue + fra * (process[6].blue - process[5].blue) / dur
|
||||
@tone.gray = process[5].gray + fra * (process[6].gray - process[5].gray) / dur
|
||||
when Processes::Color
|
||||
@color.red = process[5].red + fra * (process[6].red - process[5].red) / dur
|
||||
@color.green = process[5].green + fra * (process[6].green - process[5].green) / dur
|
||||
@color.blue = process[5].blue + fra * (process[6].blue - process[5].blue) / dur
|
||||
@color.alpha = process[5].alpha + fra * (process[6].alpha - process[5].alpha) / dur
|
||||
when Processes::Hue
|
||||
@hue = (process[6] - process[5]).to_f / dur
|
||||
when Processes::Opacity
|
||||
@opacity = process[5] + fra * (process[6] - process[5]) / dur
|
||||
when Processes::Visible
|
||||
@visible = process[5]
|
||||
when Processes::BlendType
|
||||
@blend_type = process[5]
|
||||
when Processes::SE
|
||||
pbSEPlay(process[5],process[6])
|
||||
when Processes::Name
|
||||
@name = process[5]
|
||||
when Processes::Origin
|
||||
@origin = process[5]
|
||||
when Processes::Src
|
||||
@src_rect.x = process[5]
|
||||
@src_rect.y = process[6]
|
||||
when Processes::SrcSize
|
||||
@src_rect.width = process[5]
|
||||
@src_rect.height = process[6]
|
||||
when Processes::CropBottom
|
||||
@cropBottom = process[5]
|
||||
end
|
||||
# Increase frame counter
|
||||
process[3] += 1
|
||||
if process[3]>process[2]
|
||||
# Process has ended, erase it
|
||||
callback(process[4]) if process[4]
|
||||
@processes[i] = nil
|
||||
procEnded = true
|
||||
end
|
||||
end
|
||||
# Clear out empty spaces in @processes array caused by finished processes
|
||||
@processes.compact! if procEnded
|
||||
# Add the constant rotation speed
|
||||
if @rotate_speed != 0
|
||||
@frameUpdates.push(Processes::Angle) if !@frameUpdates.include?(Processes::Angle)
|
||||
@angle += @rotate_speed
|
||||
while @angle<0; @angle += 360; end
|
||||
@angle %= 360
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def setPictureSprite(sprite, picture, iconSprite=false)
|
||||
return if picture.frameUpdates.length==0
|
||||
for i in 0...picture.frameUpdates.length
|
||||
case picture.frameUpdates[i]
|
||||
when Processes::XY, Processes::DeltaXY
|
||||
sprite.x = picture.x.round
|
||||
sprite.y = picture.y.round
|
||||
when Processes::Z
|
||||
sprite.z = picture.z
|
||||
when Processes::Zoom
|
||||
sprite.zoom_x = picture.zoom_x / 100.0
|
||||
sprite.zoom_y = picture.zoom_y / 100.0
|
||||
when Processes::Angle
|
||||
sprite.angle = picture.angle
|
||||
when Processes::Tone
|
||||
sprite.tone = picture.tone
|
||||
when Processes::Color
|
||||
sprite.color = picture.color
|
||||
when Processes::Hue
|
||||
# This doesn't do anything.
|
||||
when Processes::BlendType
|
||||
sprite.blend_type = picture.blend_type
|
||||
when Processes::Opacity
|
||||
sprite.opacity = picture.opacity
|
||||
when Processes::Visible
|
||||
sprite.visible = picture.visible
|
||||
when Processes::Name
|
||||
sprite.name = picture.name if iconSprite && sprite.name != picture.name
|
||||
when Processes::Origin
|
||||
case picture.origin
|
||||
when PictureOrigin::TopLeft, PictureOrigin::Left, PictureOrigin::BottomLeft
|
||||
sprite.ox = 0
|
||||
when PictureOrigin::Top, PictureOrigin::Center, PictureOrigin::Bottom
|
||||
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width/2 : 0
|
||||
when PictureOrigin::TopRight, PictureOrigin::Right, PictureOrigin::BottomRight
|
||||
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width : 0
|
||||
end
|
||||
case picture.origin
|
||||
when PictureOrigin::TopLeft, PictureOrigin::Top, PictureOrigin::TopRight
|
||||
sprite.oy = 0
|
||||
when PictureOrigin::Left, PictureOrigin::Center, PictureOrigin::Right
|
||||
sprite.oy = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.height/2 : 0
|
||||
when PictureOrigin::BottomLeft, PictureOrigin::Bottom, PictureOrigin::BottomRight
|
||||
sprite.oy = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.height : 0
|
||||
end
|
||||
when Processes::Src
|
||||
next unless iconSprite && sprite.src_rect
|
||||
sprite.src_rect.x = picture.src_rect.x
|
||||
sprite.src_rect.y = picture.src_rect.y
|
||||
when Processes::SrcSize
|
||||
next unless iconSprite && sprite.src_rect
|
||||
sprite.src_rect.width = picture.src_rect.width
|
||||
sprite.src_rect.height = picture.src_rect.height
|
||||
end
|
||||
end
|
||||
if iconSprite && sprite.src_rect && picture.cropBottom>=0
|
||||
spriteBottom = sprite.y-sprite.oy+sprite.src_rect.height
|
||||
if spriteBottom>picture.cropBottom
|
||||
sprite.src_rect.height = [picture.cropBottom-sprite.y+sprite.oy,0].max
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setPictureIconSprite(sprite, picture)
|
||||
setPictureSprite(sprite,picture,true)
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PictureOrigin
|
||||
TopLeft = 0
|
||||
Center = 1
|
||||
TopRight = 2
|
||||
BottomLeft = 3
|
||||
LowerLeft = 3
|
||||
BottomRight = 4
|
||||
LowerRight = 4
|
||||
Top = 5
|
||||
Bottom = 6
|
||||
Left = 7
|
||||
Right = 8
|
||||
end
|
||||
|
||||
|
||||
|
||||
def pbTextBitmap(text, maxwidth=Graphics.width)
|
||||
dims = []
|
||||
tmp = Bitmap.new(maxwidth,Graphics.height)
|
||||
pbSetSystemFont(tmp)
|
||||
drawFormattedTextEx(tmp,0,0,maxwidth,text,Color.new(248,248,248),Color.new(168,184,184))
|
||||
return tmp
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PictureSprite < SpriteWrapper
|
||||
def initialize(viewport, picture)
|
||||
super(viewport)
|
||||
@picture = picture
|
||||
@pictureBitmap = nil
|
||||
@customBitmap = nil
|
||||
@customBitmapIsBitmap = true
|
||||
@hue = 0
|
||||
update
|
||||
end
|
||||
|
||||
def dispose
|
||||
@pictureBitmap.dispose if @pictureBitmap
|
||||
super
|
||||
end
|
||||
|
||||
# Doesn't free the bitmap
|
||||
def setCustomBitmap(bitmap)
|
||||
@customBitmap = bitmap
|
||||
@customBitmapIsBitmap = @customBitmap.is_a?(Bitmap)
|
||||
end
|
||||
|
||||
def update
|
||||
super
|
||||
@pictureBitmap.update if @pictureBitmap
|
||||
# If picture file name is different from current one
|
||||
if @customBitmap && @picture.name==""
|
||||
self.bitmap = (@customBitmapIsBitmap) ? @customBitmap : @customBitmap.bitmap
|
||||
elsif @picture_name != @picture.name || @picture.hue.to_i != @hue.to_i
|
||||
# Remember file name to instance variables
|
||||
@picture_name = @picture.name
|
||||
@hue = @picture.hue.to_i
|
||||
# If file name is not empty
|
||||
if @picture_name == ""
|
||||
@pictureBitmap.dispose if @pictureBitmap
|
||||
@pictureBitmap = nil
|
||||
self.visible = false
|
||||
return
|
||||
end
|
||||
# Get picture graphic
|
||||
@pictureBitmap.dispose if @pictureBitmap
|
||||
@pictureBitmap = AnimatedBitmap.new(@picture_name, @hue)
|
||||
self.bitmap = (@pictureBitmap) ? @pictureBitmap.bitmap : nil
|
||||
elsif @picture_name == ""
|
||||
# Set sprite to invisible
|
||||
self.visible = false
|
||||
return
|
||||
end
|
||||
setPictureSprite(self,@picture)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class EventScene
|
||||
attr_accessor :onCTrigger,:onBTrigger,:onUpdate
|
||||
|
||||
def initialize(viewport=nil)
|
||||
@viewport = viewport
|
||||
@onCTrigger = Event.new
|
||||
@onBTrigger = Event.new
|
||||
@onUpdate = Event.new
|
||||
@pictures = []
|
||||
@picturesprites = []
|
||||
@usersprites = []
|
||||
@disposed = false
|
||||
end
|
||||
|
||||
def dispose
|
||||
return if disposed?
|
||||
for sprite in @picturesprites
|
||||
sprite.dispose
|
||||
end
|
||||
for sprite in @usersprites
|
||||
sprite.dispose
|
||||
end
|
||||
@onCTrigger.clear
|
||||
@onBTrigger.clear
|
||||
@onUpdate.clear
|
||||
@pictures.clear
|
||||
@picturesprites.clear
|
||||
@usersprites.clear
|
||||
@disposed = true
|
||||
end
|
||||
|
||||
def disposed?
|
||||
return @disposed
|
||||
end
|
||||
|
||||
def addBitmap(x, y, bitmap)
|
||||
# _bitmap_ can be a Bitmap or an AnimatedBitmap
|
||||
# (update method isn't called if it's animated)
|
||||
# EventScene doesn't take ownership of the passed-in bitmap
|
||||
num = @pictures.length
|
||||
picture = PictureEx.new(num)
|
||||
picture.setXY(0,x,y)
|
||||
picture.setVisible(0,true)
|
||||
@pictures[num] = picture
|
||||
@picturesprites[num] = PictureSprite.new(@viewport,picture)
|
||||
@picturesprites[num].setCustomBitmap(bitmap)
|
||||
return picture
|
||||
end
|
||||
|
||||
def addLabel(x, y, width, text)
|
||||
addBitmap(x,y,pbTextBitmap(text,width))
|
||||
end
|
||||
|
||||
def addImage(x, y, name)
|
||||
num = @pictures.length
|
||||
picture = PictureEx.new(num)
|
||||
picture.name = name
|
||||
picture.setXY(0,x,y)
|
||||
picture.setVisible(0,true)
|
||||
@pictures[num] = picture
|
||||
@picturesprites[num] = PictureSprite.new(@viewport,picture)
|
||||
return picture
|
||||
end
|
||||
|
||||
def addUserSprite(sprite)
|
||||
@usersprites.push(sprite)
|
||||
end
|
||||
|
||||
def getPicture(num)
|
||||
return @pictures[num]
|
||||
end
|
||||
|
||||
def wait(frames)
|
||||
frames.times { update }
|
||||
end
|
||||
|
||||
def pictureWait(extraframes=0)
|
||||
loop do
|
||||
hasRunning = false
|
||||
for pic in @pictures
|
||||
hasRunning = true if pic.running?
|
||||
end
|
||||
break if !hasRunning
|
||||
update
|
||||
end
|
||||
extraframes.times { update }
|
||||
end
|
||||
|
||||
def update
|
||||
return if disposed?
|
||||
Graphics.update
|
||||
Input.update
|
||||
for picture in @pictures
|
||||
picture.update
|
||||
end
|
||||
for sprite in @picturesprites
|
||||
sprite.update
|
||||
end
|
||||
for sprite in @usersprites
|
||||
next if !sprite || sprite.disposed? || !sprite.is_a?(Sprite)
|
||||
sprite.update
|
||||
end
|
||||
@onUpdate.trigger(self)
|
||||
if Input.trigger?(Input::B)
|
||||
@onBTrigger.trigger(self)
|
||||
elsif Input.trigger?(Input::C)
|
||||
@onCTrigger.trigger(self)
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
while !disposed?
|
||||
update
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
def pbEventScreen(cls)
|
||||
pbFadeOutIn {
|
||||
viewport = Viewport.new(0,0,Graphics.width,Graphics.height)
|
||||
viewport.z = 99999
|
||||
PBDebug.logonerr {
|
||||
cls.new(viewport).main
|
||||
}
|
||||
viewport.dispose
|
||||
}
|
||||
end
|
||||
172
Data/Scripts/008_Objects and windows/011_Interpolators.rb
Normal file
172
Data/Scripts/008_Objects and windows/011_Interpolators.rb
Normal file
@@ -0,0 +1,172 @@
|
||||
class Interpolator
|
||||
ZOOM_X = 1
|
||||
ZOOM_Y = 2
|
||||
X = 3
|
||||
Y = 4
|
||||
OPACITY = 5
|
||||
COLOR = 6
|
||||
WAIT = 7
|
||||
|
||||
def initialize
|
||||
@tweening = false
|
||||
@tweensteps = []
|
||||
@sprite = nil
|
||||
@frames = 0
|
||||
@step = 0
|
||||
end
|
||||
|
||||
def tweening?
|
||||
return @tweening
|
||||
end
|
||||
|
||||
def tween(sprite,items,frames)
|
||||
@tweensteps = []
|
||||
if sprite && !sprite.disposed? && frames>0
|
||||
@frames = frames
|
||||
@step = 0
|
||||
@sprite = sprite
|
||||
for item in items
|
||||
case item[0]
|
||||
when ZOOM_X
|
||||
@tweensteps[item[0]] = [sprite.zoom_x,item[1]-sprite.zoom_x]
|
||||
when ZOOM_Y
|
||||
@tweensteps[item[0]] = [sprite.zoom_y,item[1]-sprite.zoom_y]
|
||||
when X
|
||||
@tweensteps[item[0]] = [sprite.x,item[1]-sprite.x]
|
||||
when Y
|
||||
@tweensteps[item[0]] = [sprite.y,item[1]-sprite.y]
|
||||
when OPACITY
|
||||
@tweensteps[item[0]] = [sprite.opacity,item[1]-sprite.opacity]
|
||||
when COLOR
|
||||
@tweensteps[item[0]] = [sprite.color.clone,Color.new(
|
||||
item[1].red-sprite.color.red,
|
||||
item[1].green-sprite.color.green,
|
||||
item[1].blue-sprite.color.blue,
|
||||
item[1].alpha-sprite.color.alpha
|
||||
)]
|
||||
end
|
||||
end
|
||||
@tweening = true
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if @tweening
|
||||
t = (@step*1.0)/@frames
|
||||
for i in 0...@tweensteps.length
|
||||
item = @tweensteps[i]
|
||||
next if !item
|
||||
case i
|
||||
when ZOOM_X
|
||||
@sprite.zoom_x = item[0]+item[1]*t
|
||||
when ZOOM_Y
|
||||
@sprite.zoom_y = item[0]+item[1]*t
|
||||
when X
|
||||
@sprite.x = item[0]+item[1]*t
|
||||
when Y
|
||||
@sprite.y = item[0]+item[1]*t
|
||||
when OPACITY
|
||||
@sprite.opacity = item[0]+item[1]*t
|
||||
when COLOR
|
||||
@sprite.color = Color.new(
|
||||
item[0].red+item[1].red*t,
|
||||
item[0].green+item[1].green*t,
|
||||
item[0].blue+item[1].blue*t,
|
||||
item[0].alpha+item[1].alpha*t
|
||||
)
|
||||
end
|
||||
end
|
||||
@step += 1
|
||||
if @step==@frames
|
||||
@step = 0
|
||||
@frames = 0
|
||||
@tweening = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class RectInterpolator
|
||||
def initialize(oldrect,newrect,frames)
|
||||
restart(oldrect,newrect,frames)
|
||||
end
|
||||
|
||||
def restart(oldrect,newrect,frames)
|
||||
@oldrect = oldrect
|
||||
@newrect = newrect
|
||||
@frames = [frames,1].max
|
||||
@curframe = 0
|
||||
@rect = oldrect.clone
|
||||
end
|
||||
|
||||
def set(rect)
|
||||
rect.set(@rect.x,@rect.y,@rect.width,@rect.height)
|
||||
end
|
||||
|
||||
def done?
|
||||
@curframe>@frames
|
||||
end
|
||||
|
||||
def update
|
||||
return if done?
|
||||
t = (@curframe*1.0/@frames)
|
||||
x1 = @oldrect.x
|
||||
x2 = @newrect.x
|
||||
x = x1+t*(x2-x1)
|
||||
y1 = @oldrect.y
|
||||
y2 = @newrect.y
|
||||
y = y1+t*(y2-y1)
|
||||
rx1 = @oldrect.x+@oldrect.width
|
||||
rx2 = @newrect.x+@newrect.width
|
||||
rx = rx1+t*(rx2-rx1)
|
||||
ry1 = @oldrect.y+@oldrect.height
|
||||
ry2 = @newrect.y+@newrect.height
|
||||
ry = ry1+t*(ry2-ry1)
|
||||
minx = x<rx ? x : rx
|
||||
maxx = x>rx ? x : rx
|
||||
miny = y<ry ? y : ry
|
||||
maxy = y>ry ? y : ry
|
||||
@rect.set(minx,miny,maxx-minx,maxy-miny)
|
||||
@curframe += 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PointInterpolator
|
||||
attr_reader :x
|
||||
attr_reader :y
|
||||
|
||||
def initialize(oldx,oldy,newx,newy,frames)
|
||||
restart(oldx,oldy,newx,newy,frames)
|
||||
end
|
||||
|
||||
def restart(oldx,oldy,newx,newy,frames)
|
||||
@oldx = oldx
|
||||
@oldy = oldy
|
||||
@newx = newx
|
||||
@newy = newy
|
||||
@frames = frames
|
||||
@curframe = 0
|
||||
@x = oldx
|
||||
@y = oldy
|
||||
end
|
||||
|
||||
def done?
|
||||
@curframe>@frames
|
||||
end
|
||||
|
||||
def update
|
||||
return if done?
|
||||
t = (@curframe*1.0/@frames)
|
||||
rx1 = @oldx
|
||||
rx2 = @newx
|
||||
@x = rx1+t*(rx2-rx1)
|
||||
ry1 = @oldy
|
||||
ry2 = @newy
|
||||
@y = ry1+t*(ry2-ry1)
|
||||
@curframe += 1
|
||||
end
|
||||
end
|
||||
239
Data/Scripts/009_Scenes/001_Scene_Map.rb
Normal file
239
Data/Scripts/009_Scenes/001_Scene_Map.rb
Normal file
@@ -0,0 +1,239 @@
|
||||
#===============================================================================
|
||||
# ** Modified Scene_Map class for Pokémon.
|
||||
#-------------------------------------------------------------------------------
|
||||
#
|
||||
#===============================================================================
|
||||
class Scene_Map
|
||||
attr_reader :spritesetGlobal
|
||||
|
||||
def spriteset
|
||||
for i in @spritesets.values
|
||||
return i if i.map==$game_map
|
||||
end
|
||||
return @spritesets.values[0]
|
||||
end
|
||||
|
||||
def createSpritesets
|
||||
@spritesetGlobal = Spriteset_Global.new
|
||||
@spritesets = {}
|
||||
for map in $MapFactory.maps
|
||||
@spritesets[map.map_id] = Spriteset_Map.new(map)
|
||||
end
|
||||
$MapFactory.setSceneStarted(self)
|
||||
updateSpritesets
|
||||
end
|
||||
|
||||
def createSingleSpriteset(map)
|
||||
temp = $scene.spriteset.getAnimations
|
||||
@spritesets[map] = Spriteset_Map.new($MapFactory.maps[map])
|
||||
$scene.spriteset.restoreAnimations(temp)
|
||||
$MapFactory.setSceneStarted(self)
|
||||
updateSpritesets
|
||||
end
|
||||
|
||||
def disposeSpritesets
|
||||
return if !@spritesets
|
||||
for i in @spritesets.keys
|
||||
next if !@spritesets[i]
|
||||
@spritesets[i].dispose
|
||||
@spritesets[i] = nil
|
||||
end
|
||||
@spritesets.clear
|
||||
@spritesets = {}
|
||||
@spritesetGlobal.dispose
|
||||
@spritesetGlobal = nil
|
||||
end
|
||||
|
||||
def autofade(mapid)
|
||||
playingBGM = $game_system.playing_bgm
|
||||
playingBGS = $game_system.playing_bgs
|
||||
return if !playingBGM && !playingBGS
|
||||
map = pbLoadRxData(sprintf("Data/Map%03d",mapid))
|
||||
if playingBGM && map.autoplay_bgm
|
||||
if (PBDayNight.isNight? rescue false)
|
||||
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name && playingBGM.name!=map.bgm.name+"_n"
|
||||
else
|
||||
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name
|
||||
end
|
||||
end
|
||||
if playingBGS && map.autoplay_bgs
|
||||
pbBGMFade(0.8) if playingBGS.name!=map.bgs.name
|
||||
end
|
||||
Graphics.frame_reset
|
||||
end
|
||||
|
||||
def transfer_player(cancelVehicles=true)
|
||||
$game_temp.player_transferring = false
|
||||
pbCancelVehicles($game_temp.player_new_map_id) if cancelVehicles
|
||||
autofade($game_temp.player_new_map_id)
|
||||
pbBridgeOff
|
||||
if $game_map.map_id!=$game_temp.player_new_map_id
|
||||
$MapFactory.setup($game_temp.player_new_map_id)
|
||||
end
|
||||
$game_player.moveto($game_temp.player_new_x, $game_temp.player_new_y)
|
||||
case $game_temp.player_new_direction
|
||||
when 2; $game_player.turn_down
|
||||
when 4; $game_player.turn_left
|
||||
when 6; $game_player.turn_right
|
||||
when 8; $game_player.turn_up
|
||||
end
|
||||
$game_player.straighten
|
||||
$game_map.update
|
||||
disposeSpritesets
|
||||
GC.start
|
||||
createSpritesets
|
||||
if $game_temp.transition_processing
|
||||
$game_temp.transition_processing = false
|
||||
Graphics.transition(20)
|
||||
end
|
||||
$game_map.autoplay
|
||||
Graphics.frame_reset
|
||||
Input.update
|
||||
end
|
||||
|
||||
def call_name
|
||||
$game_temp.name_calling = false
|
||||
$game_player.straighten
|
||||
$game_map.update
|
||||
end
|
||||
|
||||
def call_menu
|
||||
$game_temp.menu_calling = false
|
||||
$game_temp.in_menu = true
|
||||
$game_player.straighten
|
||||
$game_map.update
|
||||
sscene = PokemonPauseMenu_Scene.new
|
||||
sscreen = PokemonPauseMenu.new(sscene)
|
||||
sscreen.pbStartPokemonMenu
|
||||
$game_temp.in_menu = false
|
||||
end
|
||||
|
||||
def call_debug
|
||||
$game_temp.debug_calling = false
|
||||
pbPlayDecisionSE
|
||||
$game_player.straighten
|
||||
pbFadeOutIn { pbDebugMenu }
|
||||
end
|
||||
|
||||
def miniupdate
|
||||
$PokemonTemp.miniupdate = true
|
||||
loop do
|
||||
updateMaps
|
||||
$game_player.update
|
||||
$game_system.update
|
||||
$game_screen.update
|
||||
break unless $game_temp.player_transferring
|
||||
transfer_player
|
||||
break if $game_temp.transition_processing
|
||||
end
|
||||
updateSpritesets
|
||||
$PokemonTemp.miniupdate = false
|
||||
end
|
||||
|
||||
def updateMaps
|
||||
for map in $MapFactory.maps
|
||||
map.update
|
||||
end
|
||||
$MapFactory.updateMaps(self)
|
||||
end
|
||||
|
||||
def updateSpritesets
|
||||
@spritesets = {} if !@spritesets
|
||||
keys = @spritesets.keys.clone
|
||||
for i in keys
|
||||
if !$MapFactory.hasMap?(i)
|
||||
@spritesets[i].dispose if @spritesets[i]
|
||||
@spritesets[i] = nil
|
||||
@spritesets.delete(i)
|
||||
else
|
||||
@spritesets[i].update
|
||||
end
|
||||
end
|
||||
@spritesetGlobal.update
|
||||
for map in $MapFactory.maps
|
||||
@spritesets[map.map_id] = Spriteset_Map.new(map) if !@spritesets[map.map_id]
|
||||
end
|
||||
Events.onMapUpdate.trigger(self)
|
||||
end
|
||||
|
||||
def update
|
||||
loop do
|
||||
updateMaps
|
||||
pbMapInterpreter.update
|
||||
$game_player.update
|
||||
$game_system.update
|
||||
$game_screen.update
|
||||
break unless $game_temp.player_transferring
|
||||
transfer_player
|
||||
break if $game_temp.transition_processing
|
||||
end
|
||||
updateSpritesets
|
||||
if $game_temp.to_title
|
||||
$scene = pbCallTitle
|
||||
return
|
||||
end
|
||||
if $game_temp.transition_processing
|
||||
$game_temp.transition_processing = false
|
||||
if $game_temp.transition_name == ""
|
||||
Graphics.transition(20)
|
||||
else
|
||||
Graphics.transition(40, "Graphics/Transitions/" + $game_temp.transition_name)
|
||||
end
|
||||
end
|
||||
return if $game_temp.message_window_showing
|
||||
if !pbMapInterpreterRunning?
|
||||
if Input.trigger?(Input::C)
|
||||
$PokemonTemp.hiddenMoveEventCalling = true
|
||||
elsif Input.trigger?(Input::B)
|
||||
unless $game_system.menu_disabled or $game_player.moving?
|
||||
$game_temp.menu_calling = true
|
||||
$game_temp.menu_beep = true
|
||||
end
|
||||
elsif Input.trigger?(Input::F5)
|
||||
unless $game_player.moving?
|
||||
$PokemonTemp.keyItemCalling = true
|
||||
end
|
||||
elsif Input.trigger?(Input::A)
|
||||
if $PokemonSystem.runstyle==1
|
||||
$PokemonGlobal.runtoggle = !$PokemonGlobal.runtoggle
|
||||
end
|
||||
elsif Input.press?(Input::F9)
|
||||
$game_temp.debug_calling = true if $DEBUG
|
||||
end
|
||||
end
|
||||
unless $game_player.moving?
|
||||
if $game_temp.name_calling; call_name
|
||||
elsif $game_temp.menu_calling; call_menu
|
||||
elsif $game_temp.debug_calling; call_debug
|
||||
# elsif $game_temp.battle_calling; call_battle
|
||||
# elsif $game_temp.shop_calling; call_shop
|
||||
# elsif $game_temp.save_calling; call_save
|
||||
elsif $PokemonTemp.keyItemCalling
|
||||
$PokemonTemp.keyItemCalling = false
|
||||
$game_player.straighten
|
||||
pbUseKeyItem
|
||||
elsif $PokemonTemp.hiddenMoveEventCalling
|
||||
$PokemonTemp.hiddenMoveEventCalling = false
|
||||
$game_player.straighten
|
||||
Events.onAction.trigger(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def main
|
||||
createSpritesets
|
||||
Graphics.transition(20)
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
update
|
||||
break if $scene != self
|
||||
end
|
||||
Graphics.freeze
|
||||
disposeSpritesets
|
||||
if $game_temp.to_title
|
||||
Graphics.transition(20)
|
||||
Graphics.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
134
Data/Scripts/009_Scenes/002_Scene_Intro.rb
Normal file
134
Data/Scripts/009_Scenes/002_Scene_Intro.rb
Normal file
@@ -0,0 +1,134 @@
|
||||
class IntroEventScene < EventScene
|
||||
TICKS_PER_PIC = 40 # 20 ticks per second, so 2 seconds
|
||||
TICKS_PER_ENTER_FLASH = 40
|
||||
FADE_TICKS = 8
|
||||
|
||||
def initialize(pics,splash,viewport=nil)
|
||||
super(nil)
|
||||
@pics = pics
|
||||
@splash = splash
|
||||
@pic = addImage(0,0,"")
|
||||
@pic.setOpacity(0,0) # set opacity to 0 after waiting 0 frames
|
||||
@pic2 = addImage(0,0,"") # flashing "Press Enter" picture
|
||||
@pic2.setOpacity(0,0)
|
||||
@index = 0
|
||||
data_system = pbLoadRxData("Data/System")
|
||||
pbBGMPlay(data_system.title_bgm)
|
||||
openPic(self,nil)
|
||||
end
|
||||
|
||||
def openPic(scene,args)
|
||||
onCTrigger.clear
|
||||
@pic.name = "Graphics/Titles/"+@pics[@index]
|
||||
# fade to opacity 255 in FADE_TICKS ticks after waiting 0 frames
|
||||
@pic.moveOpacity(0,FADE_TICKS,255)
|
||||
pictureWait
|
||||
@timer = 0 # reset the timer
|
||||
onUpdate.set(method(:picUpdate)) # call picUpdate every frame
|
||||
onCTrigger.set(method(:closePic)) # call closePic when C key is pressed
|
||||
end
|
||||
|
||||
def closePic(scene,args)
|
||||
onUpdate.clear
|
||||
onCTrigger.clear
|
||||
@pic.moveOpacity(0,FADE_TICKS,0)
|
||||
pictureWait
|
||||
@index += 1 # Move to the next picture
|
||||
if @index>=@pics.length
|
||||
openSplash(scene,args)
|
||||
else
|
||||
openPic(scene,args)
|
||||
end
|
||||
end
|
||||
|
||||
def picUpdate(scene,args)
|
||||
@timer += 1
|
||||
if @timer>TICKS_PER_PIC*Graphics.frame_rate/20
|
||||
@timer = 0
|
||||
closePic(scene,args) # Close the picture
|
||||
end
|
||||
end
|
||||
|
||||
def openSplash(scene,args)
|
||||
onUpdate.clear
|
||||
onCTrigger.clear
|
||||
@pic.name = "Graphics/Titles/"+@splash
|
||||
@pic.moveOpacity(0,FADE_TICKS,255)
|
||||
@pic2.name = "Graphics/Titles/start"
|
||||
@pic2.setXY(0,0,322)
|
||||
@pic2.setVisible(0,true)
|
||||
@pic2.moveOpacity(0,FADE_TICKS,255)
|
||||
pictureWait
|
||||
onUpdate.set(method(:splashUpdate)) # call splashUpdate every frame
|
||||
onCTrigger.set(method(:closeSplash)) # call closeSplash when C key is pressed
|
||||
end
|
||||
|
||||
def closeSplash(scene,args)
|
||||
onUpdate.clear
|
||||
onCTrigger.clear
|
||||
# Play random cry
|
||||
cry = pbCryFile(1+rand(PBSpecies.maxValue))
|
||||
pbSEPlay(cry,80,100) if cry
|
||||
@pic.moveXY(0,20,0,0)
|
||||
pictureWait
|
||||
# Fade out
|
||||
@pic.moveOpacity(0,FADE_TICKS,0)
|
||||
@pic2.clearProcesses
|
||||
@pic2.moveOpacity(0,FADE_TICKS,0)
|
||||
pbBGMStop(1.0)
|
||||
pictureWait
|
||||
scene.dispose # Close the scene
|
||||
sscene = PokemonLoad_Scene.new
|
||||
sscreen = PokemonLoadScreen.new(sscene)
|
||||
sscreen.pbStartLoadScreen
|
||||
end
|
||||
|
||||
def closeSplashDelete(scene,args)
|
||||
onUpdate.clear
|
||||
onCTrigger.clear
|
||||
# Play random cry
|
||||
cry = pbCryFile(1+rand(PBSpecies.maxValue))
|
||||
pbSEPlay(cry,80,100) if cry
|
||||
@pic.moveXY(0,20,0,0)
|
||||
pictureWait
|
||||
# Fade out
|
||||
@pic.moveOpacity(0,FADE_TICKS,0)
|
||||
@pic2.clearProcesses
|
||||
@pic2.moveOpacity(0,FADE_TICKS,0)
|
||||
pbBGMStop(1.0)
|
||||
pictureWait
|
||||
scene.dispose # Close the scene
|
||||
sscene = PokemonLoad_Scene.new
|
||||
sscreen = PokemonLoadScreen.new(sscene)
|
||||
sscreen.pbStartDeleteScreen
|
||||
end
|
||||
|
||||
def splashUpdate(scene,args)
|
||||
# Flashing of "Press Enter" picture
|
||||
if !@pic2.running?
|
||||
@pic2.moveOpacity(TICKS_PER_ENTER_FLASH*2/10,TICKS_PER_ENTER_FLASH*4/10,0)
|
||||
@pic2.moveOpacity(TICKS_PER_ENTER_FLASH*6/10,TICKS_PER_ENTER_FLASH*4/10,255)
|
||||
end
|
||||
if Input.press?(Input::DOWN) &&
|
||||
Input.press?(Input::B) &&
|
||||
Input.press?(Input::CTRL)
|
||||
closeSplashDelete(scene,args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class Scene_Intro
|
||||
def initialize(pics, splash = nil)
|
||||
@pics = pics
|
||||
@splash = splash
|
||||
end
|
||||
|
||||
def main
|
||||
Graphics.transition(0)
|
||||
@eventscene = IntroEventScene.new(@pics,@splash)
|
||||
@eventscene.main
|
||||
Graphics.freeze
|
||||
end
|
||||
end
|
||||
46
Data/Scripts/009_Scenes/003_Scene_Controls.rb
Normal file
46
Data/Scripts/009_Scenes/003_Scene_Controls.rb
Normal file
@@ -0,0 +1,46 @@
|
||||
#==============================================================================
|
||||
# * Scene_Controls
|
||||
#------------------------------------------------------------------------------
|
||||
# Shows a help screen listing the keyboard controls.
|
||||
# Display with:
|
||||
# pbEventScreen(ButtonEventScene)
|
||||
#==============================================================================
|
||||
class ButtonEventScene < EventScene
|
||||
def initialize(viewport=nil)
|
||||
super
|
||||
Graphics.freeze
|
||||
addImage(0,0,"Graphics/Pictures/helpbg")
|
||||
@labels=[
|
||||
addLabel(52*2,13*2,Graphics.width*3/4,_INTL("Moves the main character. Also used to scroll through list entries.")),
|
||||
addLabel(52*2,53*2,Graphics.width*3/4,_INTL("Used to confirm a choice, check things, and talk to people.")),
|
||||
addLabel(52*2,93*2,Graphics.width*3/4,_INTL("Used to exit, cancel a choice or mode, and open the pause menu.")),
|
||||
addLabel(52*2,133*2,Graphics.width*3/4,_INTL("Hold down while walking to run.")),
|
||||
addLabel(52*2,157*2,Graphics.width*3/4,_INTL("Press to use a registered Key Item."))
|
||||
]
|
||||
@keys=[
|
||||
addImage(26*2,18*2,"Graphics/Pictures/helpArrowKeys"),
|
||||
addImage(26*2,59*2,"Graphics/Pictures/helpCkey"),
|
||||
addImage(26*2,99*2,"Graphics/Pictures/helpXkey"),
|
||||
addImage(26*2,130*2,"Graphics/Pictures/helpZkey"),
|
||||
addImage(26*2,154*2,"Graphics/Pictures/helpFkey")
|
||||
]
|
||||
for key in @keys
|
||||
key.origin=PictureOrigin::Top
|
||||
end
|
||||
for i in 0...5 # Make everything show (almost) immediately
|
||||
@keys[i].setOrigin(0,PictureOrigin::Top)
|
||||
@keys[i].setOpacity(0,255)
|
||||
end
|
||||
pictureWait # Update event scene with the changes
|
||||
Graphics.transition(20)
|
||||
# Go to next screen when user presses C
|
||||
onCTrigger.set(method(:pbOnScreen1))
|
||||
end
|
||||
|
||||
def pbOnScreen1(scene,args)
|
||||
# End scene
|
||||
Graphics.freeze
|
||||
scene.dispose
|
||||
Graphics.transition(20)
|
||||
end
|
||||
end
|
||||
52
Data/Scripts/009_Scenes/004_Scene_Movie.rb
Normal file
52
Data/Scripts/009_Scenes/004_Scene_Movie.rb
Normal file
@@ -0,0 +1,52 @@
|
||||
#===============================================================================
|
||||
# ** Scene_Movie class, created by SoundSpawn, fixed by Popper.
|
||||
#-------------------------------------------------------------------------------
|
||||
# Instruction
|
||||
# 1) Movies must be in a new folder called "Movies" in your directory.
|
||||
# 2) If you call this script from an event, e.g.
|
||||
# Call Script: $scene = Scene_Movie.new("INTRO")
|
||||
# 3) Have fun playing movies with this script!
|
||||
#===============================================================================
|
||||
class Scene_Movie
|
||||
def initialize(movie)
|
||||
@movie_name = RTP.getPath("Movies\\"+movie+".avi").gsub(/\//,"\\")
|
||||
end
|
||||
|
||||
def main
|
||||
@temp = Win32API.pbFindRgssWindow.to_s
|
||||
movie = Win32API.new('winmm','mciSendString','%w(p,p,l,l)','V')
|
||||
x=movie.call("open \""+@movie_name+
|
||||
"\" alias FILE style 1073741824 parent " + @temp.to_s,0,0,0)
|
||||
@message = Win32API.new('user32','SendMessage','%w(l,l,l,l)','V')
|
||||
@detector = Win32API.new('user32','GetSystemMetrics','%w(l)','L')
|
||||
@width = @detector.call(0)
|
||||
if @width == 640
|
||||
#fullscreen
|
||||
Graphics.update
|
||||
sleep(0.1)
|
||||
Graphics.update
|
||||
sleep(0.1)
|
||||
Graphics.update
|
||||
sleep(0.1)
|
||||
#fullscreen
|
||||
end
|
||||
status = " " * 255
|
||||
x=movie.call("play FILE",0,0,0)
|
||||
loop do
|
||||
sleep(0.1)
|
||||
@message.call(@temp.to_i,11,0,0)
|
||||
Graphics.update
|
||||
@message.call(@temp.to_i,11,1,0)
|
||||
Input.update
|
||||
movie.call("status FILE mode",status,255,0)
|
||||
true_status = status.unpack("aaaa")
|
||||
break if true_status.to_s != "play"
|
||||
if Input.trigger?(Input::B)
|
||||
movie.call("close FILE",0,0,0)
|
||||
$scene = Scene_Map.new
|
||||
break
|
||||
end
|
||||
end
|
||||
$scene = Scene_Map.new
|
||||
end
|
||||
end
|
||||
231
Data/Scripts/009_Scenes/005_Scene_Credits.rb
Normal file
231
Data/Scripts/009_Scenes/005_Scene_Credits.rb
Normal file
@@ -0,0 +1,231 @@
|
||||
# Backgrounds to show in credits. Found in Graphics/Titles/ folder
|
||||
CreditsBackgroundList = ["credits1","credits2","credits3","credits4","credits5"]
|
||||
CreditsMusic = "Credits"
|
||||
CreditsScrollSpeed = 2
|
||||
CreditsFrequency = 9 # Number of seconds per credits slide
|
||||
CREDITS_OUTLINE = Color.new(0,0,128, 255)
|
||||
CREDITS_SHADOW = Color.new(0,0,0, 100)
|
||||
CREDITS_FILL = Color.new(255,255,255, 255)
|
||||
|
||||
#==============================================================================
|
||||
# * Scene_Credits
|
||||
#------------------------------------------------------------------------------
|
||||
# Scrolls the credits you make below. Original Author unknown.
|
||||
#
|
||||
## Edited by MiDas Mike so it doesn't play over the Title, but runs by calling
|
||||
# the following:
|
||||
# $scene = Scene_Credits.new
|
||||
#
|
||||
## New Edit 3/6/2007 11:14 PM by AvatarMonkeyKirby.
|
||||
# Ok, what I've done is changed the part of the script that was supposed to make
|
||||
# the credits automatically end so that way they actually end! Yes, they will
|
||||
# actually end when the credits are finished! So, that will make the people you
|
||||
# should give credit to now is: Unknown, MiDas Mike, and AvatarMonkeyKirby.
|
||||
# -sincerly yours,
|
||||
# Your Beloved
|
||||
# Oh yea, and I also added a line of code that fades out the BGM so it fades
|
||||
# sooner and smoother.
|
||||
#
|
||||
## New Edit 24/1/2012 by Maruno.
|
||||
# Added the ability to split a line into two halves with <s>, with each half
|
||||
# aligned towards the centre. Please also credit me if used.
|
||||
#
|
||||
## New Edit 22/2/2012 by Maruno.
|
||||
# Credits now scroll properly when played with a zoom factor of 0.5. Music can
|
||||
# now be defined. Credits can't be skipped during their first play.
|
||||
#
|
||||
## New Edit 25/3/2020 by Maruno.
|
||||
# Scroll speed is now independent of frame rate. Now supports non-integer values
|
||||
# for CreditsScrollSpeed.
|
||||
#
|
||||
## New Edit 21/8/2020 by Marin.
|
||||
# Now automatically inserts the credits from the plugins that have been
|
||||
# registered through the PluginManager module.
|
||||
#==============================================================================
|
||||
|
||||
class Scene_Credits
|
||||
|
||||
# This next piece of code is the credits.
|
||||
#Start Editing
|
||||
CREDIT=<<_END_
|
||||
|
||||
Your credits go here.
|
||||
|
||||
Your credits go here.
|
||||
|
||||
Your credits go here.
|
||||
|
||||
Your credits go here.
|
||||
|
||||
Your credits go here.
|
||||
|
||||
{INSERTS_PLUGIN_CREDITS_DO_NOT_REMOVE}
|
||||
"Pokémon Essentials" was created by:
|
||||
Flameguru
|
||||
Poccil (Peter O.)
|
||||
Maruno
|
||||
|
||||
With contributions from:
|
||||
AvatarMonkeyKirby<s>Marin
|
||||
Boushy<s>MiDas Mike
|
||||
Brother1440<s>Near Fantastica
|
||||
FL.<s>PinkMan
|
||||
Genzai Kawakami<s>Popper
|
||||
help-14<s>Rataime
|
||||
IceGod64<s>SoundSpawn
|
||||
Jacob O. Wobbrock<s>the__end
|
||||
KitsuneKouta<s>Venom12
|
||||
Lisa Anthony<s>Wachunga
|
||||
Luka S.J.<s>
|
||||
and everyone else who helped out
|
||||
|
||||
|
||||
|
||||
"RPG Maker XP" by:
|
||||
Enterbrain
|
||||
|
||||
Pokémon is owned by:
|
||||
The Pokémon Company
|
||||
Nintendo
|
||||
Affiliated with Game Freak
|
||||
|
||||
This is a non-profit fan-made game.
|
||||
No copyright infringements intended.
|
||||
Please support the official games!
|
||||
|
||||
_END_
|
||||
#Stop Editing
|
||||
|
||||
def main
|
||||
#-------------------------------
|
||||
# Animated Background Setup
|
||||
#-------------------------------
|
||||
@sprite = IconSprite.new(0,0)
|
||||
@backgroundList = CreditsBackgroundList
|
||||
@frameCounter = 0
|
||||
# Number of game frames per background frame
|
||||
@framesPerBackground = CreditsFrequency * Graphics.frame_rate
|
||||
@sprite.setBitmap("Graphics/Titles/"+@backgroundList[0])
|
||||
#------------------
|
||||
# Credits text Setup
|
||||
#------------------
|
||||
plugin_credits = ""
|
||||
PluginManager.plugins.each do |plugin|
|
||||
pcred = PluginManager.credits(plugin)
|
||||
plugin_credits << "\"#{plugin}\" version #{PluginManager.version(plugin)}\n"
|
||||
if pcred.size >= 5
|
||||
plugin_credits << pcred[0] + "\n"
|
||||
i = 1
|
||||
until i >= pcred.size
|
||||
plugin_credits << pcred[i] + "<s>" + (pcred[i + 1] || "") + "\n"
|
||||
i += 2
|
||||
end
|
||||
else
|
||||
pcred.each do |name|
|
||||
plugin_credits << name + "\n"
|
||||
end
|
||||
end
|
||||
plugin_credits << "\n"
|
||||
end
|
||||
CREDIT.gsub!(/{INSERTS_PLUGIN_CREDITS_DO_NOT_REMOVE}/, plugin_credits)
|
||||
credit_lines = CREDIT.split(/\n/)
|
||||
credit_bitmap = Bitmap.new(Graphics.width,32 * credit_lines.size)
|
||||
credit_lines.each_index do |i|
|
||||
line = credit_lines[i]
|
||||
line = line.split("<s>")
|
||||
# LINE ADDED: If you use in your own game, you should remove this line
|
||||
pbSetSystemFont(credit_bitmap) # <--- This line was added
|
||||
x = 0
|
||||
xpos = 0
|
||||
align = 1 # Centre align
|
||||
linewidth = Graphics.width
|
||||
for j in 0...line.length
|
||||
if line.length>1
|
||||
xpos = (j==0) ? 0 : 20 + Graphics.width/2
|
||||
align = (j==0) ? 2 : 0 # Right align : left align
|
||||
linewidth = Graphics.width/2 - 20
|
||||
end
|
||||
credit_bitmap.font.color = CREDITS_SHADOW
|
||||
credit_bitmap.draw_text(xpos,i * 32 + 8,linewidth,32,line[j],align)
|
||||
credit_bitmap.font.color = CREDITS_OUTLINE
|
||||
credit_bitmap.draw_text(xpos + 2,i * 32 - 2,linewidth,32,line[j],align)
|
||||
credit_bitmap.draw_text(xpos,i * 32 - 2,linewidth,32,line[j],align)
|
||||
credit_bitmap.draw_text(xpos - 2,i * 32 - 2,linewidth,32,line[j],align)
|
||||
credit_bitmap.draw_text(xpos + 2,i * 32,linewidth,32,line[j],align)
|
||||
credit_bitmap.draw_text(xpos - 2,i * 32,linewidth,32,line[j],align)
|
||||
credit_bitmap.draw_text(xpos + 2,i * 32 + 2,linewidth,32,line[j],align)
|
||||
credit_bitmap.draw_text(xpos,i * 32 + 2,linewidth,32,line[j],align)
|
||||
credit_bitmap.draw_text(xpos - 2,i * 32 + 2,linewidth,32,line[j],align)
|
||||
credit_bitmap.font.color = CREDITS_FILL
|
||||
credit_bitmap.draw_text(xpos,i * 32,linewidth,32,line[j],align)
|
||||
end
|
||||
end
|
||||
@trim = Graphics.height/10
|
||||
@realOY = -(Graphics.height-@trim) # -430
|
||||
@oyChangePerFrame = CreditsScrollSpeed*20.0/Graphics.frame_rate
|
||||
@credit_sprite = Sprite.new(Viewport.new(0,@trim,Graphics.width,Graphics.height-(@trim*2)))
|
||||
@credit_sprite.bitmap = credit_bitmap
|
||||
@credit_sprite.z = 9998
|
||||
@credit_sprite.oy = @realOY
|
||||
@bg_index = 0
|
||||
@zoom_adjustment = 1.0/$ResizeFactor
|
||||
@last_flag = false
|
||||
#--------
|
||||
# Setup
|
||||
#--------
|
||||
# Stops all audio but background music
|
||||
previousBGM = $game_system.getPlayingBGM
|
||||
pbMEStop
|
||||
pbBGSStop
|
||||
pbSEStop
|
||||
pbBGMFade(2.0)
|
||||
pbBGMPlay(CreditsMusic)
|
||||
Graphics.transition(20)
|
||||
loop do
|
||||
Graphics.update
|
||||
Input.update
|
||||
update
|
||||
break if $scene != self
|
||||
end
|
||||
Graphics.freeze
|
||||
@sprite.dispose
|
||||
@credit_sprite.dispose
|
||||
$PokemonGlobal.creditsPlayed = true
|
||||
pbBGMPlay(previousBGM)
|
||||
end
|
||||
|
||||
# Check if the credits should be cancelled
|
||||
def cancel?
|
||||
if Input.trigger?(Input::C) && $PokemonGlobal.creditsPlayed
|
||||
$scene = Scene_Map.new
|
||||
pbBGMFade(1.0)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Checks if credits bitmap has reached its ending point
|
||||
def last?
|
||||
if @realOY > @credit_sprite.bitmap.height + @trim
|
||||
$scene = ($game_map) ? Scene_Map.new : nil
|
||||
pbBGMFade(2.0)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def update
|
||||
@frameCounter += 1
|
||||
# Go to next slide
|
||||
if @frameCounter >= @framesPerBackground
|
||||
@frameCounter -= @framesPerBackground
|
||||
@bg_index += 1
|
||||
@bg_index = 0 if @bg_index >= @backgroundList.length
|
||||
@sprite.setBitmap("Graphics/Titles/"+@backgroundList[@bg_index])
|
||||
end
|
||||
return if cancel?
|
||||
return if last?
|
||||
@realOY += @oyChangePerFrame
|
||||
@credit_sprite.oy = @realOY
|
||||
end
|
||||
end
|
||||
1617
Data/Scripts/009_Scenes/006_Transitions.rb
Normal file
1617
Data/Scripts/009_Scenes/006_Transitions.rb
Normal file
File diff suppressed because it is too large
Load Diff
472
Data/Scripts/010_Data/001_MiscData.rb
Normal file
472
Data/Scripts/010_Data/001_MiscData.rb
Normal file
@@ -0,0 +1,472 @@
|
||||
#===============================================================================
|
||||
# Phone data
|
||||
#===============================================================================
|
||||
class PhoneDatabase
|
||||
attr_accessor :generics
|
||||
attr_accessor :greetings
|
||||
attr_accessor :greetingsMorning
|
||||
attr_accessor :greetingsEvening
|
||||
attr_accessor :bodies1
|
||||
attr_accessor :bodies2
|
||||
attr_accessor :battleRequests
|
||||
attr_accessor :trainers
|
||||
|
||||
def initialize
|
||||
@generics = []
|
||||
@greetings = []
|
||||
@greetingsMorning = []
|
||||
@greetingsEvening = []
|
||||
@bodies1 = []
|
||||
@bodies2 = []
|
||||
@battleRequests = []
|
||||
@trainers = []
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module PhoneMsgType
|
||||
Generic = 0
|
||||
Greeting = 1
|
||||
Body = 2
|
||||
BattleRequest = 3
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Global and map metadata
|
||||
#===============================================================================
|
||||
MetadataHome = 1
|
||||
MetadataWildBattleBGM = 2
|
||||
MetadataTrainerBattleBGM = 3
|
||||
MetadataWildVictoryME = 4
|
||||
MetadataTrainerVictoryME = 5
|
||||
MetadataWildCaptureME = 6
|
||||
MetadataSurfBGM = 7
|
||||
MetadataBicycleBGM = 8
|
||||
MetadataPlayerA = 9
|
||||
MetadataPlayerB = 10
|
||||
MetadataPlayerC = 11
|
||||
MetadataPlayerD = 12
|
||||
MetadataPlayerE = 13
|
||||
MetadataPlayerF = 14
|
||||
MetadataPlayerG = 15
|
||||
MetadataPlayerH = 16
|
||||
|
||||
MetadataOutdoor = 1
|
||||
MetadataShowArea = 2
|
||||
MetadataBicycle = 3
|
||||
MetadataBicycleAlways = 4
|
||||
MetadataHealingSpot = 5
|
||||
MetadataWeather = 6
|
||||
MetadataMapPosition = 7
|
||||
MetadataDiveMap = 8
|
||||
MetadataDarkMap = 9
|
||||
MetadataSafariMap = 10
|
||||
MetadataSnapEdges = 11
|
||||
MetadataDungeon = 12
|
||||
MetadataBattleBack = 13
|
||||
MetadataMapWildBattleBGM = 14
|
||||
MetadataMapTrainerBattleBGM = 15
|
||||
MetadataMapWildVictoryME = 16
|
||||
MetadataMapTrainerVictoryME = 17
|
||||
MetadataMapWildCaptureME = 18
|
||||
MetadataMapSize = 19
|
||||
MetadataEnvironment = 20
|
||||
|
||||
|
||||
|
||||
module PokemonMetadata
|
||||
GlobalTypes = {
|
||||
"Home" => [MetadataHome, "uuuu"],
|
||||
"WildBattleBGM" => [MetadataWildBattleBGM, "s"],
|
||||
"TrainerBattleBGM" => [MetadataTrainerBattleBGM, "s"],
|
||||
"WildVictoryME" => [MetadataWildVictoryME, "s"],
|
||||
"TrainerVictoryME" => [MetadataTrainerVictoryME, "s"],
|
||||
"WildCaptureME" => [MetadataWildCaptureME, "s"],
|
||||
"SurfBGM" => [MetadataSurfBGM, "s"],
|
||||
"BicycleBGM" => [MetadataBicycleBGM, "s"],
|
||||
"PlayerA" => [MetadataPlayerA, "esssssss",:PBTrainers],
|
||||
"PlayerB" => [MetadataPlayerB, "esssssss",:PBTrainers],
|
||||
"PlayerC" => [MetadataPlayerC, "esssssss",:PBTrainers],
|
||||
"PlayerD" => [MetadataPlayerD, "esssssss",:PBTrainers],
|
||||
"PlayerE" => [MetadataPlayerE, "esssssss",:PBTrainers],
|
||||
"PlayerF" => [MetadataPlayerF, "esssssss",:PBTrainers],
|
||||
"PlayerG" => [MetadataPlayerG, "esssssss",:PBTrainers],
|
||||
"PlayerH" => [MetadataPlayerH, "esssssss",:PBTrainers]
|
||||
}
|
||||
NonGlobalTypes = {
|
||||
"Outdoor" => [MetadataOutdoor, "b"],
|
||||
"ShowArea" => [MetadataShowArea, "b"],
|
||||
"Bicycle" => [MetadataBicycle, "b"],
|
||||
"BicycleAlways" => [MetadataBicycleAlways, "b"],
|
||||
"HealingSpot" => [MetadataHealingSpot, "uuu"],
|
||||
"Weather" => [MetadataWeather, "eu",:PBFieldWeather],
|
||||
"MapPosition" => [MetadataMapPosition, "uuu"],
|
||||
"DiveMap" => [MetadataDiveMap, "u"],
|
||||
"DarkMap" => [MetadataDarkMap, "b"],
|
||||
"SafariMap" => [MetadataSafariMap, "b"],
|
||||
"SnapEdges" => [MetadataSnapEdges, "b"],
|
||||
"Dungeon" => [MetadataDungeon, "b"],
|
||||
"BattleBack" => [MetadataBattleBack, "s"],
|
||||
"WildBattleBGM" => [MetadataMapWildBattleBGM, "s"],
|
||||
"TrainerBattleBGM" => [MetadataMapTrainerBattleBGM, "s"],
|
||||
"WildVictoryME" => [MetadataMapWildVictoryME, "s"],
|
||||
"TrainerVictoryME" => [MetadataMapTrainerVictoryME, "s"],
|
||||
"WildCaptureME" => [MetadataMapWildCaptureME, "s"],
|
||||
"MapSize" => [MetadataMapSize, "us"],
|
||||
"Environment" => [MetadataEnvironment, "e",:PBEnvironment]
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Pokémon data
|
||||
#===============================================================================
|
||||
SpeciesType1 = 0
|
||||
SpeciesType2 = 1
|
||||
SpeciesBaseStats = 2
|
||||
SpeciesGenderRate = 3
|
||||
SpeciesGrowthRate = 4
|
||||
SpeciesBaseExp = 5
|
||||
SpeciesEffortPoints = 6
|
||||
SpeciesRareness = 7
|
||||
SpeciesHappiness = 8
|
||||
SpeciesAbilities = 9
|
||||
SpeciesHiddenAbility = 10
|
||||
SpeciesCompatibility = 11
|
||||
SpeciesStepsToHatch = 12
|
||||
SpeciesHeight = 13
|
||||
SpeciesWeight = 14
|
||||
SpeciesColor = 15
|
||||
SpeciesShape = 16
|
||||
SpeciesHabitat = 17
|
||||
SpeciesWildItemCommon = 18
|
||||
SpeciesWildItemUncommon = 19
|
||||
SpeciesWildItemRare = 20
|
||||
SpeciesIncense = 21
|
||||
SpeciesPokedexForm = 22 # For alternate forms
|
||||
SpeciesMegaStone = 23 # For alternate forms
|
||||
SpeciesMegaMove = 24 # For alternate forms
|
||||
SpeciesUnmegaForm = 25 # For alternate forms
|
||||
SpeciesMegaMessage = 26 # For alternate forms
|
||||
|
||||
MetricBattlerPlayerX = 0
|
||||
MetricBattlerPlayerY = 1
|
||||
MetricBattlerEnemyX = 2
|
||||
MetricBattlerEnemyY = 3
|
||||
MetricBattlerAltitude = 4
|
||||
MetricBattlerShadowX = 5
|
||||
MetricBattlerShadowSize = 6
|
||||
|
||||
|
||||
|
||||
module PokemonSpeciesData
|
||||
def self.requiredValues(compilingForms=false)
|
||||
ret = {
|
||||
"Type1" => [SpeciesType1, "e",:PBTypes],
|
||||
"BaseStats" => [SpeciesBaseStats, "vvvvvv"],
|
||||
"BaseEXP" => [SpeciesBaseExp, "v"],
|
||||
"EffortPoints" => [SpeciesEffortPoints, "uuuuuu"],
|
||||
"Rareness" => [SpeciesRareness, "u"],
|
||||
"Happiness" => [SpeciesHappiness, "u"],
|
||||
"Compatibility" => [SpeciesCompatibility, "eE",:PBEggGroups,:PBEggGroups],
|
||||
"StepsToHatch" => [SpeciesStepsToHatch, "v"],
|
||||
"Height" => [SpeciesHeight, "f"],
|
||||
"Weight" => [SpeciesWeight, "f"],
|
||||
"Color" => [SpeciesColor, "e",:PBColors],
|
||||
"Shape" => [SpeciesShape, "u"],
|
||||
"Moves" => [0, "*ue",nil,:PBMoves],
|
||||
"Kind" => [0, "s"],
|
||||
"Pokedex" => [0, "q"]
|
||||
}
|
||||
if !compilingForms
|
||||
ret["GenderRate"] = [SpeciesGenderRate, "e",:PBGenderRates]
|
||||
ret["GrowthRate"] = [SpeciesGrowthRate, "e",:PBGrowthRates]
|
||||
ret["Name"] = [0, "s"]
|
||||
ret["InternalName"] = [0, "n"]
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def self.optionalValues(compilingForms=false)
|
||||
ret = {
|
||||
"Type2" => [SpeciesType2, "e",:PBTypes],
|
||||
"Abilities" => [SpeciesAbilities, "eE",:PBAbilities,:PBAbilities],
|
||||
"HiddenAbility" => [SpeciesHiddenAbility, "eEEE",:PBAbilities,:PBAbilities,
|
||||
:PBAbilities,:PBAbilities],
|
||||
"Habitat" => [SpeciesHabitat, "e",:PBHabitats],
|
||||
"WildItemCommon" => [SpeciesWildItemCommon, "e",:PBItems],
|
||||
"WildItemUncommon" => [SpeciesWildItemUncommon, "e",:PBItems],
|
||||
"WildItemRare" => [SpeciesWildItemRare, "e",:PBItems],
|
||||
"BattlerPlayerX" => [MetricBattlerPlayerX, "i"],
|
||||
"BattlerPlayerY" => [MetricBattlerPlayerY, "i"],
|
||||
"BattlerEnemyX" => [MetricBattlerEnemyX, "i"],
|
||||
"BattlerEnemyY" => [MetricBattlerEnemyY, "i"],
|
||||
"BattlerAltitude" => [MetricBattlerAltitude, "i"],
|
||||
"BattlerShadowX" => [MetricBattlerShadowX, "i"],
|
||||
"BattlerShadowSize" => [MetricBattlerShadowSize, "u"],
|
||||
"EggMoves" => [0, "*e",:PBMoves],
|
||||
"FormName" => [0, "q"],
|
||||
"Evolutions" => [0, "*ses",nil,:PBEvolution,nil]
|
||||
}
|
||||
if compilingForms
|
||||
ret["PokedexForm"] = [SpeciesPokedexForm, "u"]
|
||||
ret["MegaStone"] = [SpeciesMegaStone, "e",:PBItems]
|
||||
ret["MegaMove"] = [SpeciesMegaMove, "e",:PBMoves]
|
||||
ret["UnmegaForm"] = [SpeciesUnmegaForm, "u"]
|
||||
ret["MegaMessage"] = [SpeciesMegaMessage, "u"]
|
||||
else
|
||||
ret["Incense"] = [SpeciesIncense, "e",:PBItems]
|
||||
ret["RegionalNumbers"] = [0, "*u"]
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Manipulation methods for metadata, phone data and Pokémon species data
|
||||
#===============================================================================
|
||||
class PokemonTemp
|
||||
attr_accessor :metadata
|
||||
attr_accessor :townMapData
|
||||
attr_accessor :encountersData
|
||||
attr_accessor :phoneData
|
||||
attr_accessor :regionalDexes
|
||||
attr_accessor :speciesData
|
||||
attr_accessor :speciesEggMoves
|
||||
attr_accessor :speciesMetrics
|
||||
attr_accessor :speciesMovesets
|
||||
attr_accessor :speciesTMData
|
||||
attr_accessor :speciesShadowMovesets
|
||||
attr_accessor :pokemonFormToSpecies
|
||||
attr_accessor :trainerTypesData
|
||||
attr_accessor :trainersData
|
||||
attr_accessor :moveToAnim
|
||||
attr_accessor :battleAnims
|
||||
end
|
||||
|
||||
|
||||
|
||||
def pbLoadMetadata
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.metadata
|
||||
$PokemonTemp.metadata = load_data("Data/metadata.dat") || []
|
||||
end
|
||||
return $PokemonTemp.metadata
|
||||
end
|
||||
|
||||
def pbGetMetadata(mapid,metadataType)
|
||||
meta = pbLoadMetadata
|
||||
return meta[mapid][metadataType] if meta[mapid]
|
||||
return nil
|
||||
end
|
||||
|
||||
def pbLoadTownMapData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.townMapData
|
||||
$PokemonTemp.townMapData = load_data("Data/town_map.dat")
|
||||
end
|
||||
return $PokemonTemp.townMapData
|
||||
end
|
||||
|
||||
def pbLoadEncountersData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.encountersData
|
||||
if pbRgssExists?("Data/encounters.dat")
|
||||
$PokemonTemp.encountersData = load_data("Data/encounters.dat")
|
||||
end
|
||||
end
|
||||
return $PokemonTemp.encountersData
|
||||
end
|
||||
|
||||
def pbLoadPhoneData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.phoneData
|
||||
if pbRgssExists?("Data/phone.dat")
|
||||
$PokemonTemp.phoneData = load_data("Data/phone.dat")
|
||||
end
|
||||
end
|
||||
return $PokemonTemp.phoneData
|
||||
end
|
||||
|
||||
def pbLoadRegionalDexes
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.regionalDexes
|
||||
$PokemonTemp.regionalDexes = load_data("Data/regional_dexes.dat")
|
||||
end
|
||||
return $PokemonTemp.regionalDexes
|
||||
end
|
||||
|
||||
def pbLoadSpeciesData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.speciesData
|
||||
$PokemonTemp.speciesData = load_data("Data/species.dat") || []
|
||||
end
|
||||
return $PokemonTemp.speciesData
|
||||
end
|
||||
|
||||
def pbGetSpeciesData(species,form=0,speciesDataType=-1)
|
||||
species = getID(PBSpecies,species)
|
||||
s = pbGetFSpeciesFromForm(species,form)
|
||||
speciesData = pbLoadSpeciesData
|
||||
if speciesDataType<0
|
||||
return speciesData[s] || []
|
||||
end
|
||||
return speciesData[s][speciesDataType] if speciesData[s] && speciesData[s][speciesDataType]
|
||||
case speciesDataType
|
||||
when SpeciesType2; return nil
|
||||
when SpeciesBaseStats; return [1,1,1,1,1,1]
|
||||
when SpeciesEffortPoints; return [0,0,0,0,0,0]
|
||||
when SpeciesStepsToHatch, SpeciesHeight, SpeciesWeight; return 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
def pbLoadEggMovesData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.speciesEggMoves
|
||||
$PokemonTemp.speciesEggMoves = load_data("Data/species_eggmoves.dat") || []
|
||||
end
|
||||
return $PokemonTemp.speciesEggMoves
|
||||
end
|
||||
|
||||
def pbGetSpeciesEggMoves(species,form=0)
|
||||
species = getID(PBSpecies,species)
|
||||
s = pbGetFSpeciesFromForm(species,form)
|
||||
eggMovesData = pbLoadEggMovesData
|
||||
return eggMovesData[s] if eggMovesData[s]
|
||||
return []
|
||||
end
|
||||
|
||||
def pbLoadSpeciesMetrics
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.speciesMetrics
|
||||
$PokemonTemp.speciesMetrics = load_data("Data/species_metrics.dat") || []
|
||||
end
|
||||
return $PokemonTemp.speciesMetrics
|
||||
end
|
||||
|
||||
def pbLoadMovesetsData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.speciesMovesets
|
||||
$PokemonTemp.speciesMovesets = load_data("Data/species_movesets.dat") || []
|
||||
end
|
||||
return $PokemonTemp.speciesMovesets
|
||||
end
|
||||
|
||||
def pbGetSpeciesMoveset(species,form=0)
|
||||
species = getID(PBSpecies,species)
|
||||
s = pbGetFSpeciesFromForm(species,form)
|
||||
movesetsData = pbLoadMovesetsData
|
||||
return movesetsData[s] if movesetsData[s]
|
||||
return []
|
||||
end
|
||||
|
||||
def pbLoadSpeciesTMData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.speciesTMData
|
||||
$PokemonTemp.speciesTMData = load_data("Data/tm.dat") || []
|
||||
end
|
||||
return $PokemonTemp.speciesTMData
|
||||
end
|
||||
|
||||
def pbLoadShadowMovesets
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.speciesShadowMovesets
|
||||
$PokemonTemp.speciesShadowMovesets = load_data("Data/shadow_movesets.dat") || []
|
||||
end
|
||||
return $PokemonTemp.speciesShadowMovesets
|
||||
end
|
||||
|
||||
def pbLoadFormToSpecies
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.pokemonFormToSpecies
|
||||
$PokemonTemp.pokemonFormToSpecies = load_data("Data/form2species.dat")
|
||||
end
|
||||
return $PokemonTemp.pokemonFormToSpecies
|
||||
end
|
||||
|
||||
def pbLoadTrainerTypesData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.trainerTypesData
|
||||
$PokemonTemp.trainerTypesData = load_data("Data/trainer_types.dat") || []
|
||||
end
|
||||
return $PokemonTemp.trainerTypesData
|
||||
end
|
||||
|
||||
def pbGetTrainerTypeData(type)
|
||||
data = pbLoadTrainerTypesData
|
||||
return data[type] if data
|
||||
return nil
|
||||
end
|
||||
|
||||
def pbLoadTrainersData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.trainersData
|
||||
$PokemonTemp.trainersData = load_data("Data/trainers.dat") || []
|
||||
end
|
||||
return $PokemonTemp.trainersData
|
||||
end
|
||||
|
||||
def pbGetTrainerData(trainerID,trainerName,partyID=0)
|
||||
trainersData = pbLoadTrainersData
|
||||
ret = nil
|
||||
for t in trainersData
|
||||
next if t[0]!=trainerID || t[1]!=trainerName || t[4]!=partyID
|
||||
ret = t
|
||||
break
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbLoadMoveToAnim
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.moveToAnim
|
||||
$PokemonTemp.moveToAnim = load_data("Data/move2anim.dat") || []
|
||||
end
|
||||
return $PokemonTemp.moveToAnim
|
||||
end
|
||||
|
||||
def pbLoadBattleAnimations
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.battleAnims
|
||||
if pbRgssExists?("Data/PkmnAnimations.rxdata")
|
||||
$PokemonTemp.battleAnims = load_data("Data/PkmnAnimations.rxdata")
|
||||
end
|
||||
end
|
||||
return $PokemonTemp.battleAnims
|
||||
end
|
||||
|
||||
def pbClearData
|
||||
if $PokemonTemp
|
||||
$PokemonTemp.metadata = nil
|
||||
$PokemonTemp.townMapData = nil
|
||||
$PokemonTemp.encountersData = nil
|
||||
$PokemonTemp.phoneData = nil
|
||||
$PokemonTemp.regionalDexes = nil
|
||||
$PokemonTemp.speciesData = nil
|
||||
$PokemonTemp.speciesEggMoves = nil
|
||||
$PokemonTemp.speciesMetrics = nil
|
||||
$PokemonTemp.speciesMovesets = nil
|
||||
$PokemonTemp.speciesTMData = nil
|
||||
$PokemonTemp.speciesShadowMovesets = nil
|
||||
$PokemonTemp.pokemonFormToSpecies = nil
|
||||
$PokemonTemp.trainerTypesData = nil
|
||||
$PokemonTemp.trainersData = nil
|
||||
$PokemonTemp.moveToAnim = nil
|
||||
$PokemonTemp.battleAnims = nil
|
||||
end
|
||||
MapFactoryHelper.clear
|
||||
$PokemonEncounters.setup($game_map.map_id) if $game_map && $PokemonEncounters
|
||||
if pbRgssExists?("Data/Tilesets.rxdata")
|
||||
$data_tilesets = load_data("Data/Tilesets.rxdata")
|
||||
end
|
||||
if pbRgssExists?("Data/Tilesets.rvdata")
|
||||
$data_tilesets = load_data("Data/Tilesets.rvdata")
|
||||
end
|
||||
end
|
||||
104
Data/Scripts/010_Data/002_PBMove.rb
Normal file
104
Data/Scripts/010_Data/002_PBMove.rb
Normal file
@@ -0,0 +1,104 @@
|
||||
MOVE_ID = 0
|
||||
MOVE_INTERNAL_NAME = 1
|
||||
MOVE_NAME = 2
|
||||
MOVE_FUNCTION_CODE = 3
|
||||
MOVE_BASE_DAMAGE = 4
|
||||
MOVE_TYPE = 5
|
||||
MOVE_CATEGORY = 6
|
||||
MOVE_ACCURACY = 7
|
||||
MOVE_TOTAL_PP = 8
|
||||
MOVE_EFFECT_CHANCE = 9
|
||||
MOVE_TARGET = 10
|
||||
MOVE_PRIORITY = 11
|
||||
MOVE_FLAGS = 12
|
||||
MOVE_DESCRIPTION = 13
|
||||
|
||||
|
||||
|
||||
class PokemonTemp
|
||||
attr_accessor :movesData
|
||||
end
|
||||
|
||||
|
||||
|
||||
def pbLoadMovesData
|
||||
$PokemonTemp = PokemonTemp.new if !$PokemonTemp
|
||||
if !$PokemonTemp.movesData
|
||||
if pbRgssExists?("Data/moves.dat")
|
||||
$PokemonTemp.movesData = load_data("Data/moves.dat")
|
||||
else
|
||||
$PokemonTemp.movesData = []
|
||||
end
|
||||
end
|
||||
return $PokemonTemp.movesData
|
||||
end
|
||||
|
||||
def pbGetMoveData(moveID,moveDataType=-1)
|
||||
meta = pbLoadMovesData
|
||||
if moveDataType<0
|
||||
return meta[moveID] || []
|
||||
end
|
||||
return meta[moveID][moveDataType] if meta[moveID]
|
||||
return nil
|
||||
end
|
||||
|
||||
alias __moveData__pbClearData pbClearData
|
||||
def pbClearData
|
||||
$PokemonTemp.movesData = nil if $PokemonTemp
|
||||
__moveData__pbClearData
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PBMoveData
|
||||
attr_reader :function,:basedamage,:type,:accuracy,:category
|
||||
attr_reader :totalpp,:addlEffect,:target,:priority,:flags
|
||||
|
||||
def initialize(moveid)
|
||||
moveData = pbGetMoveData(moveID)
|
||||
@function = moveData[MOVE_FUNCTION_CODE]
|
||||
@basedamage = moveData[MOVE_BASE_DAMAGE]
|
||||
@type = moveData[MOVE_TYPE]
|
||||
@category = moveData[MOVE_CATEGORY]
|
||||
@accuracy = moveData[MOVE_ACCURACY]
|
||||
@totalpp = moveData[MOVE_TOTAL_PP]
|
||||
@addlEffect = moveData[MOVE_EFFECT_CHANCE]
|
||||
@target = moveData[MOVE_TARGET]
|
||||
@priority = moveData[MOVE_PRIORITY]
|
||||
@flags = moveData[MOVE_FLAGS]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PBMove
|
||||
attr_reader(:id) # This move's ID
|
||||
attr_accessor(:pp) # The amount of PP remaining for this move
|
||||
attr_accessor(:ppup) # The number of PP Ups used for this move
|
||||
|
||||
# Initializes this object to the specified move ID.
|
||||
def initialize(moveID)
|
||||
@id = moveID
|
||||
@pp = pbGetMoveData(moveID,MOVE_TOTAL_PP) || 0
|
||||
@ppup = 0
|
||||
end
|
||||
|
||||
# Changes this move's ID, and caps the PP amount if it is now greater than the
|
||||
# new move's total PP.
|
||||
def id=(value)
|
||||
oldID = @id
|
||||
@id = value
|
||||
@pp = [@pp,self.totalpp].min if oldID>0
|
||||
end
|
||||
|
||||
# Gets this move's type.
|
||||
def type
|
||||
return pbGetMoveData(@id,MOVE_TYPE) || 0
|
||||
end
|
||||
|
||||
# Gets the maximum PP for this move.
|
||||
def totalpp
|
||||
maxPP = pbGetMoveData(@id,MOVE_TOTAL_PP) || 0
|
||||
return maxPP+maxPP*@ppup/5
|
||||
end
|
||||
end
|
||||
28
Data/Scripts/010_Data/003_PBStatuses.rb
Normal file
28
Data/Scripts/010_Data/003_PBStatuses.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
#70925035
|
||||
begin
|
||||
module PBStatuses
|
||||
NONE = 0
|
||||
SLEEP = 1
|
||||
POISON = 2
|
||||
BURN = 3
|
||||
PARALYSIS = 4
|
||||
FROZEN = 5
|
||||
|
||||
def self.getName(id)
|
||||
id = getID(PBStatuses,id)
|
||||
names = [
|
||||
_INTL("healthy"),
|
||||
_INTL("asleep"),
|
||||
_INTL("poisoned"),
|
||||
_INTL("burned"),
|
||||
_INTL("paralyzed"),
|
||||
_INTL("frozen")
|
||||
]
|
||||
return names[id]
|
||||
end end
|
||||
|
||||
rescue Exception
|
||||
if $!.is_a?(SystemExit) || "#{$!.class}"=="Reset"
|
||||
raise $!
|
||||
end
|
||||
end
|
||||
90
Data/Scripts/010_Data/004_PBTypes_Extra.rb
Normal file
90
Data/Scripts/010_Data/004_PBTypes_Extra.rb
Normal file
@@ -0,0 +1,90 @@
|
||||
module PBTypeEffectiveness
|
||||
INEFFECTIVE = 0
|
||||
NOT_EFFECTIVE_ONE = 1
|
||||
NORMAL_EFFECTIVE_ONE = 2
|
||||
SUPER_EFFECTIVE_ONE = 4
|
||||
NORMAL_EFFECTIVE = NORMAL_EFFECTIVE_ONE ** 3
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PBTypes
|
||||
@@TypeData = nil
|
||||
|
||||
def PBTypes.loadTypeData
|
||||
if !@@TypeData
|
||||
@@TypeData = load_data("Data/types.dat")
|
||||
@@TypeData[0].freeze
|
||||
@@TypeData[1].freeze
|
||||
@@TypeData[2].freeze
|
||||
@@TypeData.freeze
|
||||
end
|
||||
return @@TypeData
|
||||
end
|
||||
|
||||
def PBTypes.regularTypesCount
|
||||
ret = 0
|
||||
for i in 0..PBTypes.maxValue
|
||||
next if PBTypes.isPseudoType?(i) || isConst?(i,PBTypes,:SHADOW)
|
||||
ret += 1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def PBTypes.isPseudoType?(type)
|
||||
return PBTypes.loadTypeData[0].include?(type)
|
||||
end
|
||||
|
||||
def PBTypes.isSpecialType?(type)
|
||||
return PBTypes.loadTypeData[1].include?(type)
|
||||
end
|
||||
|
||||
def PBTypes.getEffectiveness(attackType,targetType)
|
||||
return PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if !targetType || targetType<0
|
||||
return PBTypes.loadTypeData[2][attackType*(PBTypes.maxValue+1)+targetType]
|
||||
end
|
||||
|
||||
def PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2=nil,targetType3=nil)
|
||||
mod1 = PBTypes.getEffectiveness(attackType,targetType1)
|
||||
mod2 = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE
|
||||
mod3 = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE
|
||||
if targetType2!=nil && targetType2>=0 && targetType1!=targetType2
|
||||
mod2 = PBTypes.getEffectiveness(attackType,targetType2)
|
||||
end
|
||||
if targetType3!=nil && targetType3>=0 &&
|
||||
targetType1!=targetType3 && targetType2!=targetType3
|
||||
mod3 = PBTypes.getEffectiveness(attackType,targetType3)
|
||||
end
|
||||
return mod1*mod2*mod3
|
||||
end
|
||||
|
||||
def PBTypes.ineffective?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
|
||||
return attackType==PBTypeEffectiveness::INEFFECTIVE if !targetType1
|
||||
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
|
||||
return e==PBTypeEffectiveness::INEFFECTIVE
|
||||
end
|
||||
|
||||
def PBTypes.notVeryEffective?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
|
||||
return attackType>PBTypeEffectiveness::INEFFECTIVE && attackType<PBTypeEffectiveness::NORMAL_EFFECTIVE if !targetType1
|
||||
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
|
||||
return e>PBTypeEffectiveness::INEFFECTIVE && e<PBTypeEffectiveness::NORMAL_EFFECTIVE
|
||||
end
|
||||
|
||||
def PBTypes.resistant?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
|
||||
return attackType<PBTypeEffectiveness::NORMAL_EFFECTIVE if !targetType1
|
||||
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
|
||||
return e<PBTypeEffectiveness::NORMAL_EFFECTIVE
|
||||
end
|
||||
|
||||
def PBTypes.normalEffective?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
|
||||
return attackType==PBTypeEffectiveness::NORMAL_EFFECTIVE if !targetType1
|
||||
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
|
||||
return e==PBTypeEffectiveness::NORMAL_EFFECTIVE
|
||||
end
|
||||
|
||||
def PBTypes.superEffective?(attackType,targetType1=nil,targetType2=nil,targetType3=nil)
|
||||
return attackType>PBTypeEffectiveness::NORMAL_EFFECTIVE if !targetType1
|
||||
e = PBTypes.getCombinedEffectiveness(attackType,targetType1,targetType2,targetType3)
|
||||
return e>PBTypeEffectiveness::NORMAL_EFFECTIVE
|
||||
end
|
||||
end
|
||||
87
Data/Scripts/010_Data/005_PBNatures.rb
Normal file
87
Data/Scripts/010_Data/005_PBNatures.rb
Normal file
@@ -0,0 +1,87 @@
|
||||
module PBNatures
|
||||
HARDY = 0
|
||||
LONELY = 1
|
||||
BRAVE = 2
|
||||
ADAMANT = 3
|
||||
NAUGHTY = 4
|
||||
BOLD = 5
|
||||
DOCILE = 6
|
||||
RELAXED = 7
|
||||
IMPISH = 8
|
||||
LAX = 9
|
||||
TIMID = 10
|
||||
HASTY = 11
|
||||
SERIOUS = 12
|
||||
JOLLY = 13
|
||||
NAIVE = 14
|
||||
MODEST = 15
|
||||
MILD = 16
|
||||
QUIET = 17
|
||||
BASHFUL = 18
|
||||
RASH = 19
|
||||
CALM = 20
|
||||
GENTLE = 21
|
||||
SASSY = 22
|
||||
CAREFUL = 23
|
||||
QUIRKY = 24
|
||||
|
||||
def self.maxValue; 24; end
|
||||
def self.getCount; 25; end
|
||||
|
||||
def self.getName(id)
|
||||
id = getID(PBNatures,id)
|
||||
names = [
|
||||
_INTL("Hardy"),
|
||||
_INTL("Lonely"),
|
||||
_INTL("Brave"),
|
||||
_INTL("Adamant"),
|
||||
_INTL("Naughty"),
|
||||
_INTL("Bold"),
|
||||
_INTL("Docile"),
|
||||
_INTL("Relaxed"),
|
||||
_INTL("Impish"),
|
||||
_INTL("Lax"),
|
||||
_INTL("Timid"),
|
||||
_INTL("Hasty"),
|
||||
_INTL("Serious"),
|
||||
_INTL("Jolly"),
|
||||
_INTL("Naive"),
|
||||
_INTL("Modest"),
|
||||
_INTL("Mild"),
|
||||
_INTL("Quiet"),
|
||||
_INTL("Bashful"),
|
||||
_INTL("Rash"),
|
||||
_INTL("Calm"),
|
||||
_INTL("Gentle"),
|
||||
_INTL("Sassy"),
|
||||
_INTL("Careful"),
|
||||
_INTL("Quirky")
|
||||
]
|
||||
return names[id]
|
||||
end
|
||||
|
||||
def self.getStatRaised(id)
|
||||
m = (id%25)/5 # 25 here is (number of stats)**2, not PBNatures.getCount
|
||||
return [PBStats::ATTACK,PBStats::DEFENSE,PBStats::SPEED,
|
||||
PBStats::SPATK,PBStats::SPDEF][m]
|
||||
end
|
||||
|
||||
def self.getStatLowered(id)
|
||||
m = id%5 # Don't need to %25 here because 25 is a multiple of 5
|
||||
return [PBStats::ATTACK,PBStats::DEFENSE,PBStats::SPEED,
|
||||
PBStats::SPATK,PBStats::SPDEF][m]
|
||||
end
|
||||
|
||||
def self.getStatChanges(id)
|
||||
id = getID(PBNatures,id)
|
||||
up = PBNatures.getStatRaised(id)
|
||||
dn = PBNatures.getStatLowered(id)
|
||||
ret = []
|
||||
PBStats.eachStat do |s|
|
||||
ret[s] = 100
|
||||
ret[s] += 10 if s==up
|
||||
ret[s] -= 10 if s==dn
|
||||
end
|
||||
return ret
|
||||
end
|
||||
end
|
||||
24
Data/Scripts/010_Data/006_PBGenderRates.rb
Normal file
24
Data/Scripts/010_Data/006_PBGenderRates.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
module PBGenderRates
|
||||
Genderless = 0
|
||||
AlwaysMale = 1
|
||||
FemaleOneEighth = 2
|
||||
Female25Percent = 3
|
||||
Female50Percent = 4
|
||||
Female75Percent = 5
|
||||
FemaleSevenEighths = 6
|
||||
AlwaysFemale = 7
|
||||
|
||||
def self.genderByte(gender)
|
||||
case gender
|
||||
when AlwaysMale; return 0
|
||||
when FemaleOneEighth; return 32
|
||||
when Female25Percent; return 64
|
||||
when Female50Percent; return 128
|
||||
when Female75Percent; return 192
|
||||
when FemaleSevenEighths; return 224
|
||||
when AlwaysFemale; return 254
|
||||
when Genderless; return 255
|
||||
end
|
||||
return 255 # Default value (genderless)
|
||||
end
|
||||
end
|
||||
197
Data/Scripts/010_Data/007_PBExperience.rb
Normal file
197
Data/Scripts/010_Data/007_PBExperience.rb
Normal file
@@ -0,0 +1,197 @@
|
||||
module PBGrowthRates
|
||||
Medium = MediumFast = 0
|
||||
Erratic = 1
|
||||
Fluctuating = 2
|
||||
Parabolic = MediumSlow = 3
|
||||
Fast = 4
|
||||
Slow = 5
|
||||
|
||||
def self.maxValue; return 5; end
|
||||
end
|
||||
|
||||
|
||||
|
||||
module PBExperience
|
||||
@PBExpTable = []
|
||||
@PBExpTable[PBGrowthRates::Medium] = [
|
||||
-1, 0, 8, 27, 64, 125, 216, 343, 512, 729,
|
||||
1000, 1331, 1728, 2197, 2744, 3375, 4096, 4913, 5832, 6859,
|
||||
8000, 9261, 10648, 12167, 13824, 15625, 17576, 19683, 21952, 24389,
|
||||
27000, 29791, 32768, 35937, 39304, 42875, 46656, 50653, 54872, 59319,
|
||||
64000, 68921, 74088, 79507, 85184, 91125, 97336, 103823, 110592, 117649,
|
||||
125000, 132651, 140608, 148877, 157464, 166375, 175616, 185193, 195112, 205379,
|
||||
216000, 226981, 238328, 250047, 262144, 274625, 287496, 300763, 314432, 328509,
|
||||
343000, 357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039,
|
||||
512000, 531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969,
|
||||
729000, 753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299,
|
||||
1000000]
|
||||
@PBExpTable[PBGrowthRates::Erratic] = [
|
||||
-1, 0, 15, 52, 122, 237, 406, 637, 942, 1326,
|
||||
1800, 2369, 3041, 3822, 4719, 5737, 6881, 8155, 9564, 11111,
|
||||
12800, 14632, 16610, 18737, 21012, 23437, 26012, 28737, 31610, 34632,
|
||||
37800, 41111, 44564, 48155, 51881, 55737, 59719, 63822, 68041, 72369,
|
||||
76800, 81326, 85942, 90637, 95406, 100237, 105122, 110052, 115015, 120001,
|
||||
125000, 131324, 137795, 144410, 151165, 158056, 165079, 172229, 179503, 186894,
|
||||
194400, 202013, 209728, 217540, 225443, 233431, 241496, 249633, 257834, 267406,
|
||||
276458, 286328, 296358, 305767, 316074, 326531, 336255, 346965, 357812, 367807,
|
||||
378880, 390077, 400293, 411686, 423190, 433572, 445239, 457001, 467489, 479378,
|
||||
491346, 501878, 513934, 526049, 536557, 548720, 560922, 571333, 583539, 591882,
|
||||
600000]
|
||||
@PBExpTable[PBGrowthRates::Fluctuating] = [
|
||||
-1, 0, 4, 13, 32, 65, 112, 178, 276, 393,
|
||||
540, 745, 967, 1230, 1591, 1957, 2457, 3046, 3732, 4526,
|
||||
5440, 6482, 7666, 9003, 10506, 12187, 14060, 16140, 18439, 20974,
|
||||
23760, 26811, 30146, 33780, 37731, 42017, 46656, 50653, 55969, 60505,
|
||||
66560, 71677, 78533, 84277, 91998, 98415, 107069, 114205, 123863, 131766,
|
||||
142500, 151222, 163105, 172697, 185807, 196322, 210739, 222231, 238036, 250562,
|
||||
267840, 281456, 300293, 315059, 335544, 351520, 373744, 390991, 415050, 433631,
|
||||
459620, 479600, 507617, 529063, 559209, 582187, 614566, 639146, 673863, 700115,
|
||||
737280, 765275, 804997, 834809, 877201, 908905, 954084, 987754, 1035837, 1071552,
|
||||
1122660, 1160499, 1214753, 1254796, 1312322, 1354652, 1415577, 1460276, 1524731, 1571884,
|
||||
1640000]
|
||||
@PBExpTable[PBGrowthRates::Parabolic] = [
|
||||
-1, 0, 9, 57, 96, 135, 179, 236, 314, 419,
|
||||
560, 742, 973, 1261, 1612, 2035, 2535, 3120, 3798, 4575,
|
||||
5460, 6458, 7577, 8825, 10208, 11735, 13411, 15244, 17242, 19411,
|
||||
21760, 24294, 27021, 29949, 33084, 36435, 40007, 43808, 47846, 52127,
|
||||
56660, 61450, 66505, 71833, 77440, 83335, 89523, 96012, 102810, 109923,
|
||||
117360, 125126, 133229, 141677, 150476, 159635, 169159, 179056, 189334, 199999,
|
||||
211060, 222522, 234393, 246681, 259392, 272535, 286115, 300140, 314618, 329555,
|
||||
344960, 360838, 377197, 394045, 411388, 429235, 447591, 466464, 485862, 505791,
|
||||
526260, 547274, 568841, 590969, 613664, 636935, 660787, 685228, 710266, 735907,
|
||||
762160, 789030, 816525, 844653, 873420, 902835, 932903, 963632, 995030, 1027103,
|
||||
1059860]
|
||||
@PBExpTable[PBGrowthRates::Fast] = [
|
||||
-1, 0, 6, 21, 51, 100, 172, 274, 409, 583,
|
||||
800, 1064, 1382, 1757, 2195, 2700, 3276, 3930, 4665, 5487,
|
||||
6400, 7408, 8518, 9733, 11059, 12500, 14060, 15746, 17561, 19511,
|
||||
21600, 23832, 26214, 28749, 31443, 34300, 37324, 40522, 43897, 47455,
|
||||
51200, 55136, 59270, 63605, 68147, 72900, 77868, 83058, 88473, 94119,
|
||||
100000, 106120, 112486, 119101, 125971, 133100, 140492, 148154, 156089, 164303,
|
||||
172800, 181584, 190662, 200037, 209715, 219700, 229996, 240610, 251545, 262807,
|
||||
274400, 286328, 298598, 311213, 324179, 337500, 351180, 365226, 379641, 394431,
|
||||
409600, 425152, 441094, 457429, 474163, 491300, 508844, 526802, 545177, 563975,
|
||||
583200, 602856, 622950, 643485, 664467, 685900, 707788, 730138, 752953, 776239,
|
||||
800000]
|
||||
@PBExpTable[PBGrowthRates::Slow] = [
|
||||
-1, 0, 10, 33, 80, 156, 270, 428, 640, 911,
|
||||
1250, 1663, 2160, 2746, 3430, 4218, 5120, 6141, 7290, 8573,
|
||||
10000, 11576, 13310, 15208, 17280, 19531, 21970, 24603, 27440, 30486,
|
||||
33750, 37238, 40960, 44921, 49130, 53593, 58320, 63316, 68590, 74148,
|
||||
80000, 86151, 92610, 99383, 106480, 113906, 121670, 129778, 138240, 147061,
|
||||
156250, 165813, 175760, 186096, 196830, 207968, 219520, 231491, 243890, 256723,
|
||||
270000, 283726, 297910, 312558, 327680, 343281, 359370, 375953, 393040, 410636,
|
||||
428750, 447388, 466560, 486271, 506530, 527343, 548720, 570666, 593190, 616298,
|
||||
640000, 664301, 689210, 714733, 740880, 767656, 795070, 823128, 851840, 881211,
|
||||
911250, 941963, 973360, 1005446, 1038230, 1071718, 1105920, 1140841, 1176490, 1212873,
|
||||
1250000]
|
||||
|
||||
# Returns the maximum level a Pokémon can attain. If you want to make it vary,
|
||||
# here's where you put your formulae. Note that this is also called by the
|
||||
# Compiler, which happens before anything (e.g. Game Switches/Variables, the
|
||||
# player's data) is loaded, so make sure they exist before using them, and
|
||||
# make this method return the most maximum ever level if they don't.
|
||||
def self.maxLevel
|
||||
return MAXIMUM_LEVEL
|
||||
end
|
||||
|
||||
# Erratic (600000):
|
||||
# For levels 0-50: n**3([100-n]/50)
|
||||
# For levels 51-68: n**3([150-n]/100)
|
||||
# For levels 69-98: n**3(1.274-[1/50][n/3]-p(n mod 3))
|
||||
# where p(x) = array(0.000,0.008,0.014)[x]
|
||||
# For levels 99-100: n**3([160-n]/100)
|
||||
# Fluctuating (1640000):
|
||||
# For levels 0-15 : n**3([24+{(n+1)/3}]/50)
|
||||
# For levels 16-35: n**3([14+n]/50)
|
||||
# For levels 36-100: n**3([32+{n/2}]/50)
|
||||
|
||||
def self.pbGetExpInternal(level,growth)
|
||||
if level <= 100
|
||||
# Refer to experience table for levels 100 and less
|
||||
return @PBExpTable[growth][level]
|
||||
end
|
||||
# Level 101+, use formulae
|
||||
case growth
|
||||
when PBGrowthRates::Medium # 1000000
|
||||
return level ** 3
|
||||
when PBGrowthRates::Erratic # 600000
|
||||
# Different formula that causes 600000 EXP at level 100
|
||||
return ((level ** 4) * 0.6 / 100).floor
|
||||
when PBGrowthRates::Fluctuating # 1640000
|
||||
# Different formula that causes 1640000 EXP at level 100
|
||||
rate = 82
|
||||
if level > 100
|
||||
# Slow rate with increasing level
|
||||
rate -= (level - 100) / 2
|
||||
rate = 40 if rate < 40
|
||||
end
|
||||
return ((level ** 3) * (level * rate / 100) / 50.0).floor
|
||||
when PBGrowthRates::Parabolic # 1059860
|
||||
return ((level ** 3) * 6 / 5) - 15 * (level ** 2) + 100 * level - 140
|
||||
when PBGrowthRates::Fast # 800000
|
||||
return (level ** 3) * 4 / 5
|
||||
when PBGrowthRates::Slow # 1250000
|
||||
return (level ** 3) * 5 / 4
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
# Gets the maximum Exp Points possible for the given growth rate.
|
||||
# growth -- Growth rate.
|
||||
def self.pbGetMaxExperience(growth)
|
||||
if growth<0 || growth>PBGrowthRates.maxValue
|
||||
return ArgumentError.new("The growth rate is invalid.")
|
||||
end
|
||||
return pbGetExpInternal(maxLevel,growth)
|
||||
end
|
||||
|
||||
# Gets the number of Exp Points needed to reach the given
|
||||
# level with the given growth rate.
|
||||
# growth -- Growth rate.
|
||||
def self.pbGetStartExperience(level,growth)
|
||||
if growth<0 || growth>PBGrowthRates.maxValue
|
||||
return ArgumentError.new("The growth rate is invalid.")
|
||||
end
|
||||
if level<0
|
||||
return ArgumentError.new("The level is invalid.")
|
||||
end
|
||||
mLevel = maxLevel
|
||||
level = mLevel if level>mLevel
|
||||
return pbGetExpInternal(level,growth)
|
||||
end
|
||||
|
||||
# Adds experience points ensuring that the new total doesn't
|
||||
# exceed the maximum Exp. Points for the given growth rate.
|
||||
# currexp -- Current Exp Points.
|
||||
# expgain -- Exp. Points to add
|
||||
# growth -- Growth rate.
|
||||
def self.pbAddExperience(currexp,expgain,growth)
|
||||
if growth<0 || growth>PBGrowthRates.maxValue
|
||||
return ArgumentError.new("The growth rate is invalid.")
|
||||
end
|
||||
exp = currexp+expgain
|
||||
maxexp = pbGetExpInternal(maxLevel,growth)
|
||||
exp = maxexp if exp>maxexp
|
||||
return exp
|
||||
end
|
||||
|
||||
# Calculates a level given the number of Exp Points and growth rate.
|
||||
# growth -- Growth rate.
|
||||
def self.pbGetLevelFromExperience(exp,growth)
|
||||
if growth<0 || growth>PBGrowthRates.maxValue
|
||||
return ArgumentError.new("The growth rate is invalid.")
|
||||
end
|
||||
mLevel = maxLevel
|
||||
maxexp = pbGetExpInternal(mLevel,growth)
|
||||
exp = maxexp if exp>maxexp
|
||||
i = 0
|
||||
for j in 0..mLevel
|
||||
currentExp = pbGetExpInternal(i,growth)
|
||||
return i if exp==currentExp
|
||||
return i-1 if exp<currentExp
|
||||
i += 1
|
||||
end
|
||||
return mLevel
|
||||
end
|
||||
end
|
||||
66
Data/Scripts/010_Data/008_PBStats.rb
Normal file
66
Data/Scripts/010_Data/008_PBStats.rb
Normal file
@@ -0,0 +1,66 @@
|
||||
begin
|
||||
module PBStats
|
||||
# NOTE: You can change the order that the compiler expects Pokémon base
|
||||
# stats/EV yields (effort points) to be in, by simply renumbering the
|
||||
# stats here. The "main" stats (i.e. not accuracy/evasion) must still
|
||||
# use up numbers 0 to 5 inclusive, though. It's up to you to write the
|
||||
# base stats/EV yields in pokemon.txt and pokemonforms.txt in the
|
||||
# order expected.
|
||||
HP = 0
|
||||
ATTACK = 1
|
||||
DEFENSE = 2
|
||||
SPEED = 3
|
||||
SPATK = 4
|
||||
SPDEF = 5
|
||||
ACCURACY = 6
|
||||
EVASION = 7
|
||||
|
||||
def self.getName(id)
|
||||
id = getID(PBStats,id)
|
||||
names = []
|
||||
names[HP] = _INTL("HP")
|
||||
names[ATTACK] = _INTL("Attack")
|
||||
names[DEFENSE] = _INTL("Defense")
|
||||
names[SPEED] = _INTL("Speed")
|
||||
names[SPATK] = _INTL("Special Attack")
|
||||
names[SPDEF] = _INTL("Special Defense")
|
||||
names[ACCURACY] = _INTL("accuracy")
|
||||
names[EVASION] = _INTL("evasiveness")
|
||||
return names[id]
|
||||
end
|
||||
def self.getNameBrief(id)
|
||||
id = getID(PBStats,id)
|
||||
names = []
|
||||
names[HP] = _INTL("HP")
|
||||
names[ATTACK] = _INTL("Atk")
|
||||
names[DEFENSE] = _INTL("Def")
|
||||
names[SPEED] = _INTL("Spd")
|
||||
names[SPATK] = _INTL("SpAtk")
|
||||
names[SPDEF] = _INTL("SpDef")
|
||||
names[ACCURACY] = _INTL("acc")
|
||||
names[EVASION] = _INTL("eva")
|
||||
return names[id]
|
||||
end
|
||||
def self.eachStat
|
||||
[HP,ATTACK,DEFENSE,SPATK,SPDEF,SPEED].each { |s| yield s }
|
||||
end
|
||||
|
||||
def self.eachMainBattleStat
|
||||
[ATTACK,DEFENSE,SPATK,SPDEF,SPEED].each { |s| yield s }
|
||||
end
|
||||
|
||||
def self.eachBattleStat
|
||||
[ATTACK,DEFENSE,SPATK,SPDEF,SPEED,ACCURACY,EVASION].each { |s| yield s }
|
||||
end
|
||||
|
||||
def self.validBattleStat?(stat)
|
||||
self.eachBattleStat { |s| return true if s==stat }
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
rescue Exception
|
||||
if $!.is_a?(SystemExit) || "#{$!.class}"=="Reset"
|
||||
raise $!
|
||||
end
|
||||
end
|
||||
259
Data/Scripts/010_Data/009_PBRibbons.rb
Normal file
259
Data/Scripts/010_Data/009_PBRibbons.rb
Normal file
@@ -0,0 +1,259 @@
|
||||
module PBRibbons
|
||||
HOENNCOOL = 1
|
||||
HOENNCOOLSUPER = 2
|
||||
HOENNCOOLHYPER = 3
|
||||
HOENNCOOLMASTER = 4
|
||||
HOENNBEAUTY = 5
|
||||
HOENNBEAUTYSUPER = 6
|
||||
HOENNBEAUTYHYPER = 7
|
||||
HOENNBEAUTYMASTER = 8
|
||||
HOENNCUTE = 9
|
||||
HOENNCUTESUPER = 10
|
||||
HOENNCUTEHYPER = 11
|
||||
HOENNCUTEMASTER = 12
|
||||
HOENNSMART = 13
|
||||
HOENNSMARTSUPER = 14
|
||||
HOENNSMARTHYPER = 15
|
||||
HOENNSMARTMASTER = 16
|
||||
HOENNTOUGH = 17
|
||||
HOENNTOUGHSUPER = 18
|
||||
HOENNTOUGHHYPER = 19
|
||||
HOENNTOUGHMASTER = 20
|
||||
SINNOHCOOL = 21
|
||||
SINNOHCOOLSUPER = 22
|
||||
SINNOHCOOLHYPER = 23
|
||||
SINNOHCOOLMASTER = 24
|
||||
SINNOHBEAUTY = 25
|
||||
SINNOHBEAUTYSUPER = 26
|
||||
SINNOHBEAUTYHYPER = 27
|
||||
SINNOHBEAUTYMASTER = 28
|
||||
SINNOHCUTE = 29
|
||||
SINNOHCUTESUPER = 30
|
||||
SINNOHCUTEHYPER = 31
|
||||
SINNOHCUTEMASTER = 32
|
||||
SINNOHSMART = 33
|
||||
SINNOHSMARTSUPER = 34
|
||||
SINNOHSMARTHYPER = 35
|
||||
SINNOHSMARTMASTER = 36
|
||||
SINNOHTOUGH = 37
|
||||
SINNOHTOUGHSUPER = 38
|
||||
SINNOHTOUGHHYPER = 39
|
||||
SINNOHTOUGHMASTER = 40
|
||||
WINNING = 41
|
||||
VICTORY = 42
|
||||
ABILITY = 43
|
||||
GREATABILITY = 44
|
||||
DOUBLEABILITY = 45
|
||||
MULTIABILITY = 46
|
||||
PAIRABILITY = 47
|
||||
WORLDABILITY = 48
|
||||
CHAMPION = 49
|
||||
SINNOHCHAMP = 50
|
||||
RECORD = 51
|
||||
EVENT = 52
|
||||
LEGEND = 53
|
||||
GORGEOUS = 54
|
||||
ROYAL = 55
|
||||
GORGEOUSROYAL = 56
|
||||
ALERT = 57
|
||||
SHOCK = 58
|
||||
DOWNCAST = 59
|
||||
CARELESS = 60
|
||||
RELAX = 61
|
||||
SNOOZE = 62
|
||||
SMILE = 63
|
||||
FOOTPRINT = 64
|
||||
ARTIST = 65
|
||||
EFFORT = 66
|
||||
BIRTHDAY = 67
|
||||
SPECIAL = 68
|
||||
CLASSIC = 69
|
||||
PREMIER = 70
|
||||
SOUVENIR = 71
|
||||
WISHING = 72
|
||||
NATIONAL = 73
|
||||
COUNTRY = 74
|
||||
BATTLECHAMPION = 75
|
||||
REGIONALCHAMPION = 76
|
||||
EARTH = 77
|
||||
WORLD = 78
|
||||
NATIONALCHAMPION = 79
|
||||
WORLDCHAMPION = 80
|
||||
|
||||
def self.maxValue; 80; end
|
||||
def self.getCount; 80; end
|
||||
|
||||
def self.getName(id)
|
||||
id = getID(PBRibbons,id)
|
||||
names = ["",
|
||||
_INTL("Cool Ribbon"),
|
||||
_INTL("Cool Ribbon Super"),
|
||||
_INTL("Cool Ribbon Hyper"),
|
||||
_INTL("Cool Ribbon Master"),
|
||||
_INTL("Beauty Ribbon"),
|
||||
_INTL("Beauty Ribbon Super"),
|
||||
_INTL("Beauty Ribbon Hyper"),
|
||||
_INTL("Beauty Ribbon Master"),
|
||||
_INTL("Cute Ribbon"),
|
||||
_INTL("Cute Ribbon Super"),
|
||||
_INTL("Cute Ribbon Hyper"),
|
||||
_INTL("Cute Ribbon Master"),
|
||||
_INTL("Smart Ribbon"),
|
||||
_INTL("Smart Ribbon Super"),
|
||||
_INTL("Smart Ribbon Hyper"),
|
||||
_INTL("Smart Ribbon Master"),
|
||||
_INTL("Tough Ribbon"),
|
||||
_INTL("Tough Ribbon Super"),
|
||||
_INTL("Tough Ribbon Hyper"),
|
||||
_INTL("Tough Ribbon Master"),
|
||||
_INTL("Cool Ribbon"),
|
||||
_INTL("Cool Ribbon Great"),
|
||||
_INTL("Cool Ribbon Ultra"),
|
||||
_INTL("Cool Ribbon Master"),
|
||||
_INTL("Beauty Ribbon"),
|
||||
_INTL("Beauty Ribbon Great"),
|
||||
_INTL("Beauty Ribbon Ultra"),
|
||||
_INTL("Beauty Ribbon Master"),
|
||||
_INTL("Cute Ribbon"),
|
||||
_INTL("Cute Ribbon Great"),
|
||||
_INTL("Cute Ribbon Ultra"),
|
||||
_INTL("Cute Ribbon Master"),
|
||||
_INTL("Smart Ribbon"),
|
||||
_INTL("Smart Ribbon Great"),
|
||||
_INTL("Smart Ribbon Ultra"),
|
||||
_INTL("Smart Ribbon Master"),
|
||||
_INTL("Tough Ribbon"),
|
||||
_INTL("Tough Ribbon Great"),
|
||||
_INTL("Tough Ribbon Ultra"),
|
||||
_INTL("Tough Ribbon Master"),
|
||||
_INTL("Winning Ribbon"),
|
||||
_INTL("Victory Ribbon"),
|
||||
_INTL("Ability Ribbon"),
|
||||
_INTL("Great Ability Ribbon"),
|
||||
_INTL("Double Ability Ribbon"),
|
||||
_INTL("Multi Ability Ribbon"),
|
||||
_INTL("Pair Ability Ribbon"),
|
||||
_INTL("World Ability Ribbon"),
|
||||
_INTL("Champion Ribbon"),
|
||||
_INTL("Sinnoh Champ Ribbon"),
|
||||
_INTL("Record Ribbon"),
|
||||
_INTL("Event Ribbon"),
|
||||
_INTL("Legend Ribbon"),
|
||||
_INTL("Gorgeous Ribbon"),
|
||||
_INTL("Royal Ribbon"),
|
||||
_INTL("Gorgeous Royal Ribbon"),
|
||||
_INTL("Alert Ribbon"),
|
||||
_INTL("Shock Ribbon"),
|
||||
_INTL("Downcast Ribbon"),
|
||||
_INTL("Careless Ribbon"),
|
||||
_INTL("Relax Ribbon"),
|
||||
_INTL("Snooze Ribbon"),
|
||||
_INTL("Smile Ribbon"),
|
||||
_INTL("Footprint Ribbon"),
|
||||
_INTL("Artist Ribbon"),
|
||||
_INTL("Effort Ribbon"),
|
||||
_INTL("Birthday Ribbon"),
|
||||
_INTL("Special Ribbon"),
|
||||
_INTL("Classic Ribbon"),
|
||||
_INTL("Premier Ribbon"),
|
||||
_INTL("Souvenir Ribbon"),
|
||||
_INTL("Wishing Ribbon"),
|
||||
_INTL("National Ribbon"),
|
||||
_INTL("Country Ribbon"),
|
||||
_INTL("Battle Champion Ribbon"),
|
||||
_INTL("Regional Champion Ribbon"),
|
||||
_INTL("Earth Ribbon"),
|
||||
_INTL("World Ribbon"),
|
||||
_INTL("National Champion Ribbon"),
|
||||
_INTL("World Champion Ribbon")
|
||||
]
|
||||
return names[id]
|
||||
end
|
||||
|
||||
def self.getDescription(id)
|
||||
id = getID(PBRibbons,id)
|
||||
desc = ["",
|
||||
_INTL("Hoenn Cool Contest Normal Rank winner!"),
|
||||
_INTL("Hoenn Cool Contest Super Rank winner!"),
|
||||
_INTL("Hoenn Cool Contest Hyper Rank winner!"),
|
||||
_INTL("Hoenn Cool Contest Master Rank winner!"),
|
||||
_INTL("Hoenn Beauty Contest Normal Rank winner!"),
|
||||
_INTL("Hoenn Beauty Contest Super Rank winner!"),
|
||||
_INTL("Hoenn Beauty Contest Hyper Rank winner!"),
|
||||
_INTL("Hoenn Beauty Contest Master Rank winner!"),
|
||||
_INTL("Hoenn Cute Contest Normal Rank winner!"),
|
||||
_INTL("Hoenn Cute Contest Super Rank winner!"),
|
||||
_INTL("Hoenn Cute Contest Hyper Rank winner!"),
|
||||
_INTL("Hoenn Cute Contest Master Rank winner!"),
|
||||
_INTL("Hoenn Smart Contest Normal Rank winner!"),
|
||||
_INTL("Hoenn Smart Contest Super Rank winner!"),
|
||||
_INTL("Hoenn Smart Contest Hyper Rank winner!"),
|
||||
_INTL("Hoenn Smart Contest Master Rank winner!"),
|
||||
_INTL("Hoenn Tough Contest Normal Rank winner!"),
|
||||
_INTL("Hoenn Tough Contest Super Rank winner!"),
|
||||
_INTL("Hoenn Tough Contest Hyper Rank winner!"),
|
||||
_INTL("Hoenn Tough Contest Master Rank winner!"),
|
||||
_INTL("Super Contest Cool Category Normal Rank winner!"),
|
||||
_INTL("Super Contest Cool Category Great Rank winner!"),
|
||||
_INTL("Super Contest Cool Category Ultra Rank winner!"),
|
||||
_INTL("Super Contest Cool Category Master Rank winner!"),
|
||||
_INTL("Super Contest Beauty Category Normal Rank winner!"),
|
||||
_INTL("Super Contest Beauty Category Great Rank winner!"),
|
||||
_INTL("Super Contest Beauty Category Ultra Rank winner!"),
|
||||
_INTL("Super Contest Beauty Category Master Rank winner!"),
|
||||
_INTL("Super Contest Cute Category Normal Rank winner!"),
|
||||
_INTL("Super Contest Cute Category Great Rank winner!"),
|
||||
_INTL("Super Contest Cute Category Ultra Rank winner!"),
|
||||
_INTL("Super Contest Cute Category Master Rank winner!"),
|
||||
_INTL("Super Contest Smart Category Normal Rank winner!"),
|
||||
_INTL("Super Contest Smart Category Great Rank winner!"),
|
||||
_INTL("Super Contest Smart Category Ultra Rank winner!"),
|
||||
_INTL("Super Contest Smart Category Master Rank winner!"),
|
||||
_INTL("Super Contest Tough Category Normal Rank winner!"),
|
||||
_INTL("Super Contest Tough Category Great Rank winner!"),
|
||||
_INTL("Super Contest Tough Category Ultra Rank winner!"),
|
||||
_INTL("Super Contest Tough Category Master Rank winner!"),
|
||||
_INTL("Ribbon awarded for clearing Hoenn's Battle Tower's Lv. 50 challenge."),
|
||||
_INTL("Ribbon awarded for clearing Hoenn's Battle Tower's Lv. 100 challenge."),
|
||||
_INTL("A Ribbon awarded for defeating the Tower Tycoon at the Battle Tower."),
|
||||
_INTL("A Ribbon awarded for defeating the Tower Tycoon at the Battle Tower."),
|
||||
_INTL("A Ribbon awarded for completing the Battle Tower Double challenge."),
|
||||
_INTL("A Ribbon awarded for completing the Battle Tower Multi challenge."),
|
||||
_INTL("A Ribbon awarded for completing the Battle Tower Link Multi challenge."),
|
||||
_INTL("A Ribbon awarded for completing the Wi-Fi Battle Tower challenge."),
|
||||
_INTL("Ribbon for clearing the Pokémon League and entering the Hall of Fame in another region. "),
|
||||
_INTL("Ribbon awarded for beating the Sinnoh Champion and entering the Hall of Fame."),
|
||||
_INTL("A Ribbon awarded for setting an incredible record."),
|
||||
_INTL("Pokémon Event Participation Ribbon."),
|
||||
_INTL("A Ribbon awarded for setting a legendary record."),
|
||||
_INTL("An extraordinarily gorgeous and extravagant Ribbon."),
|
||||
_INTL("An incredibly regal Ribbon with an air of nobility."),
|
||||
_INTL("A gorgeous and regal Ribbon that is the peak of fabulous."),
|
||||
_INTL("A Ribbon for recalling an invigorating event that created life energy."),
|
||||
_INTL("A Ribbon for recalling a thrilling event that made life more exciting."),
|
||||
_INTL("A Ribbon for recalling feelings of sadness that added spice to life."),
|
||||
_INTL("A Ribbon for recalling a careless error that helped steer life decisions."),
|
||||
_INTL("A Ribbon for recalling a refreshing event that added sparkle to life."),
|
||||
_INTL("A Ribbon for recalling a deep slumber that made life soothing."),
|
||||
_INTL("A Ribbon for recalling that smiles enrich the quality of life."),
|
||||
_INTL("A Ribbon awarded to a Pokémon deemed to have a top-quality footprint."),
|
||||
_INTL("Ribbon awarded for being chosen as a super sketch model in Hoenn."),
|
||||
_INTL("Ribbon awarded for being an exceptionally hard worker."),
|
||||
_INTL("A Ribbon to celebrate a birthday."),
|
||||
_INTL("A special Ribbon for a special day."),
|
||||
_INTL("A Ribbon that proclaims love for Pokémon."),
|
||||
_INTL("Special Holiday Ribbon."),
|
||||
_INTL("A Ribbon to cherish a special memory."),
|
||||
_INTL("A Ribbon said to make your wish come true."),
|
||||
_INTL("A Ribbon awarded for overcoming all difficult challenges."),
|
||||
_INTL("Pokémon League Champion Ribbon."),
|
||||
_INTL("Battle Competition Champion Ribbon."),
|
||||
_INTL("Pokémon World Championships Regional Champion Ribbon."),
|
||||
_INTL("A Ribbon awarded for winning 100 matches in a row."),
|
||||
_INTL("Pokémon League Champion Ribbon."),
|
||||
_INTL("Pokémon World Championships National Champion Ribbon."),
|
||||
_INTL("Pokémon World Championships World Champion Ribbon.")
|
||||
]
|
||||
return desc[id]
|
||||
end
|
||||
end
|
||||
42
Data/Scripts/010_Data/010_PBEggGroups.rb
Normal file
42
Data/Scripts/010_Data/010_PBEggGroups.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
module PBEggGroups
|
||||
Undiscovered = 0 # NoEggs, None, NA
|
||||
Monster = 1
|
||||
Water1 = 2
|
||||
Bug = 3
|
||||
Flying = 4
|
||||
Field = 5 # Ground
|
||||
Fairy = 6
|
||||
Grass = 7 # Plant
|
||||
Humanlike = 8 # Humanoid, Humanshape, Human
|
||||
Water3 = 9
|
||||
Mineral = 10
|
||||
Amorphous = 11 # Indeterminate
|
||||
Water2 = 12
|
||||
Ditto = 13
|
||||
Dragon = 14
|
||||
|
||||
def self.maxValue; 14; end
|
||||
def self.getCount; 15; end
|
||||
|
||||
def self.getName(id)
|
||||
id = getID(PBEggGroups,id)
|
||||
names = [
|
||||
_INTL("Undiscovered"),
|
||||
_INTL("Monster"),
|
||||
_INTL("Water 1"),
|
||||
_INTL("Bug"),
|
||||
_INTL("Flying"),
|
||||
_INTL("Field"),
|
||||
_INTL("Fairy"),
|
||||
_INTL("Grass"),
|
||||
_INTL("Human-like"),
|
||||
_INTL("Water 3"),
|
||||
_INTL("Mineral"),
|
||||
_INTL("Amorphous"),
|
||||
_INTL("Water 2"),
|
||||
_INTL("Ditto"),
|
||||
_INTL("Dragon")
|
||||
]
|
||||
return names[id]
|
||||
end
|
||||
end
|
||||
33
Data/Scripts/010_Data/011_PBColors.rb
Normal file
33
Data/Scripts/010_Data/011_PBColors.rb
Normal file
@@ -0,0 +1,33 @@
|
||||
# Colors must begin at 0 and have no missing numbers
|
||||
module PBColors
|
||||
Red = 0
|
||||
Blue = 1
|
||||
Yellow = 2
|
||||
Green = 3
|
||||
Black = 4
|
||||
Brown = 5
|
||||
Purple = 6
|
||||
Gray = 7
|
||||
White = 8
|
||||
Pink = 9
|
||||
|
||||
def self.maxValue; 9; end
|
||||
def self.getCount; 10; end
|
||||
|
||||
def self.getName(id)
|
||||
id = getID(PBColors,id)
|
||||
names = [
|
||||
_INTL("Red"),
|
||||
_INTL("Blue"),
|
||||
_INTL("Yellow"),
|
||||
_INTL("Green"),
|
||||
_INTL("Black"),
|
||||
_INTL("Brown"),
|
||||
_INTL("Purple"),
|
||||
_INTL("Gray"),
|
||||
_INTL("White"),
|
||||
_INTL("Pink")
|
||||
]
|
||||
return names[id]
|
||||
end
|
||||
end
|
||||
32
Data/Scripts/010_Data/012_PBHabitats.rb
Normal file
32
Data/Scripts/010_Data/012_PBHabitats.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
module PBHabitats
|
||||
None = 0
|
||||
Grassland = 1
|
||||
Forest = 2
|
||||
WatersEdge = 3
|
||||
Sea = 4
|
||||
Cave = 5
|
||||
Mountain = 6
|
||||
RoughTerrain = 7
|
||||
Urban = 8
|
||||
Rare = 9
|
||||
|
||||
def self.maxValue; 9; end
|
||||
def self.getCount; 10; end
|
||||
|
||||
def self.getName(id)
|
||||
id = getID(PBHabitats,id)
|
||||
names = [
|
||||
_INTL("None"),
|
||||
_INTL("Grassland"),
|
||||
_INTL("Forest"),
|
||||
_INTL("Water's Edge"),
|
||||
_INTL("Sea"),
|
||||
_INTL("Cave"),
|
||||
_INTL("Mountain"),
|
||||
_INTL("Rough Terrain"),
|
||||
_INTL("Urban"),
|
||||
_INTL("Rare")
|
||||
]
|
||||
return names[id]
|
||||
end
|
||||
end
|
||||
665
Data/Scripts/011_Battle/001_Battler/001_PokeBattle_Battler.rb
Normal file
665
Data/Scripts/011_Battle/001_Battler/001_PokeBattle_Battler.rb
Normal file
@@ -0,0 +1,665 @@
|
||||
class PokeBattle_Battler
|
||||
# Fundamental to this object
|
||||
attr_reader :battle
|
||||
attr_accessor :index
|
||||
# The Pokémon and its properties
|
||||
attr_reader :pokemon
|
||||
attr_accessor :pokemonIndex
|
||||
attr_accessor :species
|
||||
attr_accessor :type1
|
||||
attr_accessor :type2
|
||||
attr_accessor :ability
|
||||
attr_accessor :moves
|
||||
attr_accessor :gender
|
||||
attr_accessor :iv
|
||||
attr_accessor :attack
|
||||
attr_accessor :spatk
|
||||
attr_accessor :speed
|
||||
attr_accessor :stages
|
||||
attr_reader :totalhp
|
||||
attr_reader :fainted # Boolean to mark whether self has fainted properly
|
||||
attr_accessor :captured # Boolean to mark whether self was captured
|
||||
attr_reader :dummy
|
||||
attr_accessor :effects
|
||||
# Things the battler has done in battle
|
||||
attr_accessor :turnCount
|
||||
attr_accessor :participants
|
||||
attr_accessor :lastAttacker
|
||||
attr_accessor :lastFoeAttacker
|
||||
attr_accessor :lastHPLost
|
||||
attr_accessor :lastHPLostFromFoe
|
||||
attr_accessor :lastMoveUsed
|
||||
attr_accessor :lastMoveUsedType
|
||||
attr_accessor :lastRegularMoveUsed
|
||||
attr_accessor :lastRegularMoveTarget # For Instruct
|
||||
attr_accessor :lastRoundMoved
|
||||
attr_accessor :lastMoveFailed # For Stomping Tantrum
|
||||
attr_accessor :lastRoundMoveFailed # For Stomping Tantrum
|
||||
attr_accessor :movesUsed
|
||||
attr_accessor :currentMove # ID of multi-turn move currently being used
|
||||
attr_accessor :tookDamage # Boolean for whether self took damage this round
|
||||
attr_accessor :tookPhysicalHit
|
||||
attr_accessor :damageState
|
||||
attr_accessor :initialHP # Set at the start of each move's usage
|
||||
|
||||
#=============================================================================
|
||||
# Complex accessors
|
||||
#=============================================================================
|
||||
attr_reader :level
|
||||
|
||||
def level=(value)
|
||||
@level = value
|
||||
@pokemon.level = value if @pokemon
|
||||
end
|
||||
|
||||
attr_reader :form
|
||||
|
||||
def form=(value)
|
||||
@form = value
|
||||
@pokemon.form = value if @pokemon
|
||||
end
|
||||
|
||||
attr_reader :item
|
||||
|
||||
def item=(value)
|
||||
@item = value
|
||||
@pokemon.setItem(value) if @pokemon
|
||||
end
|
||||
|
||||
def defense
|
||||
return @spdef if @battle.field.effects[PBEffects::WonderRoom]>0
|
||||
return @defense
|
||||
end
|
||||
|
||||
attr_writer :defense
|
||||
|
||||
def spdef
|
||||
return @defense if @battle.field.effects[PBEffects::WonderRoom]>0
|
||||
return @spdef
|
||||
end
|
||||
|
||||
attr_writer :spdef
|
||||
|
||||
attr_reader :hp
|
||||
|
||||
def hp=(value)
|
||||
@hp = value.to_i
|
||||
@pokemon.hp = value.to_i if @pokemon
|
||||
end
|
||||
|
||||
def fainted?; return @hp<=0; end
|
||||
alias isFainted? fainted?
|
||||
|
||||
attr_reader :status
|
||||
|
||||
def status=(value)
|
||||
@effects[PBEffects::Truant] = false if @status==PBStatuses::SLEEP && value!=PBStatuses::SLEEP
|
||||
@effects[PBEffects::Toxic] = 0 if value!=PBStatuses::POISON
|
||||
@status = value
|
||||
@pokemon.status = value if @pokemon
|
||||
self.statusCount = 0 if value!=PBStatuses::POISON && value!=PBStatuses::SLEEP
|
||||
@battle.scene.pbRefreshOne(@index)
|
||||
end
|
||||
|
||||
attr_reader :statusCount
|
||||
|
||||
def statusCount=(value)
|
||||
@statusCount = value
|
||||
@pokemon.statusCount = value if @pokemon
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Properties from Pokémon
|
||||
#=============================================================================
|
||||
def happiness; return @pokemon ? @pokemon.happiness : 0; end
|
||||
def nature; return @pokemon ? @pokemon.nature : 0; end
|
||||
def pokerusStage; return @pokemon ? @pokemon.pokerusStage : 0; end
|
||||
|
||||
#=============================================================================
|
||||
# Mega Evolution, Primal Reversion, Shadow Pokémon
|
||||
#=============================================================================
|
||||
def hasMega?
|
||||
return false if @effects[PBEffects::Transform]
|
||||
return @pokemon && @pokemon.hasMegaForm?
|
||||
end
|
||||
|
||||
def mega?; return @pokemon && @pokemon.mega?; end
|
||||
alias isMega? mega?
|
||||
|
||||
def hasPrimal?
|
||||
return false if @effects[PBEffects::Transform]
|
||||
return @pokemon && @pokemon.hasPrimalForm?
|
||||
end
|
||||
|
||||
def primal?; return @pokemon && @pokemon.primal?; end
|
||||
alias isPrimal? primal?
|
||||
|
||||
def shadowPokemon?; return false; end
|
||||
alias isShadow? shadowPokemon?
|
||||
|
||||
def inHyperMode?; return false; end
|
||||
|
||||
#=============================================================================
|
||||
# Display-only properties
|
||||
#=============================================================================
|
||||
def name
|
||||
return @effects[PBEffects::Illusion].name if @effects[PBEffects::Illusion]
|
||||
return @name
|
||||
end
|
||||
|
||||
attr_writer :name
|
||||
|
||||
def displayPokemon
|
||||
return @effects[PBEffects::Illusion] if @effects[PBEffects::Illusion]
|
||||
return self.pokemon
|
||||
end
|
||||
|
||||
def displaySpecies
|
||||
return @effects[PBEffects::Illusion].species if @effects[PBEffects::Illusion]
|
||||
return self.species
|
||||
end
|
||||
|
||||
def displayGender
|
||||
return @effects[PBEffects::Illusion].gender if @effects[PBEffects::Illusion]
|
||||
return self.gender
|
||||
end
|
||||
|
||||
def displayForm
|
||||
return @effects[PBEffects::Illusion].form if @effects[PBEffects::Illusion]
|
||||
return self.form
|
||||
end
|
||||
|
||||
def shiny?
|
||||
return @effects[PBEffects::Illusion].shiny? if @effects[PBEffects::Illusion]
|
||||
return @pokemon && @pokemon.shiny?
|
||||
end
|
||||
alias isShiny? shiny?
|
||||
|
||||
def owned?
|
||||
return false if !@battle.wildBattle?
|
||||
return $Trainer.owned[displaySpecies]
|
||||
end
|
||||
alias owned owned?
|
||||
|
||||
def abilityName; return PBAbilities.getName(@ability); end
|
||||
def itemName; return PBItems.getName(@item); end
|
||||
|
||||
def pbThis(lowerCase=false)
|
||||
if opposes?
|
||||
if @battle.trainerBattle?
|
||||
return lowerCase ? _INTL("the opposing {1}",name) : _INTL("The opposing {1}",name)
|
||||
else
|
||||
return lowerCase ? _INTL("the wild {1}",name) : _INTL("The wild {1}",name)
|
||||
end
|
||||
elsif !pbOwnedByPlayer?
|
||||
return lowerCase ? _INTL("the ally {1}",name) : _INTL("The ally {1}",name)
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
def pbTeam(lowerCase=false)
|
||||
if opposes?
|
||||
return lowerCase ? _INTL("the opposing team") : _INTL("The opposing team")
|
||||
end
|
||||
return lowerCase ? _INTL("your team") : _INTL("Your team")
|
||||
end
|
||||
|
||||
def pbOpposingTeam(lowerCase=false)
|
||||
if opposes?
|
||||
return lowerCase ? _INTL("your team") : _INTL("Your team")
|
||||
end
|
||||
return lowerCase ? _INTL("the opposing team") : _INTL("The opposing team")
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Calculated properties
|
||||
#=============================================================================
|
||||
def pbSpeed
|
||||
return 1 if fainted?
|
||||
stageMul = [2,2,2,2,2,2, 2, 3,4,5,6,7,8]
|
||||
stageDiv = [8,7,6,5,4,3, 2, 2,2,2,2,2,2]
|
||||
stage = @stages[PBStats::SPEED] + 6
|
||||
speed = @speed*stageMul[stage]/stageDiv[stage]
|
||||
speedMult = 0x1000
|
||||
# Ability effects that alter calculated Speed
|
||||
if abilityActive?
|
||||
speedMult = BattleHandlers.triggerSpeedCalcAbility(@ability,self,speedMult)
|
||||
end
|
||||
# Item effects that alter calculated Speed
|
||||
if itemActive?
|
||||
speedMult = BattleHandlers.triggerSpeedCalcItem(@item,self,speedMult)
|
||||
end
|
||||
# Other effects
|
||||
speedMult *= 2 if pbOwnSide.effects[PBEffects::Tailwind]>0
|
||||
speedMult /= 2 if pbOwnSide.effects[PBEffects::Swamp]>0
|
||||
# Paralysis
|
||||
if status==PBStatuses::PARALYSIS && !hasActiveAbility?(:QUICKFEET)
|
||||
speedMult /= (NEWEST_BATTLE_MECHANICS) ? 2 : 4
|
||||
end
|
||||
# Badge multiplier
|
||||
if @battle.internalBattle && pbOwnedByPlayer? &&
|
||||
@battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_SPEED
|
||||
speedMult *= 1.1
|
||||
end
|
||||
# Calculation
|
||||
return [(speed.to_f*speedMult/0x1000).round,1].max
|
||||
end
|
||||
|
||||
def pbWeight
|
||||
ret = (@pokemon) ? @pokemon.weight : 500
|
||||
ret += @effects[PBEffects::WeightChange]
|
||||
ret = 1 if ret<1
|
||||
if abilityActive? && !@battle.moldBreaker
|
||||
ret = BattleHandlers.triggerWeightCalcAbility(@ability,self,ret)
|
||||
end
|
||||
if itemActive?
|
||||
ret = BattleHandlers.triggerWeightCalcItem(@item,self,ret)
|
||||
end
|
||||
return [ret,1].max
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Queries about what the battler has
|
||||
#=============================================================================
|
||||
def plainStats
|
||||
ret = []
|
||||
ret[PBStats::ATTACK] = self.attack
|
||||
ret[PBStats::DEFENSE] = self.defense
|
||||
ret[PBStats::SPATK] = self.spatk
|
||||
ret[PBStats::SPDEF] = self.spdef
|
||||
ret[PBStats::SPEED] = self.speed
|
||||
return ret
|
||||
end
|
||||
|
||||
# Returns the active types of this Pokémon. The array should not include the
|
||||
# same type more than once, and should not include any invalid type numbers
|
||||
# (e.g. -1).
|
||||
def pbTypes(withType3=false)
|
||||
ret = [@type1]
|
||||
ret.push(@type2) if @type2!=@type1
|
||||
# Burn Up erases the Fire-type.
|
||||
if @effects[PBEffects::BurnUp]
|
||||
ret.reject! { |type| isConst?(type,PBTypes,:FIRE) }
|
||||
end
|
||||
# Roost erases the Flying-type. If there are no types left, adds the Normal-
|
||||
# type.
|
||||
if @effects[PBEffects::Roost]
|
||||
ret.reject! { |type| isConst?(type,PBTypes,:FLYING) }
|
||||
ret.push(getConst(PBTypes,:NORMAL) || 0) if ret.length==0
|
||||
end
|
||||
# Add the third type specially.
|
||||
if withType3 && @effects[PBEffects::Type3]>=0
|
||||
ret.push(@effects[PBEffects::Type3]) if !ret.include?(@effects[PBEffects::Type3])
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbHasType?(type)
|
||||
type = getConst(PBTypes,type) if type.is_a?(Symbol) || type.is_a?(String)
|
||||
return false if !type || type<0
|
||||
activeTypes = pbTypes(true)
|
||||
return activeTypes.include?(type)
|
||||
end
|
||||
|
||||
def pbHasOtherType?(type)
|
||||
type = getConst(PBTypes,type) if type.is_a?(Symbol) || type.is_a?(String)
|
||||
return false if !type || type<0
|
||||
activeTypes = pbTypes(true)
|
||||
activeTypes.reject! { |t| t==type }
|
||||
return activeTypes.length>0
|
||||
end
|
||||
|
||||
# NOTE: Do not create any held item which affects whether a Pokémon's ability
|
||||
# is active. The ability Klutz affects whether a Pokémon's item is
|
||||
# active, and the code for the two combined would cause an infinite loop
|
||||
# (regardless of whether any Pokémon actualy has either the ability or
|
||||
# the item - the code existing is enough to cause the loop).
|
||||
def abilityActive?(ignoreFainted=false)
|
||||
return false if fainted? && !ignoreFainted
|
||||
return false if @effects[PBEffects::GastroAcid]
|
||||
return true
|
||||
end
|
||||
|
||||
def hasActiveAbility?(ability,ignoreFainted=false)
|
||||
return false if !abilityActive?(ignoreFainted)
|
||||
if ability.is_a?(Array)
|
||||
ability.each do |a|
|
||||
a = getID(PBAbilities,a)
|
||||
return true if a!=0 && a==@ability
|
||||
end
|
||||
return false
|
||||
end
|
||||
ability = getID(PBAbilities,ability)
|
||||
return ability!=0 && ability==@ability
|
||||
end
|
||||
alias hasWorkingAbility hasActiveAbility?
|
||||
|
||||
def nonNegatableAbility?
|
||||
abilityBlacklist = [
|
||||
# Form-changing abilities
|
||||
:BATTLEBOND,
|
||||
:DISGUISE,
|
||||
# :FLOWERGIFT, # This can be negated
|
||||
# :FORECAST, # This can be negated
|
||||
:MULTITYPE,
|
||||
:POWERCONSTRUCT,
|
||||
:SCHOOLING,
|
||||
:SHIELDSDOWN,
|
||||
:STANCECHANGE,
|
||||
:ZENMODE,
|
||||
# Abilities intended to be inherent properties of a certain species
|
||||
:COMATOSE,
|
||||
:RKSSYSTEM
|
||||
]
|
||||
failed = false
|
||||
abilityBlacklist.each do |abil|
|
||||
return true if isConst?(@ability,PBAbilities,abil)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def itemActive?(ignoreFainted=false)
|
||||
return false if fainted? && !ignoreFainted
|
||||
return false if @effects[PBEffects::Embargo]>0
|
||||
return false if @battle.field.effects[PBEffects::MagicRoom]>0
|
||||
return false if hasActiveAbility?(:KLUTZ,ignoreFainted)
|
||||
return true
|
||||
end
|
||||
|
||||
def hasActiveItem?(item,ignoreFainted=false)
|
||||
return false if !itemActive?(ignoreFainted)
|
||||
if item.is_a?(Array)
|
||||
item.each do |i|
|
||||
i = getID(PBItems,i)
|
||||
return true if i!=0 && i==@item
|
||||
end
|
||||
return false
|
||||
end
|
||||
item = getID(PBItems,item)
|
||||
return item!=0 && item==@item
|
||||
end
|
||||
alias hasWorkingItem hasActiveItem?
|
||||
|
||||
# Returns whether the specified item will be unlosable for this Pokémon.
|
||||
def unlosableItem?(item)
|
||||
return false if item<=0
|
||||
return true if pbIsMail?(item)
|
||||
return false if @effects[PBEffects::Transform]
|
||||
# Items that change a Pokémon's form
|
||||
return true if @pokemon && @pokemon.getMegaForm(true)>0 # Mega Stone
|
||||
return pbIsUnlosableItem?(item,@species,@ability)
|
||||
end
|
||||
|
||||
def eachMove
|
||||
@moves.each { |m| yield m if m && m.id!=0 }
|
||||
end
|
||||
|
||||
def eachMoveWithIndex
|
||||
@moves.each_with_index { |m,i| yield m,i if m && m.id!=0 }
|
||||
end
|
||||
|
||||
def pbHasMove?(id)
|
||||
id = getID(PBMoves,id)
|
||||
return false if !id || id<=0
|
||||
eachMove { |m| return true if m.id==id }
|
||||
return false
|
||||
end
|
||||
|
||||
def pbHasMoveType?(type)
|
||||
type = getConst(PBTypes,type)
|
||||
return false if !type || type<0
|
||||
eachMove { |m| return true if m.type==type }
|
||||
return false
|
||||
end
|
||||
|
||||
def pbHasMoveFunction?(*arg)
|
||||
return false if !code
|
||||
eachMove do |m|
|
||||
arg.each { |code| return true if m.function==code }
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def hasMoldBreaker?
|
||||
return hasActiveAbility?([:MOLDBREAKER,:TERAVOLT,:TURBOBLAZE])
|
||||
end
|
||||
|
||||
def canChangeType?
|
||||
return false if isConst?(@ability,PBAbilities,:MULTITYPE) ||
|
||||
isConst?(@ability,PBAbilities,:RKSSYSTEM)
|
||||
return true
|
||||
end
|
||||
|
||||
def airborne?
|
||||
return false if hasActiveItem?(:IRONBALL)
|
||||
return false if @effects[PBEffects::Ingrain]
|
||||
return false if @effects[PBEffects::SmackDown]
|
||||
return false if @battle.field.effects[PBEffects::Gravity]>0
|
||||
return true if pbHasType?(:FLYING)
|
||||
return true if hasActiveAbility?(:LEVITATE) && !@battle.moldBreaker
|
||||
return true if hasActiveItem?(:AIRBALLOON)
|
||||
return true if @effects[PBEffects::MagnetRise]>0
|
||||
return true if @effects[PBEffects::Telekinesis]>0
|
||||
return false
|
||||
end
|
||||
|
||||
def affectedByTerrain?
|
||||
return false if airborne?
|
||||
return false if semiInvulnerable?
|
||||
return true
|
||||
end
|
||||
|
||||
def takesIndirectDamage?(showMsg=false)
|
||||
return false if fainted?
|
||||
if hasActiveAbility?(:MAGICGUARD)
|
||||
if showMsg
|
||||
@battle.pbShowAbilitySplash(self)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,abilityName))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
end
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def takesSandstormDamage?
|
||||
return false if !takesIndirectDamage?
|
||||
return false if pbHasType?(:GROUND) || pbHasType?(:ROCK) || pbHasType?(:STEEL)
|
||||
return false if inTwoTurnAttack?("0CA","0CB") # Dig, Dive
|
||||
return false if hasActiveAbility?([:OVERCOAT,:SANDFORCE,:SANDRUSH,:SANDVEIL])
|
||||
return false if hasActiveItem?(:SAFETYGOGGLES)
|
||||
return true
|
||||
end
|
||||
|
||||
def takesHailDamage?
|
||||
return false if !takesIndirectDamage?
|
||||
return false if pbHasType?(:ICE)
|
||||
return false if inTwoTurnAttack?("0CA","0CB") # Dig, Dive
|
||||
return false if hasActiveAbility?([:OVERCOAT,:ICEBODY,:SNOWCLOAK])
|
||||
return false if hasActiveItem?(:SAFETYGOGGLES)
|
||||
return true
|
||||
end
|
||||
|
||||
def takesShadowSkyDamage?
|
||||
return false if fainted?
|
||||
return false if shadowPokemon?
|
||||
return true
|
||||
end
|
||||
|
||||
def affectedByPowder?(showMsg=false)
|
||||
return false if fainted?
|
||||
return true if !NEWEST_BATTLE_MECHANICS
|
||||
if pbHasType?(:GRASS)
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMsg
|
||||
return false
|
||||
end
|
||||
if hasActiveAbility?(:OVERCOAT) && !@battle.moldBreaker
|
||||
if showMsg
|
||||
@battle.pbShowAbilitySplash(self)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,abilityName))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
end
|
||||
return false
|
||||
end
|
||||
if hasActiveItem?(:SAFETYGOGGLES)
|
||||
if showMsg
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",pbThis,itemName))
|
||||
end
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def canHeal?
|
||||
return false if fainted? || @hp>=@totalhp
|
||||
return false if @effects[PBEffects::HealBlock]>0
|
||||
return true
|
||||
end
|
||||
|
||||
def affectedByContactEffect?(showMsg=false)
|
||||
return false if fainted?
|
||||
if hasActiveItem?(:PROTECTIVEPADS)
|
||||
@battle.pbDisplay(_INTL("{1} protected itself with the {2}!",pbThis,itemName)) if showMsg
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def movedThisRound?
|
||||
return @lastRoundMoved && @lastRoundMoved==@battle.turnCount
|
||||
end
|
||||
|
||||
def usingMultiTurnAttack?
|
||||
return true if @effects[PBEffects::TwoTurnAttack]>0
|
||||
return true if @effects[PBEffects::HyperBeam]>0
|
||||
return true if @effects[PBEffects::Rollout]>0
|
||||
return true if @effects[PBEffects::Outrage]>0
|
||||
return true if @effects[PBEffects::Uproar]>0
|
||||
return true if @effects[PBEffects::Bide]>0
|
||||
return false
|
||||
end
|
||||
|
||||
def inTwoTurnAttack?(*arg)
|
||||
return false if @effects[PBEffects::TwoTurnAttack]==0
|
||||
ttaFunction = pbGetMoveData(@effects[PBEffects::TwoTurnAttack],MOVE_FUNCTION_CODE)
|
||||
arg.each { |a| return true if a==ttaFunction }
|
||||
return false
|
||||
end
|
||||
|
||||
def semiInvulnerable?
|
||||
return inTwoTurnAttack?("0C9","0CA","0CB","0CC","0CD","0CE","14D")
|
||||
end
|
||||
|
||||
def pbEncoredMoveIndex
|
||||
return -1 if @effects[PBEffects::Encore]==0 || @effects[PBEffects::EncoreMove]==0
|
||||
ret = -1
|
||||
eachMoveWithIndex do |m,i|
|
||||
next if m.id!=@effects[PBEffects::EncoreMove]
|
||||
ret = i
|
||||
break
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def initialItem
|
||||
return @battle.initialItems[@index&1][@pokemonIndex]
|
||||
end
|
||||
|
||||
def setInitialItem(newItem)
|
||||
@battle.initialItems[@index&1][@pokemonIndex] = newItem
|
||||
end
|
||||
|
||||
def recycleItem
|
||||
return @battle.recycleItems[@index&1][@pokemonIndex]
|
||||
end
|
||||
|
||||
def setRecycleItem(newItem)
|
||||
@battle.recycleItems[@index&1][@pokemonIndex] = newItem
|
||||
end
|
||||
|
||||
def belched?
|
||||
return @battle.belch[@index&1][@pokemonIndex]
|
||||
end
|
||||
|
||||
def setBelched
|
||||
@battle.belch[@index&1][@pokemonIndex] = true
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Methods relating to this battler's position on the battlefield
|
||||
#=============================================================================
|
||||
# Returns whether the given position belongs to the opposing Pokémon's side.
|
||||
def opposes?(i=0)
|
||||
i = i.index if i.respond_to?("index")
|
||||
return (@index&1)!=(i&1)
|
||||
end
|
||||
|
||||
# Returns whether the given position/battler is near to self.
|
||||
def near?(i)
|
||||
i = i.index if i.respond_to?("index")
|
||||
return @battle.nearBattlers?(@index,i)
|
||||
end
|
||||
|
||||
# Returns whether self is owned by the player.
|
||||
def pbOwnedByPlayer?
|
||||
return @battle.pbOwnedByPlayer?(@index)
|
||||
end
|
||||
|
||||
# Returns 0 if self is on the player's side, or 1 if self is on the opposing
|
||||
# side.
|
||||
def idxOwnSide
|
||||
return @index&1
|
||||
end
|
||||
|
||||
# Returns 1 if self is on the player's side, or 0 if self is on the opposing
|
||||
# side.
|
||||
def idxOpposingSide
|
||||
return (@index&1)^1
|
||||
end
|
||||
|
||||
# Returns the data structure for this battler's side.
|
||||
def pbOwnSide
|
||||
return @battle.sides[idxOwnSide]
|
||||
end
|
||||
|
||||
# Returns the data structure for the opposing Pokémon's side.
|
||||
def pbOpposingSide
|
||||
return @battle.sides[idxOpposingSide]
|
||||
end
|
||||
|
||||
# Yields each unfainted ally Pokémon.
|
||||
def eachAlly
|
||||
@battle.battlers.each do |b|
|
||||
yield b if b && !b.fainted? && !b.opposes?(@index) && b.index!=@index
|
||||
end
|
||||
end
|
||||
|
||||
# Yields each unfainted opposing Pokémon.
|
||||
def eachOpposing
|
||||
@battle.battlers.each { |b| yield b if b && !b.fainted? && b.opposes?(@index) }
|
||||
end
|
||||
|
||||
# Returns the battler that is most directly opposite to self. unfaintedOnly is
|
||||
# whether it should prefer to return a non-fainted battler.
|
||||
def pbDirectOpposing(unfaintedOnly=false)
|
||||
@battle.pbGetOpposingIndicesInOrder(@index).each do |i|
|
||||
next if !@battle.battlers[i]
|
||||
break if unfaintedOnly && @battle.battlers[i].fainted?
|
||||
return @battle.battlers[i]
|
||||
end
|
||||
# Wanted an unfainted battler but couldn't find one; make do with a fainted
|
||||
# battler
|
||||
@battle.pbGetOpposingIndicesInOrder(@index).each do |i|
|
||||
return @battle.battlers[i] if @battle.battlers[i]
|
||||
end
|
||||
return @battle.battlers[(@index^1)]
|
||||
end
|
||||
end
|
||||
326
Data/Scripts/011_Battle/001_Battler/002_Battler_Initialize.rb
Normal file
326
Data/Scripts/011_Battle/001_Battler/002_Battler_Initialize.rb
Normal file
@@ -0,0 +1,326 @@
|
||||
class PokeBattle_Battler
|
||||
#=============================================================================
|
||||
# Creating a battler
|
||||
#=============================================================================
|
||||
def initialize(btl,idxBattler)
|
||||
@battle = btl
|
||||
@index = idxBattler
|
||||
@captured = false
|
||||
@dummy = false
|
||||
@stages = []
|
||||
@effects = []
|
||||
@damageState = PokeBattle_DamageState.new
|
||||
pbInitBlank
|
||||
pbInitEffects(false)
|
||||
end
|
||||
|
||||
def pbInitBlank
|
||||
@name = ""
|
||||
@species = 0
|
||||
@form = 0
|
||||
@level = 0
|
||||
@hp = @totalhp = 0
|
||||
@type1 = @type2 = 0
|
||||
@ability = 0
|
||||
@item = 0
|
||||
@gender = 0
|
||||
@attack = @defense = @spatk = @spdef = @speed = 0
|
||||
@status = PBStatuses::NONE
|
||||
@statusCount = 0
|
||||
@pokemon = nil
|
||||
@pokemonIndex = -1
|
||||
@participants = []
|
||||
@moves = []
|
||||
@iv = [0,0,0,0,0,0]
|
||||
end
|
||||
|
||||
# Used by Future Sight only, when Future Sight's user is no longer in battle.
|
||||
def pbInitDummyPokemon(pkmn,idxParty)
|
||||
raise _INTL("An egg can't be an active Pokémon.") if pkmn.egg?
|
||||
@name = pkmn.name
|
||||
@species = pkmn.species
|
||||
@form = pkmn.form
|
||||
@level = pkmn.level
|
||||
@hp = pkmn.hp
|
||||
@totalhp = pkmn.totalhp
|
||||
@type1 = pkmn.type1
|
||||
@type2 = pkmn.type2
|
||||
# ability and item intentionally not copied across here
|
||||
@gender = pkmn.gender
|
||||
@attack = pkmn.attack
|
||||
@defense = pkmn.defense
|
||||
@spatk = pkmn.spatk
|
||||
@spdef = pkmn.spdef
|
||||
@speed = pkmn.speed
|
||||
@status = pkmn.status
|
||||
@statusCount = pkmn.statusCount
|
||||
@pokemon = pkmn
|
||||
@pokemonIndex = idxParty
|
||||
@participants = []
|
||||
# moves intentionally not copied across here
|
||||
@iv = pkmn.iv.clone
|
||||
@dummy = true
|
||||
end
|
||||
|
||||
def pbInitialize(pkmn,idxParty,batonPass=false)
|
||||
pbInitPokemon(pkmn,idxParty)
|
||||
pbInitEffects(batonPass)
|
||||
end
|
||||
|
||||
def pbInitPokemon(pkmn,idxParty)
|
||||
raise _INTL("An egg can't be an active Pokémon.") if pkmn.egg?
|
||||
@name = pkmn.name
|
||||
@species = pkmn.species
|
||||
@form = pkmn.form
|
||||
@level = pkmn.level
|
||||
@hp = pkmn.hp
|
||||
@totalhp = pkmn.totalhp
|
||||
@type1 = pkmn.type1
|
||||
@type2 = pkmn.type2
|
||||
@ability = pkmn.ability
|
||||
@item = pkmn.item
|
||||
@gender = pkmn.gender
|
||||
@attack = pkmn.attack
|
||||
@defense = pkmn.defense
|
||||
@spatk = pkmn.spatk
|
||||
@spdef = pkmn.spdef
|
||||
@speed = pkmn.speed
|
||||
@status = pkmn.status
|
||||
@statusCount = pkmn.statusCount
|
||||
@pokemon = pkmn
|
||||
@pokemonIndex = idxParty
|
||||
@participants = [] # Participants earn Exp. if this battler is defeated
|
||||
@moves = []
|
||||
pkmn.moves.each_with_index do |m,i|
|
||||
@moves[i] = PokeBattle_Move.pbFromPBMove(@battle,m)
|
||||
end
|
||||
@iv = pkmn.iv.clone
|
||||
end
|
||||
|
||||
def pbInitEffects(batonPass)
|
||||
if batonPass
|
||||
# These effects are passed on if Baton Pass is used, but they need to be
|
||||
# reapplied
|
||||
@effects[PBEffects::LaserFocus] = (@effects[PBEffects::LaserFocus]>0) ? 2 : 0
|
||||
@effects[PBEffects::LockOn] = (@effects[PBEffects::LockOn]>0) ? 2 : 0
|
||||
if @effects[PBEffects::PowerTrick]
|
||||
@attack,@defense = @defense,@attack
|
||||
end
|
||||
# These effects are passed on if Baton Pass is used, but they need to be
|
||||
# cancelled in certain circumstances anyway
|
||||
@effects[PBEffects::Telekinesis] = 0 if isConst?(@species,PBSpecies,:GENGAR) && mega?
|
||||
@effects[PBEffects::GastroAcid] = false if nonNegatableAbility?
|
||||
else
|
||||
# These effects are passed on if Baton Pass is used
|
||||
@stages[PBStats::ATTACK] = 0
|
||||
@stages[PBStats::DEFENSE] = 0
|
||||
@stages[PBStats::SPEED] = 0
|
||||
@stages[PBStats::SPATK] = 0
|
||||
@stages[PBStats::SPDEF] = 0
|
||||
@stages[PBStats::EVASION] = 0
|
||||
@stages[PBStats::ACCURACY] = 0
|
||||
@effects[PBEffects::AquaRing] = false
|
||||
@effects[PBEffects::Confusion] = 0
|
||||
@effects[PBEffects::Curse] = false
|
||||
@effects[PBEffects::Embargo] = 0
|
||||
@effects[PBEffects::FocusEnergy] = 0
|
||||
@effects[PBEffects::GastroAcid] = false
|
||||
@effects[PBEffects::HealBlock] = 0
|
||||
@effects[PBEffects::Ingrain] = false
|
||||
@effects[PBEffects::LaserFocus] = 0
|
||||
@effects[PBEffects::LeechSeed] = -1
|
||||
@effects[PBEffects::LockOn] = 0
|
||||
@effects[PBEffects::LockOnPos] = -1
|
||||
@effects[PBEffects::MagnetRise] = 0
|
||||
@effects[PBEffects::PerishSong] = 0
|
||||
@effects[PBEffects::PerishSongUser] = -1
|
||||
@effects[PBEffects::PowerTrick] = false
|
||||
@effects[PBEffects::Substitute] = 0
|
||||
@effects[PBEffects::Telekinesis] = 0
|
||||
end
|
||||
@damageState.reset
|
||||
@fainted = (@hp==0)
|
||||
@initialHP = 0
|
||||
@lastAttacker = []
|
||||
@lastFoeAttacker = []
|
||||
@lastHPLost = 0
|
||||
@lastHPLostFromFoe = 0
|
||||
@tookDamage = false
|
||||
@tookPhysicalHit = false
|
||||
@lastMoveUsed = -1
|
||||
@lastMoveUsedType = -1
|
||||
@lastRegularMoveUsed = -1
|
||||
@lastRegularMoveTarget = -1
|
||||
@lastRoundMoved = -1
|
||||
@lastMoveFailed = false
|
||||
@lastRoundMoveFailed = false
|
||||
@movesUsed = []
|
||||
@turnCount = 0
|
||||
@effects[PBEffects::Attract] = -1
|
||||
@battle.eachBattler do |b| # Other battlers no longer attracted to self
|
||||
b.effects[PBEffects::Attract] = -1 if b.effects[PBEffects::Attract]==@index
|
||||
end
|
||||
@effects[PBEffects::BanefulBunker] = false
|
||||
@effects[PBEffects::BeakBlast] = false
|
||||
@effects[PBEffects::Bide] = 0
|
||||
@effects[PBEffects::BideDamage] = 0
|
||||
@effects[PBEffects::BideTarget] = -1
|
||||
@effects[PBEffects::BurnUp] = false
|
||||
@effects[PBEffects::Charge] = 0
|
||||
@effects[PBEffects::ChoiceBand] = -1
|
||||
@effects[PBEffects::Counter] = -1
|
||||
@effects[PBEffects::CounterTarget] = -1
|
||||
@effects[PBEffects::Dancer] = false
|
||||
@effects[PBEffects::DefenseCurl] = false
|
||||
@effects[PBEffects::DestinyBond] = false
|
||||
@effects[PBEffects::DestinyBondPrevious] = false
|
||||
@effects[PBEffects::DestinyBondTarget] = -1
|
||||
@effects[PBEffects::Disable] = 0
|
||||
@effects[PBEffects::DisableMove] = 0
|
||||
@effects[PBEffects::Electrify] = false
|
||||
@effects[PBEffects::Encore] = 0
|
||||
@effects[PBEffects::EncoreMove] = 0
|
||||
@effects[PBEffects::Endure] = false
|
||||
@effects[PBEffects::FirstPledge] = 0
|
||||
@effects[PBEffects::FlashFire] = false
|
||||
@effects[PBEffects::Flinch] = false
|
||||
@effects[PBEffects::FocusPunch] = false
|
||||
@effects[PBEffects::FollowMe] = 0
|
||||
@effects[PBEffects::Foresight] = false
|
||||
@effects[PBEffects::FuryCutter] = 0
|
||||
@effects[PBEffects::GemConsumed] = 0
|
||||
@effects[PBEffects::Grudge] = false
|
||||
@effects[PBEffects::HelpingHand] = false
|
||||
@effects[PBEffects::HyperBeam] = 0
|
||||
@effects[PBEffects::Illusion] = nil
|
||||
if hasActiveAbility?(:ILLUSION)
|
||||
idxLastParty = @battle.pbLastInTeam(@index)
|
||||
if idxLastParty!=@pokemonIndex
|
||||
@effects[PBEffects::Illusion] = @battle.pbParty(@index)[idxLastParty]
|
||||
end
|
||||
end
|
||||
@effects[PBEffects::Imprison] = false
|
||||
@effects[PBEffects::Instruct] = false
|
||||
@effects[PBEffects::Instructed] = false
|
||||
@effects[PBEffects::KingsShield] = false
|
||||
@battle.eachBattler do |b| # Other battlers lose their lock-on against self
|
||||
next if b.effects[PBEffects::LockOn]==0
|
||||
next if b.effects[PBEffects::LockOnPos]!=@index
|
||||
b.effects[PBEffects::LockOn] = 0
|
||||
b.effects[PBEffects::LockOnPos] = -1
|
||||
end
|
||||
@effects[PBEffects::MagicBounce] = false
|
||||
@effects[PBEffects::MagicCoat] = false
|
||||
@effects[PBEffects::MeanLook] = -1
|
||||
@battle.eachBattler do |b| # Other battlers no longer blocked by self
|
||||
b.effects[PBEffects::MeanLook] = -1 if b.effects[PBEffects::MeanLook]==@index
|
||||
end
|
||||
@effects[PBEffects::MeFirst] = false
|
||||
@effects[PBEffects::Metronome] = 0
|
||||
@effects[PBEffects::MicleBerry] = false
|
||||
@effects[PBEffects::Minimize] = false
|
||||
@effects[PBEffects::MiracleEye] = false
|
||||
@effects[PBEffects::MirrorCoat] = -1
|
||||
@effects[PBEffects::MirrorCoatTarget] = -1
|
||||
@effects[PBEffects::MoveNext] = false
|
||||
@effects[PBEffects::MudSport] = false
|
||||
@effects[PBEffects::Nightmare] = false
|
||||
@effects[PBEffects::Outrage] = 0
|
||||
@effects[PBEffects::ParentalBond] = 0
|
||||
@effects[PBEffects::PickupItem] = 0
|
||||
@effects[PBEffects::PickupUse] = 0
|
||||
@effects[PBEffects::Pinch] = false
|
||||
@effects[PBEffects::Powder] = false
|
||||
@effects[PBEffects::Prankster] = false
|
||||
@effects[PBEffects::PriorityAbility] = false
|
||||
@effects[PBEffects::PriorityItem] = false
|
||||
@effects[PBEffects::Protect] = false
|
||||
@effects[PBEffects::ProtectRate] = 1
|
||||
@effects[PBEffects::Pursuit] = false
|
||||
@effects[PBEffects::Quash] = 0
|
||||
@effects[PBEffects::Rage] = false
|
||||
@effects[PBEffects::RagePowder] = false
|
||||
@effects[PBEffects::Revenge] = 0
|
||||
@effects[PBEffects::Rollout] = 0
|
||||
@effects[PBEffects::Roost] = false
|
||||
@effects[PBEffects::SkyDrop] = -1
|
||||
@battle.eachBattler do |b| # Other battlers no longer Sky Dropped by self
|
||||
b.effects[PBEffects::SkyDrop] = -1 if b.effects[PBEffects::SkyDrop]==@index
|
||||
end
|
||||
@effects[PBEffects::SlowStart] = 0
|
||||
@effects[PBEffects::SmackDown] = false
|
||||
@effects[PBEffects::Snatch] = 0
|
||||
@effects[PBEffects::SpikyShield] = false
|
||||
@effects[PBEffects::Spotlight] = 0
|
||||
@effects[PBEffects::Stockpile] = 0
|
||||
@effects[PBEffects::StockpileDef] = 0
|
||||
@effects[PBEffects::StockpileSpDef] = 0
|
||||
@effects[PBEffects::Taunt] = 0
|
||||
@effects[PBEffects::ThroatChop] = 0
|
||||
@effects[PBEffects::Torment] = false
|
||||
@effects[PBEffects::Toxic] = 0
|
||||
@effects[PBEffects::Transform] = false
|
||||
@effects[PBEffects::TransformSpecies] = 0
|
||||
@effects[PBEffects::Trapping] = 0
|
||||
@effects[PBEffects::TrappingMove] = 0
|
||||
@effects[PBEffects::TrappingUser] = -1
|
||||
@battle.eachBattler do |b| # Other battlers no longer trapped by self
|
||||
next if b.effects[PBEffects::TrappingUser]!=@index
|
||||
b.effects[PBEffects::Trapping] = 0
|
||||
b.effects[PBEffects::TrappingUser] = -1
|
||||
end
|
||||
@effects[PBEffects::Truant] = false
|
||||
@effects[PBEffects::TwoTurnAttack] = 0
|
||||
@effects[PBEffects::Type3] = -1
|
||||
@effects[PBEffects::Unburden] = false
|
||||
@effects[PBEffects::Uproar] = 0
|
||||
@effects[PBEffects::WaterSport] = false
|
||||
@effects[PBEffects::WeightChange] = 0
|
||||
@effects[PBEffects::Yawn] = 0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Refreshing a battler's properties
|
||||
#=============================================================================
|
||||
def pbUpdate(fullChange=false)
|
||||
return if !@pokemon
|
||||
@pokemon.calcStats
|
||||
@level = @pokemon.level
|
||||
@hp = @pokemon.hp
|
||||
@totalhp = @pokemon.totalhp
|
||||
if !@effects[PBEffects::Transform]
|
||||
@attack = @pokemon.attack
|
||||
@defense = @pokemon.defense
|
||||
@spatk = @pokemon.spatk
|
||||
@spdef = @pokemon.spdef
|
||||
@speed = @pokemon.speed
|
||||
if fullChange
|
||||
@type1 = @pokemon.type1
|
||||
@type2 = @pokemon.type2
|
||||
@ability = @pokemon.ability
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Used only to erase the battler of a Shadow Pokémon that has been snagged.
|
||||
def pbReset
|
||||
@pokemon = nil
|
||||
@pokemonIndex = -1
|
||||
@hp = 0
|
||||
pbInitEffects(false)
|
||||
@participants = []
|
||||
# Reset status
|
||||
@status = PBStatuses::NONE
|
||||
@statusCount = 0
|
||||
# Reset choice
|
||||
@battle.pbClearChoice(@index)
|
||||
end
|
||||
|
||||
# Update which Pokémon will gain Exp if this battler is defeated.
|
||||
def pbUpdateParticipants
|
||||
return if fainted? || !@battle.opposes?(@index)
|
||||
eachOpposing do |b|
|
||||
@participants.push(b.pokemonIndex) if !@participants.include?(b.pokemonIndex)
|
||||
end
|
||||
end
|
||||
end
|
||||
301
Data/Scripts/011_Battle/001_Battler/003_Battler_ChangeSelf.rb
Normal file
301
Data/Scripts/011_Battle/001_Battler/003_Battler_ChangeSelf.rb
Normal file
@@ -0,0 +1,301 @@
|
||||
class PokeBattle_Battler
|
||||
#=============================================================================
|
||||
# Change HP
|
||||
#=============================================================================
|
||||
def pbReduceHP(amt,anim=true,registerDamage=true,anyAnim=true)
|
||||
amt = amt.round
|
||||
amt = @hp if amt>@hp
|
||||
amt = 1 if amt<1 && !fainted?
|
||||
oldHP = @hp
|
||||
self.hp -= amt
|
||||
PBDebug.log("[HP change] #{pbThis} lost #{amt} HP (#{oldHP}=>#{@hp})")
|
||||
raise _INTL("HP less than 0") if @hp<0
|
||||
raise _INTL("HP greater than total HP") if @hp>@totalhp
|
||||
@battle.scene.pbHPChanged(self,oldHP,anim) if anyAnim && amt>0
|
||||
@tookDamage = true if amt>0 && registerDamage
|
||||
return amt
|
||||
end
|
||||
|
||||
def pbRecoverHP(amt,anim=true,anyAnim=true)
|
||||
amt = amt.round
|
||||
amt = @totalhp-@hp if amt>@totalhp-@hp
|
||||
amt = 1 if amt<1 && @hp<@totalhp
|
||||
oldHP = @hp
|
||||
self.hp += amt
|
||||
PBDebug.log("[HP change] #{pbThis} gained #{amt} HP (#{oldHP}=>#{@hp})")
|
||||
raise _INTL("HP less than 0") if @hp<0
|
||||
raise _INTL("HP greater than total HP") if @hp>@totalhp
|
||||
@battle.scene.pbHPChanged(self,oldHP,anim) if anyAnim && amt>0
|
||||
return amt
|
||||
end
|
||||
|
||||
def pbRecoverHPFromDrain(amt,target,msg=nil)
|
||||
if target.hasActiveAbility?(:LIQUIDOOZE)
|
||||
oldHP = @hp
|
||||
@battle.pbShowAbilitySplash(target)
|
||||
pbReduceHP(amt)
|
||||
@battle.pbDisplay(_INTL("{1} sucked up the liquid ooze!",pbThis))
|
||||
@battle.pbHideAbilitySplash(target)
|
||||
pbItemHPHealCheck
|
||||
else
|
||||
msg = _INTL("{1} had its energy drained!",target.pbThis) if !msg || msg==""
|
||||
@battle.pbDisplay(msg)
|
||||
if canHeal?
|
||||
amt = (amt*1.3).floor if hasActiveItem?(:BIGROOT)
|
||||
pbRecoverHP(amt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbFaint(showMessage=true)
|
||||
if !fainted?
|
||||
PBDebug.log("!!!***Can't faint with HP greater than 0")
|
||||
return
|
||||
end
|
||||
return if @fainted # Has already fainted properly
|
||||
@battle.pbDisplayBrief(_INTL("{1} fainted!",pbThis)) if showMessage
|
||||
PBDebug.log("[Pokémon fainted] #{pbThis} (#{@index})") if !showMessage
|
||||
@battle.scene.pbFaintBattler(self)
|
||||
pbInitEffects(false)
|
||||
# Reset status
|
||||
self.status = PBStatuses::NONE
|
||||
self.statusCount = 0
|
||||
# Lose happiness
|
||||
if @pokemon && @battle.internalBattle
|
||||
badLoss = false
|
||||
@battle.eachOtherSideBattler(@index) do |b|
|
||||
badLoss = true if b.level>=self.level+30
|
||||
end
|
||||
@pokemon.changeHappiness((badLoss) ? "faintbad" : "faint")
|
||||
end
|
||||
# Reset form
|
||||
@battle.peer.pbOnLeavingBattle(@battle,@pokemon,@battle.usedInBattle[idxOwnSide][@index/2])
|
||||
@pokemon.makeUnmega if mega?
|
||||
@pokemon.makeUnprimal if primal?
|
||||
# Do other things
|
||||
@battle.pbClearChoice(@index) # Reset choice
|
||||
pbOwnSide.effects[PBEffects::LastRoundFainted] = @battle.turnCount
|
||||
# Check other battlers' abilities that trigger upon a battler fainting
|
||||
pbAbilitiesOnFainting
|
||||
# Check for end of primordial weather
|
||||
@battle.pbEndPrimordialWeather
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Move PP
|
||||
#=============================================================================
|
||||
def pbSetPP(move,pp)
|
||||
move.pp = pp
|
||||
# No need to care about @effects[PBEffects::Mimic], since Mimic can't copy
|
||||
# Mimic
|
||||
if move.realMove && move.id==move.realMove.id && !@effects[PBEffects::Transform]
|
||||
move.realMove.pp = pp
|
||||
end
|
||||
end
|
||||
|
||||
def pbReducePP(move)
|
||||
return true if usingMultiTurnAttack?
|
||||
return true if move.pp<0 # Don't reduce PP for special calls of moves
|
||||
return true if move.totalpp<=0 # Infinite PP, can always be used
|
||||
return false if move.pp==0 # Ran out of PP, couldn't reduce
|
||||
pbSetPP(move,move.pp-1) if move.pp>0
|
||||
return true
|
||||
end
|
||||
|
||||
def pbReducePPOther(move)
|
||||
pbSetPP(move,move.pp-1) if move.pp>0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Change type
|
||||
#=============================================================================
|
||||
def pbChangeTypes(newType)
|
||||
if newType.is_a?(PokeBattle_Battler)
|
||||
newTypes = newType.pbTypes
|
||||
newTypes.push(getConst(PBTypes,:NORMAL) || 0) if newTypes.length==0
|
||||
newType3 = newType.effects[PBEffects::Type3]
|
||||
newType3 = -1 if newTypes.include?(newType3)
|
||||
@type1 = newTypes[0]
|
||||
@type2 = (newTypes.length==1) ? newTypes[0] : newTypes[1]
|
||||
@effects[PBEffects::Type3] = newType3
|
||||
else
|
||||
newType = getConst(PBTypes,newType) if newType.is_a?(Symbol) || newType.is_a?(String)
|
||||
@type1 = newType
|
||||
@type2 = newType
|
||||
@effects[PBEffects::Type3] = -1
|
||||
end
|
||||
@effects[PBEffects::BurnUp] = false
|
||||
@effects[PBEffects::Roost] = false
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Forms
|
||||
#=============================================================================
|
||||
def pbChangeForm(newForm,msg)
|
||||
return if fainted? || @effects[PBEffects::Transform] || @form==newForm
|
||||
oldForm = @form
|
||||
oldDmg = @totalhp-@hp
|
||||
self.form = newForm
|
||||
pbUpdate(true)
|
||||
@hp = @totalhp-oldDmg
|
||||
@effects[PBEffects::WeightChange] = 0 if NEWEST_BATTLE_MECHANICS
|
||||
@battle.scene.pbChangePokemon(self,@pokemon)
|
||||
@battle.scene.pbRefreshOne(@index)
|
||||
@battle.pbDisplay(msg) if msg && msg!=""
|
||||
PBDebug.log("[Form changed] #{pbThis} changed from form #{oldForm} to form #{newForm}")
|
||||
@battle.pbSetSeen(self)
|
||||
end
|
||||
|
||||
def pbCheckFormOnStatusChange
|
||||
return if fainted? || @effects[PBEffects::Transform]
|
||||
# Shaymin - reverts if frozen
|
||||
if isConst?(@species,PBSpecies,:SHAYMIN) && frozen?
|
||||
pbChangeForm(0,_INTL("{1} transformed!",pbThis))
|
||||
end
|
||||
end
|
||||
|
||||
def pbCheckFormOnMovesetChange
|
||||
return if fainted? || @effects[PBEffects::Transform]
|
||||
# Keldeo - knowing Secret Sword
|
||||
if isConst?(@species,PBSpecies,:KELDEO)
|
||||
newForm = 0
|
||||
newForm = 1 if pbHasMove?(:SECRETSWORD)
|
||||
pbChangeForm(newForm,_INTL("{1} transformed!",pbThis))
|
||||
end
|
||||
end
|
||||
|
||||
def pbCheckFormOnWeatherChange
|
||||
return if fainted? || @effects[PBEffects::Transform]
|
||||
# Castform - Forecast
|
||||
if isConst?(@species,PBSpecies,:CASTFORM)
|
||||
if hasActiveAbility?(:FORECAST)
|
||||
newForm = 0
|
||||
case @battle.pbWeather
|
||||
when PBWeather::Sun, PBWeather::HarshSun; newForm = 1
|
||||
when PBWeather::Rain, PBWeather::HeavyRain; newForm = 2
|
||||
when PBWeather::Hail; newForm = 3
|
||||
end
|
||||
if @form!=newForm
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(newForm,_INTL("{1} transformed!",pbThis))
|
||||
end
|
||||
else
|
||||
pbChangeForm(0,_INTL("{1} transformed!",pbThis))
|
||||
end
|
||||
end
|
||||
# Cherrim - Flower Gift
|
||||
if isConst?(@species,PBSpecies,:CHERRIM)
|
||||
if hasActiveAbility?(:FLOWERGIFT)
|
||||
newForm = 0
|
||||
case @battle.pbWeather
|
||||
when PBWeather::Sun, PBWeather::HarshSun; newForm = 1
|
||||
end
|
||||
if @form!=newForm
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(newForm,_INTL("{1} transformed!",pbThis))
|
||||
end
|
||||
else
|
||||
pbChangeForm(0,_INTL("{1} transformed!",pbThis))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Checks the Pokémon's form and updates it if necessary. Used for when a
|
||||
# Pokémon enters battle (endOfRound=false) and at the end of each round
|
||||
# (endOfRound=true).
|
||||
def pbCheckForm(endOfRound=false)
|
||||
return if fainted? || @effects[PBEffects::Transform]
|
||||
# Form changes upon entering battle and when the weather changes
|
||||
pbCheckFormOnWeatherChange if !endOfRound
|
||||
# Darmanitan - Zen Mode
|
||||
if isConst?(@species,PBSpecies,:DARMANITAN) && isConst?(@ability,PBAbilities,:ZENMODE)
|
||||
if @hp<=@totalhp/2
|
||||
if @form!=1
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(1,_INTL("{1} triggered!",abilityName))
|
||||
end
|
||||
elsif @form!=0
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(0,_INTL("{1} triggered!",abilityName))
|
||||
end
|
||||
end
|
||||
# Minior - Shields Down
|
||||
if isConst?(@species,PBSpecies,:MINIOR) && isConst?(@ability,PBAbilities,:SHIELDSDOWN)
|
||||
if @hp>@totalhp/2 # Turn into Meteor form
|
||||
newForm = (@form>=7) ? @form-7 : @form
|
||||
if @form!=newForm
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(newForm,_INTL("{1} deactivated!",abilityName))
|
||||
elsif !endOfRound
|
||||
@battle.pbDisplay(_INTL("{1} deactivated!",abilityName))
|
||||
end
|
||||
elsif @form<7 # Turn into Core form
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(@form+7,_INTL("{1} activated!",abilityName))
|
||||
end
|
||||
end
|
||||
# Wishiwashi - Schooling
|
||||
if isConst?(@species,PBSpecies,:WISHIWASHI) && isConst?(@ability,PBAbilities,:SCHOOLING)
|
||||
if @level>=20 && @hp>@totalhp/4
|
||||
if @form!=1
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(1,_INTL("{1} formed a school!",pbThis))
|
||||
end
|
||||
elsif @form!=0
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(0,_INTL("{1} stopped schooling!",pbThis))
|
||||
end
|
||||
end
|
||||
# Zygarde - Power Construct
|
||||
if isConst?(@species,PBSpecies,:ZYGARDE) && isConst?(@ability,PBAbilities,:POWERCONSTRUCT) &&
|
||||
endOfRound
|
||||
if @hp<=@totalhp/2 && @form<2 # Turn into Complete Forme
|
||||
newForm = @form+2
|
||||
@battle.pbDisplay(_INTL("You sense the presence of many!"))
|
||||
@battle.pbShowAbilitySplash(self,true)
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
pbChangeForm(newForm,_INTL("{1} transformed into its Complete Forme!",pbThis))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbTransform(target)
|
||||
oldAbil = @ability
|
||||
@effects[PBEffects::Transform] = true
|
||||
@effects[PBEffects::TransformSpecies] = target.species
|
||||
pbChangeTypes(target)
|
||||
@ability = target.ability
|
||||
@attack = target.attack
|
||||
@defense = target.defense
|
||||
@spatk = target.spatk
|
||||
@spdef = target.spdef
|
||||
@speed = target.speed
|
||||
PBStats.eachBattleStat { |s| @stages[s] = target.stages[s] }
|
||||
if NEWEST_BATTLE_MECHANICS
|
||||
@effects[PBEffects::FocusEnergy] = target.effects[PBEffects::FocusEnergy]
|
||||
@effects[PBEffects::LaserFocus] = target.effects[PBEffects::LaserFocus]
|
||||
end
|
||||
@moves.clear
|
||||
target.moves.each_with_index do |m,i|
|
||||
@moves[i] = PokeBattle_Move.pbFromPBMove(@battle,PBMove.new(m.id))
|
||||
@moves[i].pp = 5
|
||||
@moves[i].totalpp = 5
|
||||
end
|
||||
@effects[PBEffects::Disable] = 0
|
||||
@effects[PBEffects::DisableMove] = 0
|
||||
@effects[PBEffects::WeightChange] = target.effects[PBEffects::WeightChange]
|
||||
@battle.scene.pbRefreshOne(@index)
|
||||
@battle.pbDisplay(_INTL("{1} transformed into {2}!",pbThis,target.pbThis(true)))
|
||||
pbOnAbilityChanged(oldAbil)
|
||||
end
|
||||
|
||||
def pbHyperMode; end
|
||||
end
|
||||
573
Data/Scripts/011_Battle/001_Battler/004_Battler_Statuses.rb
Normal file
573
Data/Scripts/011_Battle/001_Battler/004_Battler_Statuses.rb
Normal file
@@ -0,0 +1,573 @@
|
||||
class PokeBattle_Battler
|
||||
#=============================================================================
|
||||
# Generalised checks for whether a status problem can be inflicted
|
||||
#=============================================================================
|
||||
# NOTE: Not all "does it have this status?" checks use this method. If the
|
||||
# check is leading up to curing self of that status condition, then it
|
||||
# will look at the value of @status directly instead - if it is that
|
||||
# PBStatuses value then it is curable. This method only checks for
|
||||
# "counts as having that status", which includes Comatose which can't be
|
||||
# cured.
|
||||
def pbHasStatus?(checkStatus)
|
||||
if BattleHandlers.triggerStatusCheckAbilityNonIgnorable(@ability,self,checkStatus)
|
||||
return true
|
||||
end
|
||||
return @status==checkStatus
|
||||
end
|
||||
|
||||
def pbHasAnyStatus?
|
||||
if BattleHandlers.triggerStatusCheckAbilityNonIgnorable(@ability,self,nil)
|
||||
return true
|
||||
end
|
||||
return @status!=PBStatuses::NONE
|
||||
end
|
||||
|
||||
def pbCanInflictStatus?(newStatus,user,showMessages,move=nil,ignoreStatus=false)
|
||||
return false if fainted?
|
||||
selfInflicted = (user && user.index==@index)
|
||||
# Already have that status problem
|
||||
if self.status==newStatus && !ignoreStatus
|
||||
if showMessages
|
||||
msg = ""
|
||||
case self.status
|
||||
when PBStatuses::SLEEP; msg = _INTL("{1} is already asleep!",pbThis)
|
||||
when PBStatuses::POISON; msg = _INTL("{1} is already poisoned!",pbThis)
|
||||
when PBStatuses::BURN; msg = _INTL("{1} already has a burn!",pbThis)
|
||||
when PBStatuses::PARALYSIS; msg = _INTL("{1} is already paralyzed!",pbThis)
|
||||
when PBStatuses::FROZEN; msg = _INTL("{1} is already frozen solid!",pbThis)
|
||||
end
|
||||
@battle.pbDisplay(msg)
|
||||
end
|
||||
return false
|
||||
end
|
||||
# Trying to replace a status problem with another one
|
||||
if self.status!=PBStatuses::NONE && !ignoreStatus && !selfInflicted
|
||||
@battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages
|
||||
return false
|
||||
end
|
||||
# Trying to inflict a status problem on a Pokémon behind a substitute
|
||||
if @effects[PBEffects::Substitute]>0 && !(move && move.ignoresSubstitute?(user)) &&
|
||||
!selfInflicted
|
||||
@battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages
|
||||
return false
|
||||
end
|
||||
# Weather immunity
|
||||
if newStatus==PBStatuses::FROZEN &&
|
||||
(@battle.pbWeather==PBWeather::Sun || @battle.pbWeather==PBWeather::HarshSun)
|
||||
@battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages
|
||||
return false
|
||||
end
|
||||
# Terrains immunity
|
||||
if affectedByTerrain?
|
||||
case @battle.field.terrain
|
||||
when PBBattleTerrains::Electric
|
||||
if newStatus==PBStatuses::SLEEP
|
||||
@battle.pbDisplay(_INTL("{1} surrounds itself with electrified terrain!",
|
||||
pbThis(true))) if showMessages
|
||||
return false
|
||||
end
|
||||
when PBBattleTerrains::Misty
|
||||
@battle.pbDisplay(_INTL("{1} surrounds itself with misty terrain!",pbThis(true))) if showMessages
|
||||
return false
|
||||
end
|
||||
end
|
||||
# Uproar immunity
|
||||
if newStatus==PBStatuses::SLEEP &&
|
||||
!(hasActiveAbility?(:SOUNDPROOF) && !@battle.moldBreaker)
|
||||
@battle.eachBattler do |b|
|
||||
next if b.effects[PBEffects::Uproar]==0
|
||||
@battle.pbDisplay(_INTL("But the uproar kept {1} awake!",pbThis(true))) if showMessages
|
||||
return false
|
||||
end
|
||||
end
|
||||
# Type immunities
|
||||
hasImmuneType = false
|
||||
case newStatus
|
||||
when PBStatuses::SLEEP
|
||||
# No type is immune to sleep
|
||||
when PBStatuses::POISON
|
||||
if !(user && user.hasActiveAbility?(:CORROSION))
|
||||
hasImmuneType |= pbHasType?(:POISON)
|
||||
hasImmuneType |= pbHasType?(:STEEL)
|
||||
end
|
||||
when PBStatuses::BURN
|
||||
hasImmuneType |= pbHasType?(:FIRE)
|
||||
when PBStatuses::PARALYSIS
|
||||
hasImmuneType |= pbHasType?(:ELECTRIC) && NEWEST_BATTLE_MECHANICS
|
||||
when PBStatuses::FROZEN
|
||||
hasImmuneType |= pbHasType?(:ICE)
|
||||
end
|
||||
if hasImmuneType
|
||||
@battle.pbDisplay(_INTL("It doesn't affect {1}...",pbThis(true))) if showMessages
|
||||
return false
|
||||
end
|
||||
# Ability immunity
|
||||
immuneByAbility = false; immAlly = nil
|
||||
if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(@ability,self,newStatus)
|
||||
immuneByAbility = true
|
||||
elsif selfInflicted || !@battle.moldBreaker
|
||||
if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(@ability,self,newStatus)
|
||||
immuneByAbility = true
|
||||
else
|
||||
eachAlly do |b|
|
||||
next if !b.abilityActive?
|
||||
next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability,self,newStatus)
|
||||
immuneByAbility = true
|
||||
immAlly = b
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if immuneByAbility
|
||||
if showMessages
|
||||
@battle.pbShowAbilitySplash(immAlly || self)
|
||||
msg = ""
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
case newStatus
|
||||
when PBStatuses::SLEEP; msg = _INTL("{1} stays awake!",pbThis)
|
||||
when PBStatuses::POISON; msg = _INTL("{1} cannot be poisoned!",pbThis)
|
||||
when PBStatuses::BURN; msg = _INTL("{1} cannot be burned!",pbThis)
|
||||
when PBStatuses::PARALYSIS; msg = _INTL("{1} cannot be paralyzed!",pbThis)
|
||||
when PBStatuses::FROZEN; msg = _INTL("{1} cannot be frozen solid!",pbThis)
|
||||
end
|
||||
elsif immAlly
|
||||
case newStatus
|
||||
when PBStatuses::SLEEP
|
||||
msg = _INTL("{1} stays awake because of {2}'s {3}!",
|
||||
pbThis,immAlly.pbThis(true),immAlly.abilityName)
|
||||
when PBStatuses::POISON
|
||||
msg = _INTL("{1} cannot be poisoned because of {2}'s {3}!",
|
||||
pbThis,immAlly.pbThis(true),immAlly.abilityName)
|
||||
when PBStatuses::BURN
|
||||
msg = _INTL("{1} cannot be burned because of {2}'s {3}!",
|
||||
pbThis,immAlly.pbThis(true),immAlly.abilityName)
|
||||
when PBStatuses::PARALYSIS
|
||||
msg = _INTL("{1} cannot be paralyzed because of {2}'s {3}!",
|
||||
pbThis,immAlly.pbThis(true),immAlly.abilityName)
|
||||
when PBStatuses::FROZEN
|
||||
msg = _INTL("{1} cannot be frozen solid because of {2}'s {3}!",
|
||||
pbThis,immAlly.pbThis(true),immAlly.abilityName)
|
||||
end
|
||||
else
|
||||
case newStatus
|
||||
when PBStatuses::SLEEP; msg = _INTL("{1} stays awake because of its {2}!",pbThis,abilityName)
|
||||
when PBStatuses::POISON; msg = _INTL("{1}'s {2} prevents poisoning!",pbThis,abilityName)
|
||||
when PBStatuses::BURN; msg = _INTL("{1}'s {2} prevents burns!",pbThis,abilityName)
|
||||
when PBStatuses::PARALYSIS; msg = _INTL("{1}'s {2} prevents paralysis!",pbThis,abilityName)
|
||||
when PBStatuses::FROZEN; msg = _INTL("{1}'s {2} prevents freezing!",pbThis,abilityName)
|
||||
end
|
||||
end
|
||||
@battle.pbDisplay(msg)
|
||||
@battle.pbHideAbilitySplash(immAlly || self)
|
||||
end
|
||||
return false
|
||||
end
|
||||
# Safeguard immunity
|
||||
if pbOwnSide.effects[PBEffects::Safeguard]>0 && !selfInflicted && move &&
|
||||
!(user && user.hasActiveAbility?(:INFILTRATOR))
|
||||
@battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!",pbThis)) if showMessages
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbCanSynchronizeStatus?(status,target)
|
||||
return false if fainted?
|
||||
# Trying to replace a status problem with another one
|
||||
return false if self.status!=PBStatuses::NONE
|
||||
# Terrain immunity
|
||||
return false if @battle.field.terrain==PBBattleTerrains::Misty && affectedByTerrain?
|
||||
# Type immunities
|
||||
hasImmuneType = false
|
||||
case self.status
|
||||
when PBStatuses::POISON
|
||||
# NOTE: target will have Synchronize, so it can't have Corrosion.
|
||||
if !(target && target.hasActiveAbility?(:CORROSION))
|
||||
hasImmuneType |= pbHasType?(:POISON)
|
||||
hasImmuneType |= pbHasType?(:STEEL)
|
||||
end
|
||||
when PBStatuses::BURN
|
||||
hasImmuneType |= pbHasType?(:FIRE)
|
||||
when PBStatuses::PARALYSIS
|
||||
hasImmuneType |= pbHasType?(:ELECTRIC) && NEWEST_BATTLE_MECHANICS
|
||||
end
|
||||
return false if hasImmuneType
|
||||
# Ability immunity
|
||||
if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(@ability,self,status)
|
||||
return false
|
||||
end
|
||||
if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(@ability,self,status)
|
||||
return false
|
||||
end
|
||||
eachAlly do |b|
|
||||
next if !b.abilityActive?
|
||||
next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability,self,status)
|
||||
return false
|
||||
end
|
||||
# Safeguard immunity
|
||||
if pbOwnSide.effects[PBEffects::Safeguard]>0 &&
|
||||
!(user && user.hasActiveAbility?(:INFILTRATOR))
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Generalised infliction of status problem
|
||||
#=============================================================================
|
||||
def pbInflictStatus(newStatus,newStatusCount=0,msg=nil,user=nil)
|
||||
# Inflict the new status
|
||||
self.status = newStatus
|
||||
self.statusCount = newStatusCount
|
||||
@effects[PBEffects::Toxic] = 0
|
||||
# Record status change in debug log, generate default message, show animation
|
||||
case newStatus
|
||||
when PBStatuses::SLEEP
|
||||
@battle.pbCommonAnimation("Sleep",self)
|
||||
msg = _INTL("{1} fell asleep!",pbThis) if !msg || msg==""
|
||||
when PBStatuses::POISON
|
||||
if newStatusCount>0
|
||||
@battle.pbCommonAnimation("Toxic",self)
|
||||
msg = _INTL("{1} was badly poisoned!",pbThis) if !msg || msg==""
|
||||
else
|
||||
@battle.pbCommonAnimation("Poison",self)
|
||||
msg = _INTL("{1} was poisoned!",pbThis) if !msg || msg==""
|
||||
end
|
||||
when PBStatuses::BURN
|
||||
@battle.pbCommonAnimation("Burn",self)
|
||||
msg = _INTL("{1} was burned!",pbThis) if !msg || msg==""
|
||||
when PBStatuses::PARALYSIS
|
||||
@battle.pbCommonAnimation("Paralysis",self)
|
||||
msg = _INTL("{1} is paralyzed! It may be unable to move!",pbThis) if !msg || msg==""
|
||||
when PBStatuses::FROZEN
|
||||
@battle.pbCommonAnimation("Frozen",self)
|
||||
msg = _INTL("{1} was frozen solid!",pbThis) if !msg || msg==""
|
||||
end
|
||||
# Show message
|
||||
@battle.pbDisplay(msg) if msg && msg!=""
|
||||
PBDebug.log("[Status change] #{pbThis}'s sleep count is #{newStatusCount}") if newStatus==PBStatuses::SLEEP
|
||||
pbCheckFormOnStatusChange
|
||||
# Synchronize
|
||||
if abilityActive?
|
||||
BattleHandlers.triggerAbilityOnStatusInflicted(@ability,self,user,newStatus)
|
||||
end
|
||||
# Status cures
|
||||
pbItemStatusCureCheck
|
||||
pbAbilityStatusCureCheck
|
||||
# Petal Dance/Outrage/Thrash get cancelled immediately by falling asleep
|
||||
# NOTE: I don't know why this applies only to Outrage and only to falling
|
||||
# asleep (i.e. it doesn't cancel Rollout/Uproar/other multi-turn
|
||||
# moves, and it doesn't cancel any moves if self becomes frozen/
|
||||
# disabled/anything else). This behaviour was tested in Gen 5.
|
||||
if @status==PBStatuses::SLEEP && @effects[PBEffects::Outrage]>0
|
||||
@effects[PBEffects::Outrage] = 0
|
||||
@currentMove = 0
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Sleep
|
||||
#=============================================================================
|
||||
def asleep?
|
||||
return pbHasStatus?(PBStatuses::SLEEP)
|
||||
end
|
||||
|
||||
def pbCanSleep?(user,showMessages,move=nil,ignoreStatus=false)
|
||||
return pbCanInflictStatus?(PBStatuses::SLEEP,user,showMessages,move,ignoreStatus)
|
||||
end
|
||||
|
||||
def pbCanSleepYawn?
|
||||
return false if self.status!=PBStatuses::NONE
|
||||
if affectedByTerrain?
|
||||
return false if @battle.field.terrain==PBBattleTerrains::Electric
|
||||
return false if @battle.field.terrain==PBBattleTerrains::Misty
|
||||
end
|
||||
if !hasActiveAbility?(:SOUNDPROOF)
|
||||
@battle.eachBattler do |b|
|
||||
return false if b.effects[PBEffects::Uproar]>0
|
||||
end
|
||||
end
|
||||
if BattleHandlers.triggerStatusImmunityAbilityNonIgnorable(@ability,self,PBStatuses::SLEEP)
|
||||
return false
|
||||
end
|
||||
# NOTE: Bulbapedia claims that Flower Veil shouldn't prevent sleep due to
|
||||
# drowsiness, but I disagree because that makes no sense. Also, the
|
||||
# comparable Sweet Veil does prevent sleep due to drowsiness.
|
||||
if abilityActive? && BattleHandlers.triggerStatusImmunityAbility(@ability,self,PBStatuses::SLEEP)
|
||||
return false
|
||||
end
|
||||
eachAlly do |b|
|
||||
next if !b.abilityActive?
|
||||
next if !BattleHandlers.triggerStatusImmunityAllyAbility(b.ability,self,PBStatuses::SLEEP)
|
||||
return false
|
||||
end
|
||||
# NOTE: Bulbapedia claims that Safeguard shouldn't prevent sleep due to
|
||||
# drowsiness. I disagree with this too. Compare with the other sided
|
||||
# effects Misty/Electric Terrain, which do prevent it.
|
||||
return false if pbOwnSide.effects[PBEffects::Safeguard]>0
|
||||
return true
|
||||
end
|
||||
|
||||
def pbSleep(msg=nil)
|
||||
pbInflictStatus(PBStatuses::SLEEP,pbSleepDuration,msg)
|
||||
end
|
||||
|
||||
def pbSleepSelf(msg=nil,duration=-1)
|
||||
pbInflictStatus(PBStatuses::SLEEP,pbSleepDuration(duration),msg)
|
||||
end
|
||||
|
||||
def pbSleepDuration(duration=-1)
|
||||
duration = 2+@battle.pbRandom(3) if duration<=0
|
||||
duration = (duration/2).floor if hasActiveAbility?(:EARLYBIRD)
|
||||
return duration
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Poison
|
||||
#=============================================================================
|
||||
def poisoned?
|
||||
return pbHasStatus?(PBStatuses::POISON)
|
||||
end
|
||||
|
||||
def pbCanPoison?(user,showMessages,move=nil)
|
||||
return pbCanInflictStatus?(PBStatuses::POISON,user,showMessages,move)
|
||||
end
|
||||
|
||||
def pbCanPoisonSynchronize?(target)
|
||||
return pbCanSynchronizeStatus?(PBStatuses::POISON,target)
|
||||
end
|
||||
|
||||
def pbPoison(user=nil,msg=nil,toxic=false)
|
||||
pbInflictStatus(PBStatuses::POISON,(toxic) ? 1 : 0,msg,user)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Burn
|
||||
#=============================================================================
|
||||
def burned?
|
||||
return pbHasStatus?(PBStatuses::BURN)
|
||||
end
|
||||
|
||||
def pbCanBurn?(user,showMessages,move=nil)
|
||||
return pbCanInflictStatus?(PBStatuses::BURN,user,showMessages,move)
|
||||
end
|
||||
|
||||
def pbCanBurnSynchronize?(target)
|
||||
return pbCanSynchronizeStatus?(PBStatuses::BURN,target)
|
||||
end
|
||||
|
||||
def pbBurn(user=nil,msg=nil)
|
||||
pbInflictStatus(PBStatuses::BURN,0,msg,user)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Paralyze
|
||||
#=============================================================================
|
||||
def paralyzed?
|
||||
return pbHasStatus?(PBStatuses::PARALYSIS)
|
||||
end
|
||||
|
||||
def pbCanParalyze?(user,showMessages,move=nil)
|
||||
return pbCanInflictStatus?(PBStatuses::PARALYSIS,user,showMessages,move)
|
||||
end
|
||||
|
||||
def pbCanParalyzeSynchronize?(target)
|
||||
return pbCanSynchronizeStatus?(PBStatuses::PARALYSIS,target)
|
||||
end
|
||||
|
||||
def pbParalyze(user=nil,msg=nil)
|
||||
pbInflictStatus(PBStatuses::PARALYSIS,0,msg,user)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Freeze
|
||||
#=============================================================================
|
||||
def frozen?
|
||||
return pbHasStatus?(PBStatuses::FROZEN)
|
||||
end
|
||||
|
||||
def pbCanFreeze?(user,showMessages,move=nil)
|
||||
return pbCanInflictStatus?(PBStatuses::FROZEN,user,showMessages,move)
|
||||
end
|
||||
|
||||
def pbFreeze(msg=nil)
|
||||
pbInflictStatus(PBStatuses::FROZEN,0,msg)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Generalised status displays
|
||||
#=============================================================================
|
||||
def pbContinueStatus
|
||||
anim = ""; msg = ""
|
||||
case self.status
|
||||
when PBStatuses::SLEEP
|
||||
anim = "Sleep"; msg = _INTL("{1} is fast asleep.",pbThis)
|
||||
when PBStatuses::POISON
|
||||
anim = (@statusCount>0) ? "Toxic" : "Poison"
|
||||
msg = _INTL("{1} was hurt by poison!",pbThis)
|
||||
when PBStatuses::BURN
|
||||
anim = "Burn"; msg = _INTL("{1} was hurt by its burn!",pbThis)
|
||||
when PBStatuses::PARALYSIS
|
||||
anim = "Paralysis"; msg = _INTL("{1} is paralyzed! It can't move!",pbThis)
|
||||
when PBStatuses::FROZEN
|
||||
anim = "Frozen"; msg = _INTL("{1} is frozen solid!",pbThis)
|
||||
end
|
||||
@battle.pbCommonAnimation(anim,self) if anim!=""
|
||||
yield if block_given?
|
||||
@battle.pbDisplay(msg) if msg!=""
|
||||
PBDebug.log("[Status continues] #{pbThis}'s sleep count is #{@statusCount}") if self.status==PBStatuses::SLEEP
|
||||
end
|
||||
|
||||
def pbCureStatus(showMessages=true)
|
||||
oldStatus = status
|
||||
self.status = PBStatuses::NONE
|
||||
if showMessages
|
||||
case oldStatus
|
||||
when PBStatuses::SLEEP; @battle.pbDisplay(_INTL("{1} woke up!",pbThis))
|
||||
when PBStatuses::POISON; @battle.pbDisplay(_INTL("{1} was cured of its poisoning.",pbThis))
|
||||
when PBStatuses::BURN; @battle.pbDisplay(_INTL("{1}'s burn was healed.",pbThis))
|
||||
when PBStatuses::PARALYSIS; @battle.pbDisplay(_INTL("{1} was cured of paralysis.",pbThis))
|
||||
when PBStatuses::FROZEN; @battle.pbDisplay(_INTL("{1} thawed out!",pbThis))
|
||||
end
|
||||
end
|
||||
PBDebug.log("[Status change] #{pbThis}'s status was cured") if !showMessages
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Confusion
|
||||
#=============================================================================
|
||||
def pbCanConfuse?(user=nil,showMessages=true,move=nil,selfInflicted=false)
|
||||
return false if fainted?
|
||||
if @effects[PBEffects::Confusion]>0
|
||||
@battle.pbDisplay(_INTL("{1} is already confused.",pbThis)) if showMessages
|
||||
return false
|
||||
end
|
||||
if @effects[PBEffects::Substitute]>0 && !(move && move.ignoresSubstitute?(user)) &&
|
||||
!selfInflicted
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if showMessages
|
||||
return false
|
||||
end
|
||||
# Terrains immunity
|
||||
if affectedByTerrain? && @battle.field.terrain==PBBattleTerrains::Misty
|
||||
@battle.pbDisplay(_INTL("{1} surrounds itself with misty terrain!",pbThis(true))) if showMessages
|
||||
return false
|
||||
end
|
||||
if selfInflicted || !@battle.moldBreaker
|
||||
if hasActiveAbility?(:OWNTEMPO)
|
||||
if showMessages
|
||||
@battle.pbShowAbilitySplash(self)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} doesn't become confused!",pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1}'s {2} prevents confusion!",pbThis,abilityName))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
if pbOwnSide.effects[PBEffects::Safeguard]>0 && !selfInflicted &&
|
||||
!(user && user.hasActiveAbility?(:INFILTRATOR))
|
||||
@battle.pbDisplay(_INTL("{1}'s team is protected by Safeguard!",pbThis)) if showMessages
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbCanConfuseSelf?(showMessages)
|
||||
return pbCanConfuse?(nil,showMessages,nil,true)
|
||||
end
|
||||
|
||||
def pbConfuse(msg=nil)
|
||||
@effects[PBEffects::Confusion] = pbConfusionDuration
|
||||
@battle.pbCommonAnimation("Confusion",self)
|
||||
msg = _INTL("{1} became confused!",pbThis) if !msg || msg==""
|
||||
@battle.pbDisplay(msg)
|
||||
PBDebug.log("[Lingering effect] #{pbThis}'s confusion count is #{@effects[PBEffects::Confusion]}")
|
||||
# Confusion cures
|
||||
pbItemStatusCureCheck
|
||||
pbAbilityStatusCureCheck
|
||||
end
|
||||
|
||||
def pbConfusionDuration(duration=-1)
|
||||
duration = 2+@battle.pbRandom(4) if duration<=0
|
||||
return duration
|
||||
end
|
||||
|
||||
def pbCureConfusion
|
||||
@effects[PBEffects::Confusion] = 0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Attraction
|
||||
#=============================================================================
|
||||
def pbCanAttract?(user,showMessages=true)
|
||||
return false if fainted?
|
||||
return false if !user || user.fainted?
|
||||
if @effects[PBEffects::Attract]>=0
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMessages
|
||||
return false
|
||||
end
|
||||
agender = user.gender
|
||||
ogender = gender
|
||||
if agender==2 || ogender==2 || agender==ogender
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis)) if showMessages
|
||||
return false
|
||||
end
|
||||
if !@battle.moldBreaker
|
||||
if hasActiveAbility?([:AROMAVEIL,:OBLIVIOUS])
|
||||
if showMessages
|
||||
@battle.pbShowAbilitySplash(self)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1}'s {2} prevents romance!",pbThis,abilityName))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
end
|
||||
return false
|
||||
else
|
||||
eachAlly do |b|
|
||||
next if !b.hasActiveAbility?(:AROMAVEIL)
|
||||
if showMessages
|
||||
@battle.pbShowAbilitySplash(self)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1}'s {2} prevents romance!",b.pbThis,b.abilityName))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(self)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbAttract(user,msg=nil)
|
||||
@effects[PBEffects::Attract] = user.index
|
||||
@battle.pbCommonAnimation("Attract",self)
|
||||
msg = _INTL("{1} fell in love!",pbThis) if !msg || msg==""
|
||||
@battle.pbDisplay(msg)
|
||||
# Destiny Knot
|
||||
if hasActiveItem?(:DESTINYKNOT) && user.pbCanAttract?(self,false)
|
||||
user.pbAttract(self,_INTL("{1} fell in love from the {2}!",user.pbThis(true),itemName))
|
||||
end
|
||||
# Attraction cures
|
||||
pbItemStatusCureCheck
|
||||
pbAbilityStatusCureCheck
|
||||
end
|
||||
|
||||
def pbCureAttract
|
||||
@effects[PBEffects::Attract] = -1
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Flinching
|
||||
#=============================================================================
|
||||
def pbFlinch(user=nil)
|
||||
return if hasActiveAbility?(:INNERFOCUS) && !@battle.moldBreaker
|
||||
@effects[PBEffects::Flinch] = true
|
||||
end
|
||||
end
|
||||
310
Data/Scripts/011_Battle/001_Battler/005_Battler_StatStages.rb
Normal file
310
Data/Scripts/011_Battle/001_Battler/005_Battler_StatStages.rb
Normal file
@@ -0,0 +1,310 @@
|
||||
class PokeBattle_Battler
|
||||
#=============================================================================
|
||||
# Increase stat stages
|
||||
#=============================================================================
|
||||
def statStageAtMax?(stat)
|
||||
return @stages[stat]>=6
|
||||
end
|
||||
|
||||
def pbCanRaiseStatStage?(stat,user=nil,move=nil,showFailMsg=false,ignoreContrary=false)
|
||||
return false if fainted?
|
||||
# Contrary
|
||||
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
|
||||
return pbCanLowerStatStage?(stat,user,move,showFailMsg,true)
|
||||
end
|
||||
# Check the stat stage
|
||||
if statStageAtMax?(stat)
|
||||
@battle.pbDisplay(_INTL("{1}'s {2} won't go any higher!",
|
||||
pbThis,PBStats.getName(stat))) if showFailMsg
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbRaiseStatStageBasic(stat,increment,ignoreContrary=false)
|
||||
if !@battle.moldBreaker
|
||||
# Contrary
|
||||
if hasActiveAbility?(:CONTRARY) && !ignoreContrary
|
||||
return pbLowerStatStageBasic(stat,increment,true)
|
||||
end
|
||||
# Simple
|
||||
increment *= 2 if hasActiveAbility?(:SIMPLE)
|
||||
end
|
||||
# Change the stat stage
|
||||
increment = [increment,6-@stages[stat]].min
|
||||
if increment>0
|
||||
s = PBStats.getName(stat); new = @stages[stat]+increment
|
||||
PBDebug.log("[Stat change] #{pbThis}'s #{s}: #{@stages[stat]} -> #{new} (+#{increment})")
|
||||
@stages[stat] += increment
|
||||
end
|
||||
return increment
|
||||
end
|
||||
|
||||
def pbRaiseStatStage(stat,increment,user,showAnim=true,ignoreContrary=false)
|
||||
return false if !PBStats.validBattleStat?(stat)
|
||||
# Contrary
|
||||
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
|
||||
return pbLowerStatStage(stat,increment,user,showAnim,true)
|
||||
end
|
||||
# Perform the stat stage change
|
||||
increment = pbRaiseStatStageBasic(stat,increment,ignoreContrary)
|
||||
return false if increment<=0
|
||||
# Stat up animation and message
|
||||
@battle.pbCommonAnimation("StatUp",self) if showAnim
|
||||
arrStatTexts = [
|
||||
_INTL("{1}'s {2} rose!",pbThis,PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} rose sharply!",pbThis,PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} rose drastically!",pbThis,PBStats.getName(stat))]
|
||||
@battle.pbDisplay(arrStatTexts[[increment-1,2].min])
|
||||
# Trigger abilities upon stat gain
|
||||
if abilityActive?
|
||||
BattleHandlers.triggerAbilityOnStatGain(@ability,self,stat,user)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbRaiseStatStageByCause(stat,increment,user,cause,showAnim=true,ignoreContrary=false)
|
||||
return false if !PBStats.validBattleStat?(stat)
|
||||
# Contrary
|
||||
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
|
||||
return pbLowerStatStageByCause(stat,increment,user,cause,showAnim,true)
|
||||
end
|
||||
# Perform the stat stage change
|
||||
increment = pbRaiseStatStageBasic(stat,increment,ignoreContrary)
|
||||
return false if increment<=0
|
||||
# Stat up animation and message
|
||||
@battle.pbCommonAnimation("StatUp",self) if showAnim
|
||||
if user.index==@index
|
||||
arrStatTexts = [
|
||||
_INTL("{1}'s {2} raised its {3}!",pbThis,cause,PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} sharply raised its {3}!",pbThis,cause,PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} drastically raised its {3}!",pbThis,cause,PBStats.getName(stat))]
|
||||
else
|
||||
arrStatTexts = [
|
||||
_INTL("{1}'s {2} raised {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} sharply raised {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} drastically raised {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat))]
|
||||
end
|
||||
@battle.pbDisplay(arrStatTexts[[increment-1,2].min])
|
||||
# Trigger abilities upon stat gain
|
||||
if abilityActive?
|
||||
BattleHandlers.triggerAbilityOnStatGain(@ability,self,stat,user)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbRaiseStatStageByAbility(stat,increment,user,splashAnim=true)
|
||||
return false if fainted?
|
||||
ret = false
|
||||
@battle.pbShowAbilitySplash(user) if splashAnim
|
||||
if pbCanRaiseStatStage?(stat,user,nil,PokeBattle_SceneConstants::USE_ABILITY_SPLASH)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
ret = pbRaiseStatStage(stat,increment,user)
|
||||
else
|
||||
ret = pbRaiseStatStageByCause(stat,increment,user,user.abilityName)
|
||||
end
|
||||
end
|
||||
@battle.pbHideAbilitySplash(user) if splashAnim
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Decrease stat stages
|
||||
#=============================================================================
|
||||
def statStageAtMin?(stat)
|
||||
return @stages[stat]<=-6
|
||||
end
|
||||
|
||||
def pbCanLowerStatStage?(stat,user=nil,move=nil,showFailMsg=false,ignoreContrary=false)
|
||||
return false if fainted?
|
||||
# Contrary
|
||||
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
|
||||
return pbCanRaiseStatStage?(stat,user,move,showFailMsg,true)
|
||||
end
|
||||
if !user || user.index!=@index # Not self-inflicted
|
||||
if @effects[PBEffects::Substitute]>0 && !(move && move.ignoresSubstitute?(user))
|
||||
@battle.pbDisplay(_INTL("{1} is protected by its substitute!",pbThis)) if showFailMsg
|
||||
return false
|
||||
end
|
||||
if pbOwnSide.effects[PBEffects::Mist]>0 &&
|
||||
!(user && user.hasActiveAbility?(:INFILTRATOR))
|
||||
@battle.pbDisplay(_INTL("{1} is protected by Mist!",pbThis)) if showFailMsg
|
||||
return false
|
||||
end
|
||||
if abilityActive?
|
||||
return false if BattleHandlers.triggerStatLossImmunityAbility(
|
||||
@ability,self,stat,@battle,showFailMsg) if !@battle.moldBreaker
|
||||
return false if BattleHandlers.triggerStatLossImmunityAbilityNonIgnorable(
|
||||
@ability,self,stat,@battle,showFailMsg)
|
||||
end
|
||||
if !@battle.moldBreaker
|
||||
eachAlly do |b|
|
||||
next if !b.abilityActive?
|
||||
return false if BattleHandlers.triggerStatLossImmunityAllyAbility(
|
||||
b.ability,b,self,stat,@battle,showFailMsg)
|
||||
end
|
||||
end
|
||||
end
|
||||
# Check the stat stage
|
||||
if statStageAtMin?(stat)
|
||||
@battle.pbDisplay(_INTL("{1}'s {2} won't go any lower!",
|
||||
pbThis,PBStats.getName(stat))) if showFailMsg
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbLowerStatStageBasic(stat,increment,ignoreContrary=false)
|
||||
if !@battle.moldBreaker
|
||||
# Contrary
|
||||
if hasActiveAbility?(:CONTRARY) && !ignoreContrary
|
||||
return pbRaiseStatStageBasic(stat,increment,true)
|
||||
end
|
||||
# Simple
|
||||
increment *= 2 if hasActiveAbility?(:SIMPLE)
|
||||
end
|
||||
# Change the stat stage
|
||||
increment = [increment,6+@stages[stat]].min
|
||||
if increment>0
|
||||
s = PBStats.getName(stat); new = @stages[stat]-increment
|
||||
PBDebug.log("[Stat change] #{pbThis}'s #{s}: #{@stages[stat]} -> #{new} (-#{increment})")
|
||||
@stages[stat] -= increment
|
||||
end
|
||||
return increment
|
||||
end
|
||||
|
||||
def pbLowerStatStage(stat,increment,user,showAnim=true,ignoreContrary=false)
|
||||
return false if !PBStats.validBattleStat?(stat)
|
||||
# Contrary
|
||||
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
|
||||
return pbRaiseStatStage(stat,increment,user,showAnim,true)
|
||||
end
|
||||
# Perform the stat stage change
|
||||
increment = pbLowerStatStageBasic(stat,increment,ignoreContrary)
|
||||
return false if increment<=0
|
||||
# Stat down animation and message
|
||||
@battle.pbCommonAnimation("StatDown",self) if showAnim
|
||||
arrStatTexts = [
|
||||
_INTL("{1}'s {2} fell!",pbThis,PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} harshly fell!",pbThis,PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} severely fell!",pbThis,PBStats.getName(stat))]
|
||||
@battle.pbDisplay(arrStatTexts[[increment-1,2].min])
|
||||
# Trigger abilities upon stat loss
|
||||
if abilityActive?
|
||||
BattleHandlers.triggerAbilityOnStatLoss(@ability,self,stat,user)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbLowerStatStageByCause(stat,increment,user,cause,showAnim=true,ignoreContrary=false)
|
||||
return false if !PBStats.validBattleStat?(stat)
|
||||
# Contrary
|
||||
if hasActiveAbility?(:CONTRARY) && !ignoreContrary && !@battle.moldBreaker
|
||||
return pbRaiseStatStageByCause(stat,increment,user,cause,showAnim,true)
|
||||
end
|
||||
# Perform the stat stage change
|
||||
increment = pbLowerStatStageBasic(stat,increment,ignoreContrary)
|
||||
return false if increment<=0
|
||||
# Stat down animation and message
|
||||
@battle.pbCommonAnimation("StatDown",self) if showAnim
|
||||
if user.index==@index
|
||||
arrStatTexts = [
|
||||
_INTL("{1}'s {2} lowered its {3}!",pbThis,cause,PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} harshly lowered its {3}!",pbThis,cause,PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} severely lowered its {3}!",pbThis,cause,PBStats.getName(stat))]
|
||||
else
|
||||
arrStatTexts = [
|
||||
_INTL("{1}'s {2} lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} harshly lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat)),
|
||||
_INTL("{1}'s {2} severely lowered {3}'s {4}!",user.pbThis,cause,pbThis(true),PBStats.getName(stat))]
|
||||
end
|
||||
@battle.pbDisplay(arrStatTexts[[increment-1,2].min])
|
||||
# Trigger abilities upon stat loss
|
||||
if abilityActive?
|
||||
BattleHandlers.triggerAbilityOnStatLoss(@ability,self,stat,user)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def pbLowerStatStageByAbility(stat,increment,user,splashAnim=true,checkContact=false)
|
||||
ret = false
|
||||
@battle.pbShowAbilitySplash(user) if splashAnim
|
||||
if pbCanLowerStatStage?(stat,user,nil,PokeBattle_SceneConstants::USE_ABILITY_SPLASH) &&
|
||||
(!checkContact || affectedByContactEffect?(PokeBattle_SceneConstants::USE_ABILITY_SPLASH))
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
ret = pbLowerStatStage(stat,increment,user)
|
||||
else
|
||||
ret = pbLowerStatStageByCause(stat,increment,user,user.abilityName)
|
||||
end
|
||||
end
|
||||
@battle.pbHideAbilitySplash(user) if splashAnim
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbLowerAttackStatStageIntimidate(user)
|
||||
return false if fainted?
|
||||
# NOTE: Substitute intentially blocks Intimidate even if self has Contrary.
|
||||
if @effects[PBEffects::Substitute]>0
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} is protected by its substitute!",pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1}'s substitute protected it from {2}'s {3}!",
|
||||
pbThis,user.pbThis(true),user.abilityName))
|
||||
end
|
||||
return false
|
||||
end
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
return pbLowerStatStageByAbility(PBStats::ATTACK,1,user,false)
|
||||
end
|
||||
# NOTE: These checks exist to ensure appropriate messages are shown if
|
||||
# Intimidate is blocked somehow (i.e. the messages should mention the
|
||||
# Intimidate ability by name).
|
||||
if !hasActiveAbility?(:CONTRARY)
|
||||
if pbOwnSide.effects[PBEffects::Mist]>0
|
||||
@battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by Mist!",
|
||||
pbThis,user.pbThis(true),user.abilityName))
|
||||
return false
|
||||
end
|
||||
if abilityActive?
|
||||
if BattleHandlers.triggerStatLossImmunityAbility(@ability,self,PBStats::ATTACK,@battle,false) ||
|
||||
BattleHandlers.triggerStatLossImmunityAbilityNonIgnorable(@ability,self,PBStats::ATTACK,@battle,false)
|
||||
@battle.pbDisplay(_INTL("{1}'s {2} prevented {3}'s {4} from working!",
|
||||
pbThis,abilityName,user.pbThis(true),user.abilityName))
|
||||
return false
|
||||
end
|
||||
end
|
||||
eachAlly do |b|
|
||||
next if !b.abilityActive?
|
||||
if BattleHandlers.triggerStatLossImmunityAllyAbility(b.ability,b,self,PBStats::ATTACK,@battle,false)
|
||||
@battle.pbDisplay(_INTL("{1} is protected from {2}'s {3} by {4}'s {5}!",
|
||||
pbThis,user.pbThis(true),user.abilityName,b.pbThis(true),b.abilityName))
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return false if !pbCanLowerStatStage?(PBStats::ATTACK,user)
|
||||
return pbLowerStatStageByCause(PBStats::ATTACK,1,user,user.abilityName)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Reset stat stages
|
||||
#=============================================================================
|
||||
def hasAlteredStatStages?
|
||||
PBStats.eachBattleStat { |s| return true if @stages[s]!=0 }
|
||||
return false
|
||||
end
|
||||
|
||||
def hasRaisedStatStages?
|
||||
PBStats.eachBattleStat { |s| return true if @stages[s]>0 }
|
||||
return false
|
||||
end
|
||||
|
||||
def hasLoweredStatStages?
|
||||
PBStats.eachBattleStat { |s| return true if @stages[s]<0 }
|
||||
return false
|
||||
end
|
||||
|
||||
def pbResetStatStages
|
||||
PBStats.eachBattleStat { |s| @stages[s] = 0 }
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
729
Data/Scripts/011_Battle/001_Battler/007_Battler_UseMove.rb
Normal file
729
Data/Scripts/011_Battle/001_Battler/007_Battler_UseMove.rb
Normal file
@@ -0,0 +1,729 @@
|
||||
class PokeBattle_Battler
|
||||
#=============================================================================
|
||||
# Turn processing
|
||||
#=============================================================================
|
||||
def pbProcessTurn(choice,tryFlee=true)
|
||||
return false if fainted?
|
||||
# Wild roaming Pokémon always flee if possible
|
||||
if tryFlee && @battle.wildBattle? && opposes? &&
|
||||
@battle.rules["alwaysflee"] && @battle.pbCanRun?(@index)
|
||||
pbBeginTurn(choice)
|
||||
@battle.pbDisplay(_INTL("{1} fled from battle!",pbThis)) { pbSEPlay("Battle flee") }
|
||||
@battle.decision = 3
|
||||
pbEndTurn(choice)
|
||||
return true
|
||||
end
|
||||
# Shift with the battler next to this one
|
||||
if choice[0]==:Shift
|
||||
idxOther = -1
|
||||
case @battle.pbSideSize(@index)
|
||||
when 2
|
||||
idxOther = (@index+2)%4
|
||||
when 3
|
||||
if @index!=2 && @index!=3 # If not in middle spot already
|
||||
idxOther = ((@index%2)==0) ? 2 : 3
|
||||
end
|
||||
end
|
||||
if idxOther>=0
|
||||
@battle.pbSwapBattlers(@index,idxOther)
|
||||
case @battle.pbSideSize(@index)
|
||||
when 2
|
||||
@battle.pbDisplay(_INTL("{1} moved across!",pbThis))
|
||||
when 3
|
||||
@battle.pbDisplay(_INTL("{1} moved to the center!",pbThis))
|
||||
end
|
||||
end
|
||||
pbBeginTurn(choice)
|
||||
pbCancelMoves
|
||||
@lastRoundMoved = @battle.turnCount # Done something this round
|
||||
return true
|
||||
end
|
||||
# If this battler's action for this round wasn't "use a move"
|
||||
if choice[0]!=:UseMove
|
||||
# Clean up effects that end at battler's turn
|
||||
pbBeginTurn(choice)
|
||||
pbEndTurn(choice)
|
||||
return false
|
||||
end
|
||||
# Turn is skipped if Pursuit was used during switch
|
||||
if @effects[PBEffects::Pursuit]
|
||||
@effects[PBEffects::Pursuit] = false
|
||||
pbCancelMoves
|
||||
pbEndTurn(choice)
|
||||
@battle.pbJudge
|
||||
return false
|
||||
end
|
||||
# Use the move
|
||||
PBDebug.log("[Move usage] #{pbThis} started using #{choice[2].name}")
|
||||
PBDebug.logonerr{
|
||||
pbUseMove(choice,choice[2]==@battle.struggle)
|
||||
}
|
||||
@battle.pbJudge
|
||||
# Update priority order
|
||||
# @battle.pbCalculatePriority if NEWEST_BATTLE_MECHANICS
|
||||
return true
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
#
|
||||
#=============================================================================
|
||||
def pbBeginTurn(choice)
|
||||
# Cancel some lingering effects which only apply until the user next moves
|
||||
@effects[PBEffects::BeakBlast] = false
|
||||
@effects[PBEffects::DestinyBondPrevious] = @effects[PBEffects::DestinyBond]
|
||||
@effects[PBEffects::DestinyBond] = false
|
||||
@effects[PBEffects::Grudge] = false
|
||||
@effects[PBEffects::MoveNext] = false
|
||||
@effects[PBEffects::Quash] = 0
|
||||
@effects[PBEffects::ShellTrap] = false
|
||||
# Encore's effect ends if the encored move is no longer available
|
||||
if @effects[PBEffects::Encore]>0 && pbEncoredMoveIndex<0
|
||||
@effects[PBEffects::Encore] = 0
|
||||
@effects[PBEffects::EncoreMove] = 0
|
||||
end
|
||||
end
|
||||
|
||||
# Called when the usage of various multi-turn moves is disrupted due to
|
||||
# failing pbTryUseMove, being ineffective against all targets, or because
|
||||
# Pursuit was used specially to intercept a switching foe.
|
||||
# Cancels the use of multi-turn moves and counters thereof. Note that Hyper
|
||||
# Beam's effect is NOT cancelled.
|
||||
def pbCancelMoves
|
||||
# Outragers get confused anyway if they are disrupted during their final
|
||||
# turn of using the move
|
||||
if @effects[PBEffects::Outrage]==1 && pbCanConfuseSelf?(false)
|
||||
pbConfuse(_INTL("{1} became confused due to fatigue!",pbThis))
|
||||
end
|
||||
# Cancel usage of most multi-turn moves
|
||||
@effects[PBEffects::TwoTurnAttack] = 0
|
||||
@effects[PBEffects::Rollout] = 0
|
||||
@effects[PBEffects::Outrage] = 0
|
||||
@effects[PBEffects::Uproar] = 0
|
||||
@effects[PBEffects::Bide] = 0
|
||||
@currentMove = 0
|
||||
# Reset counters for moves which increase them when used in succession
|
||||
@effects[PBEffects::FuryCutter] = 0
|
||||
end
|
||||
|
||||
def pbEndTurn(choice)
|
||||
@lastRoundMoved = @battle.turnCount # Done something this round
|
||||
if @effects[PBEffects::ChoiceBand]<0 &&
|
||||
hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF])
|
||||
if @lastMoveUsed>=0 && pbHasMove?(@lastMoveUsed)
|
||||
@effects[PBEffects::ChoiceBand] = @lastMoveUsed
|
||||
elsif @lastRegularMoveUsed>=0 && pbHasMove?(@lastRegularMoveUsed)
|
||||
@effects[PBEffects::ChoiceBand] = @lastRegularMoveUsed
|
||||
end
|
||||
end
|
||||
@effects[PBEffects::Charge] = 0 if @effects[PBEffects::Charge]==1
|
||||
@effects[PBEffects::GemConsumed] = 0
|
||||
@battle.eachBattler { |b| b.pbContinualAbilityChecks } # Trace, end primordial weathers
|
||||
end
|
||||
|
||||
def pbConfusionDamage(msg)
|
||||
@damageState.reset
|
||||
@damageState.initialHP = @hp
|
||||
confusionMove = PokeBattle_Confusion.new(@battle,nil)
|
||||
confusionMove.calcType = confusionMove.pbCalcType(self) # -1
|
||||
@damageState.typeMod = confusionMove.pbCalcTypeMod(confusionMove.calcType,self,self) # 8
|
||||
confusionMove.pbCheckDamageAbsorption(self,self)
|
||||
confusionMove.pbCalcDamage(self,self)
|
||||
confusionMove.pbReduceDamage(self,self)
|
||||
self.hp -= @damageState.hpLost
|
||||
confusionMove.pbAnimateHitAndHPLost(self,[self])
|
||||
@battle.pbDisplay(msg) # "It hurt itself in its confusion!"
|
||||
confusionMove.pbRecordDamageLost(self,self)
|
||||
confusionMove.pbEndureKOMessage(self)
|
||||
pbFaint if fainted?
|
||||
pbItemHPHealCheck
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Simple "use move" method, used when a move calls another move and for Future
|
||||
# Sight's attack
|
||||
#=============================================================================
|
||||
def pbUseMoveSimple(moveID,target=-1,idxMove=-1,specialUsage=true)
|
||||
choice = []
|
||||
choice[0] = :UseMove # "Use move"
|
||||
choice[1] = idxMove # Index of move to be used in user's moveset
|
||||
if idxMove>=0
|
||||
choice[2] = @moves[idxMove]
|
||||
else
|
||||
choice[2] = PokeBattle_Move.pbFromPBMove(@battle,PBMove.new(moveID)) # PokeBattle_Move object
|
||||
choice[2].pp = -1
|
||||
end
|
||||
choice[3] = target # Target (-1 means no target yet)
|
||||
PBDebug.log("[Move usage] #{pbThis} started using the called/simple move #{choice[2].name}")
|
||||
pbUseMove(choice,specialUsage)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Master "use move" method
|
||||
#=============================================================================
|
||||
def pbUseMove(choice,specialUsage=false)
|
||||
# NOTE: This is intentionally determined before a multi-turn attack can
|
||||
# set specialUsage to true.
|
||||
skipAccuracyCheck = (specialUsage && choice[2]!=@battle.struggle)
|
||||
# Start using the move
|
||||
pbBeginTurn(choice)
|
||||
# Force the use of certain moves if they're already being used
|
||||
if usingMultiTurnAttack?
|
||||
choice[2] = PokeBattle_Move.pbFromPBMove(@battle,PBMove.new(@currentMove))
|
||||
specialUsage = true
|
||||
elsif @effects[PBEffects::Encore]>0 && choice[1]>=0 &&
|
||||
@battle.pbCanShowCommands?(@index)
|
||||
idxEncoredMove = pbEncoredMoveIndex
|
||||
if idxEncoredMove>=0 && @battle.pbCanChooseMove?(@index,idxEncoredMove,false)
|
||||
if choice[1]!=idxEncoredMove # Change move if battler was Encored mid-round
|
||||
choice[1] = idxEncoredMove
|
||||
choice[2] = @moves[idxEncoredMove]
|
||||
choice[3] = -1 # No target chosen
|
||||
end
|
||||
end
|
||||
end
|
||||
# Labels the move being used as "move"
|
||||
move = choice[2]
|
||||
return if !move || move.id==0 # if move was not chosen somehow
|
||||
# Try to use the move (inc. disobedience)
|
||||
@lastMoveFailed = false
|
||||
if !pbTryUseMove(choice,move,specialUsage,skipAccuracyCheck)
|
||||
@lastMoveUsed = -1
|
||||
@lastMoveUsedType = -1
|
||||
if !specialUsage
|
||||
@lastRegularMoveUsed = -1
|
||||
@lastRegularMoveTarget = -1
|
||||
end
|
||||
@battle.pbGainExp # In case self is KO'd due to confusion
|
||||
pbCancelMoves
|
||||
pbEndTurn(choice)
|
||||
return
|
||||
end
|
||||
move = choice[2] # In case disobedience changed the move to be used
|
||||
return if !move || move.id==0 # if move was not chosen somehow
|
||||
# Subtract PP
|
||||
if !specialUsage
|
||||
if !pbReducePP(move)
|
||||
@battle.pbDisplay(_INTL("{1} used {2}!",pbThis,move.name))
|
||||
@battle.pbDisplay(_INTL("But there was no PP left for the move!"))
|
||||
@lastMoveUsed = -1
|
||||
@lastMoveUsedType = -1
|
||||
@lastRegularMoveUsed = -1
|
||||
@lastRegularMoveTarget = -1
|
||||
@lastMoveFailed = true
|
||||
pbCancelMoves
|
||||
pbEndTurn(choice)
|
||||
return
|
||||
end
|
||||
end
|
||||
# Stance Change
|
||||
if isConst?(@species,PBSpecies,:AEGISLASH) && isConst?(@ability,PBAbilities,:STANCECHANGE)
|
||||
if move.damagingMove?
|
||||
pbChangeForm(1,_INTL("{1} changed to Blade Forme!",pbThis))
|
||||
elsif isConst?(move.id,PBMoves,:KINGSSHIELD)
|
||||
pbChangeForm(0,_INTL("{1} changed to Shield Forme!",pbThis))
|
||||
end
|
||||
end
|
||||
# Calculate the move's type during this usage
|
||||
move.calcType = move.pbCalcType(self)
|
||||
# Start effect of Mold Breaker
|
||||
@battle.moldBreaker = hasMoldBreaker?
|
||||
# Remember that user chose a two-turn move
|
||||
if move.pbIsChargingTurn?(self)
|
||||
# Beginning the use of a two-turn attack
|
||||
@effects[PBEffects::TwoTurnAttack] = move.id
|
||||
@currentMove = move.id
|
||||
else
|
||||
@effects[PBEffects::TwoTurnAttack] = 0 # Cancel use of two-turn attack
|
||||
end
|
||||
# Add to counters for moves which increase them when used in succession
|
||||
move.pbChangeUsageCounters(self,specialUsage)
|
||||
# Charge up Metronome item
|
||||
if hasActiveItem?(:METRONOME) && !move.callsAnotherMove?
|
||||
if @lastMoveUsed==move.id && !@lastMoveFailed
|
||||
@effects[PBEffects::Metronome] += 1
|
||||
else
|
||||
@effects[PBEffects::Metronome] = 0
|
||||
end
|
||||
end
|
||||
# Record move as having been used
|
||||
@lastMoveUsed = move.id
|
||||
@lastMoveUsedType = move.calcType # For Conversion 2
|
||||
if !specialUsage
|
||||
@lastRegularMoveUsed = move.id # For Disable, Encore, Instruct, Mimic, Mirror Move, Sketch, Spite
|
||||
@lastRegularMoveTarget = choice[3] # For Instruct (remembering original target is fine)
|
||||
@movesUsed.push(move.id) if !@movesUsed.include?(move.id) # For Last Resort
|
||||
end
|
||||
@battle.lastMoveUsed = move.id # For Copycat
|
||||
@battle.lastMoveUser = @index # For "self KO" battle clause to avoid draws
|
||||
@battle.successStates[@index].useState = 1 # Battle Arena - assume failure
|
||||
# Find the default user (self or Snatcher) and target(s)
|
||||
user = pbFindUser(choice,move)
|
||||
user = pbChangeUser(choice,move,user)
|
||||
targets = pbFindTargets(choice,move,user)
|
||||
targets = pbChangeTargets(move,user,targets)
|
||||
# Pressure
|
||||
if !specialUsage
|
||||
targets.each do |b|
|
||||
next unless b.opposes?(user) && b.hasActiveAbility?(:PRESSURE)
|
||||
PBDebug.log("[Ability triggered] #{b.pbThis}'s #{b.abilityName}")
|
||||
user.pbReducePP(move)
|
||||
end
|
||||
if PBTargets.targetsFoeSide?(move.pbTarget(user))
|
||||
@battle.eachOtherSideBattler(user) do |b|
|
||||
next unless b.hasActiveAbility?(:PRESSURE)
|
||||
PBDebug.log("[Ability triggered] #{b.pbThis}'s #{b.abilityName}")
|
||||
user.pbReducePP(move)
|
||||
end
|
||||
end
|
||||
end
|
||||
# Dazzling/Queenly Majesty make the move fail here
|
||||
@battle.pbPriority(true).each do |b|
|
||||
next if !b || !b.abilityActive?
|
||||
if BattleHandlers.triggerMoveBlockingAbility(b.ability,b,user,targets,move,@battle)
|
||||
@battle.pbDisplayBrief(_INTL("{1} used {2}!",user.pbThis,move.name))
|
||||
@battle.pbShowAbilitySplash(b)
|
||||
@battle.pbDisplay(_INTL("{1} cannot use {2}!",user.pbThis,move.name))
|
||||
@battle.pbHideAbilitySplash(b)
|
||||
user.lastMoveFailed = true
|
||||
pbCancelMoves
|
||||
pbEndTurn(choice)
|
||||
return
|
||||
end
|
||||
end
|
||||
# "X used Y!" message
|
||||
# Can be different for Bide, Fling, Focus Punch and Future Sight
|
||||
# NOTE: This intentionally passes self rather than user. The user is always
|
||||
# self except if Snatched, but this message should state the original
|
||||
# user (self) even if the move is Snatched.
|
||||
move.pbDisplayUseMessage(self)
|
||||
# Snatch's message (user is the new user, self is the original user)
|
||||
if move.snatched
|
||||
@lastMoveFailed = true # Intentionally applies to self, not user
|
||||
@battle.pbDisplay(_INTL("{1} snatched {2}'s move!",user.pbThis,pbThis(true)))
|
||||
end
|
||||
# "But it failed!" checks
|
||||
if move.pbMoveFailed?(user,targets)
|
||||
PBDebug.log(sprintf("[Move failed] In function code %s's def pbMoveFailed?",move.function))
|
||||
user.lastMoveFailed = true
|
||||
pbCancelMoves
|
||||
pbEndTurn(choice)
|
||||
return
|
||||
end
|
||||
# Perform set-up actions and display messages
|
||||
# Messages include Magnitude's number and Pledge moves' "it's a combo!"
|
||||
move.pbOnStartUse(user,targets)
|
||||
# Self-thawing due to the move
|
||||
if user.status==PBStatuses::FROZEN && move.thawsUser?
|
||||
user.pbCureStatus(false)
|
||||
@battle.pbDisplay(_INTL("{1} melted the ice!",user.pbThis))
|
||||
end
|
||||
# Powder
|
||||
if user.effects[PBEffects::Powder] && isConst?(move.calcType,PBTypes,:FIRE)
|
||||
@battle.pbCommonAnimation("Powder",user)
|
||||
@battle.pbDisplay(_INTL("When the flame touched the powder on the Pokémon, it exploded!"))
|
||||
user.lastMoveFailed = true
|
||||
w = @battle.pbWeather
|
||||
if w!=PBWeather.RAINDANCE && w!=PBWeather.HEAVYRAIN && user.takesIndirectDamage?
|
||||
oldHP = user.hp
|
||||
user.pbReduceHP((user.totalhp/4.0).round,false)
|
||||
user.pbFaint if user.fainted?
|
||||
@battle.pbGainExp # In case user is KO'd by this
|
||||
user.pbItemHPHealCheck
|
||||
if user.pbAbilitiesOnDamageTaken(oldHP)
|
||||
user.pbEffectsOnSwitchIn(true)
|
||||
end
|
||||
end
|
||||
pbCancelMoves
|
||||
pbEndTurn(choice)
|
||||
return
|
||||
end
|
||||
# Primordial Sea, Desolate Land
|
||||
if move.damagingMove?
|
||||
case @battle.pbWeather
|
||||
when PBWeather::HeavyRain
|
||||
if isConst?(move.calcType,PBTypes,:FIRE)
|
||||
@battle.pbDisplay(_INTL("The Fire-type attack fizzled out in the heavy rain!"))
|
||||
user.lastMoveFailed = true
|
||||
pbCancelMoves
|
||||
pbEndTurn(choice)
|
||||
return
|
||||
end
|
||||
when PBWeather::HarshSun
|
||||
if isConst?(move.calcType,PBTypes,:WATER)
|
||||
@battle.pbDisplay(_INTL("The Water-type attack evaporated in the harsh sunlight!"))
|
||||
user.lastMoveFailed = true
|
||||
pbCancelMoves
|
||||
pbEndTurn(choice)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
# Protean
|
||||
if user.hasActiveAbility?(:PROTEAN) && !move.callsAnotherMove? && !move.snatched
|
||||
if user.pbHasOtherType?(moveType) && !PBTypes.isPseudoType?(move.calcType)
|
||||
@battle.pbShowAbilitySplash(user)
|
||||
user.pbChangeTypes(move.calcType)
|
||||
typeName = PBTypes.getName(move.calcType)
|
||||
@battle.pbDisplay(_INTL("{1} transformed into the {2} type!",user.pbThis,typeName))
|
||||
@battle.pbHideAbilitySplash(user)
|
||||
# NOTE: The GF games say that if Curse is used by a non-Ghost-type
|
||||
# Pokémon which becomes Ghost-type because of Protean, it should
|
||||
# target and curse itself. I think this is silly, so I'm making it
|
||||
# choose a random opponent to curse instead.
|
||||
if move.function=="10D" && targets.length==0 # Curse
|
||||
choice[3] = -1
|
||||
targets = pbFindTargets(choice,move,user)
|
||||
end
|
||||
end
|
||||
end
|
||||
#---------------------------------------------------------------------------
|
||||
magicCoater = -1
|
||||
magicBouncer = -1
|
||||
if targets.length==0 && !PBTargets.noTargets?(move.pbTarget(user)) &&
|
||||
!move.worksWithNoTargets?
|
||||
# def pbFindTargets should have found a target(s), but it didn't because
|
||||
# they were all fainted
|
||||
# All target types except: None, User, UserSide, FoeSide, BothSides
|
||||
@battle.pbDisplay(_INTL("But there was no target..."))
|
||||
user.lastMoveFailed = true
|
||||
else # We have targets, or move doesn't use targets
|
||||
# Reset whole damage state, perform various success checks (not accuracy)
|
||||
user.initialHP = user.hp
|
||||
targets.each do |b|
|
||||
b.damageState.reset
|
||||
b.damageState.initialHP = b.hp
|
||||
if !pbSuccessCheckAgainstTarget(move,user,b)
|
||||
b.damageState.unaffected = true
|
||||
end
|
||||
end
|
||||
# Magic Coat/Magic Bounce checks (for moves which don't target Pokémon)
|
||||
if targets.length==0 && move.canMagicCoat?
|
||||
@battle.pbPriority(true).each do |b|
|
||||
next if b.fainted? || !b.opposes?(user)
|
||||
next if b.semiInvulnerable?
|
||||
if b.effects[PBEffects::MagicCoat]
|
||||
magicCoater = b.index
|
||||
b.effects[PBEffects::MagicCoat] = false
|
||||
break
|
||||
elsif b.hasActiveAbility?(:MAGICBOUNCE) && !@battle.moldBreaker &&
|
||||
!b.effects[PBEffects::MagicBounce]
|
||||
magicBouncer = b.index
|
||||
b.effects[PBEffects::MagicBounce] = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
# Get the number of hits
|
||||
numHits = move.pbNumHits(user,targets)
|
||||
# Process each hit in turn
|
||||
realNumHits = 0
|
||||
for i in 0...numHits
|
||||
break if magicCoater>=0 || magicBouncer>=0
|
||||
success = pbProcessMoveHit(move,user,targets,i,skipAccuracyCheck)
|
||||
if !success
|
||||
if i==0 && targets.length>0
|
||||
hasFailed = false
|
||||
targets.each do |t|
|
||||
next if t.damageState.protected
|
||||
hasFailed = t.damageState.unaffected
|
||||
break if !t.damageState.unaffected
|
||||
end
|
||||
user.lastMoveFailed = hasFailed
|
||||
end
|
||||
break
|
||||
end
|
||||
realNumHits += 1
|
||||
break if user.fainted?
|
||||
break if user.status==PBStatuses::SLEEP || user.status==PBStatuses::FROZEN
|
||||
# NOTE: If a multi-hit move becomes disabled partway through doing those
|
||||
# hits (e.g. by Cursed Body), the rest of the hits continue as
|
||||
# normal.
|
||||
notFainted = false
|
||||
break if !targets.any? { |t| !t.fainted? } # All targets are fainted
|
||||
end
|
||||
# Battle Arena only - attack is successful
|
||||
@battle.successStates[user.index].useState = 2
|
||||
if targets.length>0
|
||||
@battle.successStates[user.index].typeMod = 0
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
@battle.successStates[user.index].typeMod += b.damageState.typeMod
|
||||
end
|
||||
end
|
||||
# Effectiveness message for multi-hit moves
|
||||
# NOTE: No move is both multi-hit and multi-target, and the messages below
|
||||
# aren't quite right for such a hypothetical move.
|
||||
if numHits>1
|
||||
if move.damagingMove?
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected || b.damageState.substitute
|
||||
move.pbEffectivenessMessage(user,b,targets.length)
|
||||
end
|
||||
end
|
||||
if realNumHits==1
|
||||
@battle.pbDisplay(_INTL("Hit 1 time!"))
|
||||
elsif realNumHits>1
|
||||
@battle.pbDisplay(_INTL("Hit {1} times!",realNumHits))
|
||||
end
|
||||
end
|
||||
# Magic Coat's bouncing back (move has targets)
|
||||
targets.each do |b|
|
||||
next if b.fainted?
|
||||
next if !b.damageState.magicCoat && !b.damageState.magicBounce
|
||||
@battle.pbShowAbilitySplash(b) if b.damageState.magicBounce
|
||||
@battle.pbDisplay(_INTL("{1} bounced the {2} back!",b.pbThis,move.name))
|
||||
@battle.pbHideAbilitySplash(b) if b.damageState.magicBounce
|
||||
newChoice = choice.clone
|
||||
newChoice[3] = user.index
|
||||
newTargets = pbFindTargets(newChoice,move,b)
|
||||
newTargets = pbChangeTargets(move,b,newTargets)
|
||||
success = pbProcessMoveHit(move,b,newTargets,0,false)
|
||||
b.lastMoveFailed = true if !success
|
||||
targets.each { |b| b.pbFaint if b && b.fainted? }
|
||||
user.pbFaint if user.fainted?
|
||||
end
|
||||
# Magic Coat's bouncing back (move has no targets)
|
||||
if magicCoater>=0 || magicBouncer>=0
|
||||
mc = @battle.battlers[(magicCoater>=0) ? magicCoater : magicBouncer]
|
||||
if !mc.fainted?
|
||||
user.lastMoveFailed = true
|
||||
@battle.pbShowAbilitySplash(mc) if magicBouncer>=0
|
||||
@battle.pbDisplay(_INTL("{1} bounced the {2} back!",mc.pbThis,move.name))
|
||||
@battle.pbHideAbilitySplash(mc) if magicBouncer>=0
|
||||
success = pbProcessMoveHit(move,mc,[],0,false)
|
||||
mc.lastMoveFailed = true if !success
|
||||
targets.each { |b| b.pbFaint if b && b.fainted? }
|
||||
user.pbFaint if user.fainted?
|
||||
end
|
||||
end
|
||||
# Move-specific effects after all hits
|
||||
targets.each { |b| move.pbEffectAfterAllHits(user,b) }
|
||||
# Faint if 0 HP
|
||||
targets.each { |b| b.pbFaint if b && b.fainted? }
|
||||
user.pbFaint if user.fainted?
|
||||
# External/general effects after all hits. Eject Button, Shell Bell, etc.
|
||||
pbEffectsAfterMove(user,targets,move,realNumHits)
|
||||
end
|
||||
# End effect of Mold Breaker
|
||||
@battle.moldBreaker = false
|
||||
# Gain Exp
|
||||
@battle.pbGainExp
|
||||
# Battle Arena only - update skills
|
||||
@battle.eachBattler { |b| @battle.successStates[b.index].updateSkill }
|
||||
# Shadow Pokémon triggering Hyper Mode
|
||||
pbHyperMode if @battle.choices[@index][0]!=:None # Not if self is replaced
|
||||
# End of move usage
|
||||
pbEndTurn(choice)
|
||||
# Instruct
|
||||
@battle.eachBattler do |b|
|
||||
next if !b.effects[PBEffects::Instruct]
|
||||
b.effects[PBEffects::Instruct] = false
|
||||
idxMove = -1
|
||||
b.eachMoveWithIndex { |m,i| idxMove = i if m.id==b.lastMoveUsed }
|
||||
next if idxMove<0
|
||||
oldLastRoundMoved = b.lastRoundMoved
|
||||
@battle.pbDisplay(_INTL("{1} used the move instructed by {2}!",b.pbThis,user.pbThis(true)))
|
||||
PBDebug.logonerr{
|
||||
b.effects[PBEffects::Instructed] = true
|
||||
b.pbUseMoveSimple(b.lastMoveUsed,b.lastRegularMoveTarget,idxMove,false)
|
||||
b.effects[PBEffects::Instructed] = false
|
||||
}
|
||||
b.lastRoundMoved = oldLastRoundMoved
|
||||
@battle.pbJudge
|
||||
return if @battle.decision>0
|
||||
end
|
||||
# Dancer
|
||||
if !@effects[PBEffects::Dancer] && !user.lastMoveFailed && realNumHits>0 &&
|
||||
!move.snatched && magicCoater<0 && @battle.pbCheckGlobalAbility(:DANCER)
|
||||
dancers = []
|
||||
@battle.pbPriority(true).each do |b|
|
||||
dancers.push(b) if b.index!=user.index && b.hasActiveAbility?(:DANCER)
|
||||
end
|
||||
while dancers.length>0
|
||||
nextUser = dancers.pop
|
||||
oldLastRoundMoved = nextUser.lastRoundMoved
|
||||
# NOTE: Petal Dance being used because of Dancer shouldn't lock the
|
||||
# Dancer into using that move, and shouldn't contribute to its
|
||||
# turn counter if it's already locked into Petal Dance.
|
||||
oldOutrage = nextUser.effects[PBEffects::Outrage]
|
||||
nextUser.effects[PBEffects::Outrage] += 1 if nextUser.effects[PBEffects::Outrage]>0
|
||||
oldCurrentMove = nextUser.currentMove
|
||||
preTarget = choice[3]
|
||||
preTarget = user.index if nextUser.opposes?(user) || !nextUser.opposes?(preTarget)
|
||||
@battle.pbShowAbilitySplash(nextUser,true)
|
||||
@battle.pbHideAbilitySplash(nextUser)
|
||||
if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} kept the dance going with {2}!",
|
||||
nextUser.pbThis,nextUser.abilityName))
|
||||
end
|
||||
PBDebug.logonerr{
|
||||
nextUser.effects[PBEffects::Dancer] = true
|
||||
nextUser.pbUseMoveSimple(move.id,preTarget)
|
||||
nextUser.effects[PBEffects::Dancer] = false
|
||||
}
|
||||
nextUser.lastRoundMoved = oldLastRoundMoved
|
||||
nextUser.effects[PBEffects::Outrage] = oldOutrage
|
||||
nextUser.currentMove = oldCurrentMove
|
||||
@battle.pbJudge
|
||||
return if @battle.decision>0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Attack a single target
|
||||
#=============================================================================
|
||||
def pbProcessMoveHit(move,user,targets,hitNum,skipAccuracyCheck)
|
||||
return false if user.fainted?
|
||||
# For two-turn attacks being used in a single turn
|
||||
move.pbInitialEffect(user,targets,hitNum)
|
||||
numTargets = 0 # Number of targets that are affected by this hit
|
||||
targets.each { |b| b.damageState.resetPerHit }
|
||||
# Count a hit for Parental Bond (if it applies)
|
||||
user.effects[PBEffects::ParentalBond] -= 1 if user.effects[PBEffects::ParentalBond]>0
|
||||
# Accuracy check (accuracy/evasion calc)
|
||||
if hitNum==0 || move.successCheckPerHit?
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
if pbSuccessCheckPerHit(move,user,b,skipAccuracyCheck)
|
||||
numTargets += 1
|
||||
else
|
||||
b.damageState.missed = true
|
||||
b.damageState.unaffected = true
|
||||
end
|
||||
end
|
||||
# If failed against all targets
|
||||
if targets.length>0 && numTargets==0 && !move.worksWithNoTargets?
|
||||
targets.each do |b|
|
||||
next if !b.damageState.missed || b.damageState.magicCoat
|
||||
pbMissMessage(move,user,b)
|
||||
end
|
||||
move.pbCrashDamage(user)
|
||||
user.pbItemHPHealCheck
|
||||
pbCancelMoves
|
||||
return false
|
||||
end
|
||||
end
|
||||
# If we get here, this hit will happen and do something
|
||||
#---------------------------------------------------------------------------
|
||||
# Calculate damage to deal
|
||||
if move.pbDamagingMove?
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
# Check whether Substitute/Disguise will absorb the damage
|
||||
move.pbCheckDamageAbsorption(user,b)
|
||||
# Calculate the damage against b
|
||||
# pbCalcDamage shows the "eat berry" animation for SE-weakening
|
||||
# berries, although the message about it comes after the additional
|
||||
# effect below
|
||||
move.pbCalcDamage(user,b,targets.length) # Stored in damageState.calcDamage
|
||||
# Lessen damage dealt because of False Swipe/Endure/etc.
|
||||
move.pbReduceDamage(user,b) # Stored in damageState.hpLost
|
||||
end
|
||||
end
|
||||
# Show move animation (for this hit)
|
||||
move.pbShowAnimation(move.id,user,targets,hitNum)
|
||||
# Type-boosting Gem consume animation/message
|
||||
if user.effects[PBEffects::GemConsumed]>0 && hitNum==0
|
||||
# NOTE: The consume animation and message for Gems are shown now, but the
|
||||
# actual removal of the item happens in def pbEffectsAfterMove.
|
||||
@battle.pbCommonAnimation("UseItem",user)
|
||||
@battle.pbDisplay(_INTL("The {1} strengthened {2}'s power!",
|
||||
PBItems.getName(user.effects[PBEffects::GemConsumed]),move.name))
|
||||
end
|
||||
# Messages about missed target(s) (relevant for multi-target moves only)
|
||||
targets.each do |b|
|
||||
next if !b.damageState.missed
|
||||
pbMissMessage(move,user,b)
|
||||
end
|
||||
# Deal the damage (to all allies first simultaneously, then all foes
|
||||
# simultaneously)
|
||||
if move.pbDamagingMove?
|
||||
# This just changes the HP amounts and does nothing else
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
move.pbInflictHPDamage(b)
|
||||
end
|
||||
# Animate the hit flashing and HP bar changes
|
||||
move.pbAnimateHitAndHPLost(user,targets)
|
||||
end
|
||||
# Self-Destruct/Explosion's damaging and fainting of user
|
||||
move.pbSelfKO(user) if hitNum==0
|
||||
user.pbFaint if user.fainted?
|
||||
if move.pbDamagingMove?
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
# NOTE: This method is also used for the OKHO special message.
|
||||
move.pbHitEffectivenessMessages(user,b,targets.length)
|
||||
# Record data about the hit for various effects' purposes
|
||||
move.pbRecordDamageLost(user,b)
|
||||
end
|
||||
# Close Combat/Superpower's stat-lowering, Flame Burst's splash damage,
|
||||
# and Incinerate's berry destruction
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
move.pbEffectWhenDealingDamage(user,b)
|
||||
end
|
||||
# Ability/item effects such as Static/Rocky Helmet, and Grudge, etc.
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
pbEffectsOnMakingHit(move,user,b)
|
||||
end
|
||||
# Disguise/Endure/Sturdy/Focus Sash/Focus Band messages
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
move.pbEndureKOMessage(b)
|
||||
end
|
||||
# HP-healing held items (checks all battlers rather than just targets
|
||||
# because Flame Burst's splash damage affects non-targets)
|
||||
@battle.pbPriority(true).each { |b| b.pbItemHPHealCheck }
|
||||
# Animate battlers fainting (checks all battlers rather than just targets
|
||||
# because Flame Burst's splash damage affects non-targets)
|
||||
@battle.pbPriority(true).each { |b| b.pbFaint if b && b.fainted? }
|
||||
end
|
||||
@battle.pbJudgeCheckpoint(user,move)
|
||||
# Main effect (recoil/drain, etc.)
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
move.pbEffectAgainstTarget(user,b)
|
||||
end
|
||||
move.pbEffectGeneral(user)
|
||||
targets.each { |b| b.pbFaint if b && b.fainted? }
|
||||
user.pbFaint if user.fainted?
|
||||
# Additional effect
|
||||
if !user.hasActiveAbility?(:SHEERFORCE)
|
||||
targets.each do |b|
|
||||
next if b.damageState.calcDamage==0
|
||||
chance = move.pbAdditionalEffectChance(user,b)
|
||||
next if chance<=0
|
||||
if @battle.pbRandom(100)<chance
|
||||
move.pbAdditionalEffect(user,b)
|
||||
end
|
||||
end
|
||||
end
|
||||
# Make the target flinch (because of an item/ability)
|
||||
targets.each do |b|
|
||||
next if b.fainted?
|
||||
next if b.damageState.calcDamage==0 || b.damageState.substitute
|
||||
chance = move.pbFlinchChance(user,b)
|
||||
next if chance<=0
|
||||
if @battle.pbRandom(100)<chance
|
||||
PBDebug.log("[Item/ability triggered] #{user.pbThis}'s King's Rock/Razor Fang or Stench")
|
||||
b.pbFlinch(user)
|
||||
end
|
||||
end
|
||||
# Message for and consuming of type-weakening berries
|
||||
# NOTE: The "consume held item" animation for type-weakening berries occurs
|
||||
# during pbCalcDamage above (before the move's animation), but the
|
||||
# message about it only shows here.
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected
|
||||
next if !b.damageState.berryWeakened
|
||||
@battle.pbDisplay(_INTL("The {1} weakened the damage to {2}!",b.itemName,b.pbThis(true)))
|
||||
b.pbConsumeItem
|
||||
end
|
||||
targets.each { |b| b.pbFaint if b && b.fainted? }
|
||||
user.pbFaint if user.fainted?
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
30
Data/Scripts/011_Battle/001_PBEnvironment.rb
Normal file
30
Data/Scripts/011_Battle/001_PBEnvironment.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
begin
|
||||
module PBEnvironment
|
||||
None = 0
|
||||
Grass = 1
|
||||
TallGrass = 2
|
||||
MovingWater = 3
|
||||
StillWater = 4
|
||||
Puddle = 5
|
||||
Underwater = 6
|
||||
Cave = 7
|
||||
Rock = 8
|
||||
Sand = 9
|
||||
Forest = 10
|
||||
ForestGrass = 11
|
||||
Snow = 12
|
||||
Ice = 13
|
||||
Volcano = 14
|
||||
Graveyard = 15
|
||||
Sky = 16
|
||||
Space = 17
|
||||
UltraSpace = 18
|
||||
|
||||
def self.maxValue; return 18; end
|
||||
end
|
||||
|
||||
rescue Exception
|
||||
if $!.is_a?(SystemExit) || "#{$!.class}"=="Reset"
|
||||
raise $!
|
||||
end
|
||||
end
|
||||
140
Data/Scripts/011_Battle/002_Move/001_PokeBattle_Move.rb
Normal file
140
Data/Scripts/011_Battle/002_Move/001_PokeBattle_Move.rb
Normal file
@@ -0,0 +1,140 @@
|
||||
class PokeBattle_Move
|
||||
attr_reader :battle
|
||||
attr_reader :realMove
|
||||
attr_accessor :id
|
||||
attr_reader :name
|
||||
attr_reader :function
|
||||
attr_reader :baseDamage
|
||||
attr_reader :type
|
||||
attr_reader :category
|
||||
attr_reader :accuracy
|
||||
attr_accessor :pp
|
||||
attr_writer :totalpp
|
||||
attr_reader :addlEffect
|
||||
attr_reader :target
|
||||
attr_reader :priority
|
||||
attr_reader :flags
|
||||
attr_accessor :calcType
|
||||
attr_accessor :powerBoost
|
||||
attr_accessor :snatched
|
||||
|
||||
def to_int; return @id; end
|
||||
|
||||
#=============================================================================
|
||||
# Creating a move
|
||||
#=============================================================================
|
||||
def initialize(battle,move)
|
||||
@battle = battle
|
||||
@realMove = move
|
||||
@id = move.id
|
||||
@name = PBMoves.getName(@id) # Get the move's name
|
||||
# Get data on the move
|
||||
moveData = pbGetMoveData(@id)
|
||||
@function = moveData[MOVE_FUNCTION_CODE]
|
||||
@baseDamage = moveData[MOVE_BASE_DAMAGE]
|
||||
@type = moveData[MOVE_TYPE]
|
||||
@category = moveData[MOVE_CATEGORY]
|
||||
@accuracy = moveData[MOVE_ACCURACY]
|
||||
@pp = move.pp # Can be changed with Mimic/Transform
|
||||
@addlEffect = moveData[MOVE_EFFECT_CHANCE]
|
||||
@target = moveData[MOVE_TARGET]
|
||||
@priority = moveData[MOVE_PRIORITY]
|
||||
@flags = moveData[MOVE_FLAGS]
|
||||
@calcType = -1
|
||||
@powerBoost = false # For Aerilate, Pixilate, Refrigerate, Galvanize
|
||||
@snatched = false
|
||||
end
|
||||
|
||||
# This is the code actually used to generate a PokeBattle_Move object. The
|
||||
# object generated is a subclass of this one which depends on the move's
|
||||
# function code (found in the script section PokeBattle_MoveEffect).
|
||||
def PokeBattle_Move.pbFromPBMove(battle,move)
|
||||
move = PBMove.new(0) if !move
|
||||
moveFunction = pbGetMoveData(move.id,MOVE_FUNCTION_CODE) || "000"
|
||||
className = sprintf("PokeBattle_Move_%s",moveFunction)
|
||||
if Object.const_defined?(className)
|
||||
return Object.const_get(className).new(battle,move)
|
||||
end
|
||||
return PokeBattle_UnimplementedMove.new(battle,move)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# About the move
|
||||
#=============================================================================
|
||||
def pbTarget(user); return @target; end
|
||||
|
||||
def totalpp
|
||||
return @totalpp if @totalpp && @totalpp>0 # Usually undefined
|
||||
return @realMove.totalpp if @realMove
|
||||
return 0
|
||||
end
|
||||
|
||||
# NOTE: This method is only ever called while using a move (and also by the
|
||||
# AI), so using @calcType here is acceptable.
|
||||
def physicalMove?(thisType=nil)
|
||||
return (@category==0) if MOVE_CATEGORY_PER_MOVE
|
||||
thisType ||= @calcType if @calcType>=0
|
||||
thisType = @type if !thisType
|
||||
return !PBTypes.isSpecialType?(thisType)
|
||||
end
|
||||
|
||||
# NOTE: This method is only ever called while using a move (and also by the
|
||||
# AI), so using @calcType here is acceptable.
|
||||
def specialMove?(thisType=nil)
|
||||
return (@category==1) if MOVE_CATEGORY_PER_MOVE
|
||||
thisType ||= @calcType if @calcType>=0
|
||||
thisType = @type if !thisType
|
||||
return PBTypes.isSpecialType?(thisType)
|
||||
end
|
||||
|
||||
def damagingMove?; return @category!=2; end
|
||||
def statusMove?; return @category==2; end
|
||||
|
||||
def usableWhenAsleep?; return false; end
|
||||
def unusableInGravity?; return false; end
|
||||
def healingMove?; return false; end
|
||||
def recoilMove?; return false; end
|
||||
def flinchingMove?; return false; end
|
||||
def callsAnotherMove?; return false; end
|
||||
# Whether the move can/will hit more than once in the same turn (including
|
||||
# Beat Up which may instead hit just once). Not the same as pbNumHits>1.
|
||||
def multiHitMove?; return false; end
|
||||
def chargingTurnMove?; return false; end
|
||||
def successCheckPerHit?; return false; end
|
||||
def hitsFlyingTargets?; return false; end
|
||||
def hitsDiggingTargets?; return false; end
|
||||
def hitsDivingTargets?; return false; end
|
||||
def ignoresReflect?; return false; end # For Brick Break
|
||||
def cannotRedirect?; return false; end # For Future Sight/Doom Desire
|
||||
def worksWithNoTargets?; return false; end # For Explosion
|
||||
def damageReducedByBurn?; return true; end # For Facade
|
||||
def triggersHyperMode?; return false; end
|
||||
|
||||
def contactMove?; return @flags[/a/]; end
|
||||
def canProtectAgainst?; return @flags[/b/]; end
|
||||
def canMagicCoat?; return @flags[/c/]; end
|
||||
def canSnatch?; return @flags[/d/]; end
|
||||
def canMirrorMove?; return @flags[/e/]; end
|
||||
def canKingsRock?; return @flags[/f/]; end
|
||||
def thawsUser?; return @flags[/g/]; end
|
||||
def highCriticalRate?; return @flags[/h/]; end
|
||||
def bitingMove?; return @flags[/i/]; end
|
||||
def punchingMove?; return @flags[/j/]; end
|
||||
def soundMove?; return @flags[/k/]; end
|
||||
def powderMove?; return @flags[/l/]; end
|
||||
def pulseMove?; return @flags[/m/]; end
|
||||
def bombMove?; return @flags[/n/]; end
|
||||
def danceMove?; return @flags[/o/]; end
|
||||
|
||||
# Causes perfect accuracy (param=1) and double damage (param=2).
|
||||
def tramplesMinimize?(param=1); return false; end
|
||||
def nonLethal?(user,target); return false; end # For False Swipe
|
||||
|
||||
def ignoresSubstitute?(user) # user is the Pokémon using this move
|
||||
if NEWEST_BATTLE_MECHANICS
|
||||
return true if soundMove?
|
||||
return true if user && user.hasActiveAbility?(:INFILTRATOR)
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
350
Data/Scripts/011_Battle/002_Move/002_Move_Usage.rb
Normal file
350
Data/Scripts/011_Battle/002_Move/002_Move_Usage.rb
Normal file
@@ -0,0 +1,350 @@
|
||||
class PokeBattle_Move
|
||||
#=============================================================================
|
||||
# Effect methods per move usage
|
||||
#=============================================================================
|
||||
def pbCanChooseMove?(user,commandPhase,showMessages); return true; end # For Belch
|
||||
def pbDisplayChargeMessage(user); end # For Focus Punch/shell Trap/Beak Blast
|
||||
def pbOnStartUse(user,targets); end
|
||||
def pbAddTarget(targets,user); end # For Counter, etc. and Bide
|
||||
|
||||
# Reset move usage counters (child classes can increment them).
|
||||
def pbChangeUsageCounters(user,specialUsage)
|
||||
user.effects[PBEffects::FuryCutter] = 0
|
||||
user.effects[PBEffects::ParentalBond] = 0
|
||||
user.effects[PBEffects::ProtectRate] = 1
|
||||
@battle.field.effects[PBEffects::FusionBolt] = false
|
||||
@battle.field.effects[PBEffects::FusionFlare] = false
|
||||
end
|
||||
|
||||
def pbDisplayUseMessage(user)
|
||||
@battle.pbDisplayBrief(_INTL("{1} used {2}!",user.pbThis,@name))
|
||||
end
|
||||
|
||||
def pbMissMessage(user,target); return false; end
|
||||
|
||||
#=============================================================================
|
||||
#
|
||||
#=============================================================================
|
||||
# Whether the move is currently in the "charging" turn of a two turn attack.
|
||||
# Is false if Power Herb or another effect lets a two turn move charge and
|
||||
# attack in the same turn.
|
||||
# user.effects[PBEffects::TwoTurnAttack] is set to the move's ID during the
|
||||
# charging turn, and is 0 during the attack turn.
|
||||
def pbIsChargingTurn?(user); return false; end
|
||||
def pbDamagingMove?; return damagingMove?; end
|
||||
|
||||
def pbContactMove?(user)
|
||||
return false if user.hasActiveAbility?(:LONGREACH)
|
||||
return contactMove?
|
||||
end
|
||||
|
||||
# The maximum number of hits in a round this move will actually perform. This
|
||||
# can be 1 for Beat Up, and can be 2 for any moves affected by Parental Bond.
|
||||
def pbNumHits(user,targets)
|
||||
if user.hasActiveAbility?(:PARENTALBOND) && pbDamagingMove? &&
|
||||
!chargingTurnMove? && targets.length==1
|
||||
# Record that Parental Bond applies, to weaken the second attack
|
||||
user.effects[PBEffects::ParentalBond] = 3
|
||||
return 2
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Effect methods per hit
|
||||
#=============================================================================
|
||||
def pbOverrideSuccessCheckPerHit(user,target); return false; end
|
||||
def pbCrashDamage(user); end
|
||||
def pbInitialEffect(user,targets,hitNum); end
|
||||
|
||||
def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true)
|
||||
return if !showAnimation
|
||||
if user.effects[PBEffects::ParentalBond]==1
|
||||
@battle.pbCommonAnimation("ParentalBond",user,targets)
|
||||
else
|
||||
@battle.pbAnimation(id,user,targets,hitNum)
|
||||
end
|
||||
end
|
||||
|
||||
def pbSelfKO(user); end
|
||||
def pbEffectWhenDealingDamage(user,target); end
|
||||
def pbEffectAgainstTarget(user,target); end
|
||||
def pbEffectGeneral(user); end
|
||||
def pbAdditionalEffect(user,target); end
|
||||
def pbEffectAfterAllHits(user,target); end # Move effects that occur after all hits
|
||||
def pbSwitchOutTargetsEffect(user,targets,numHits,switchedBattlers); end
|
||||
def pbEndOfMoveUsageEffect(user,targets,numHits,switchedBattlers); end
|
||||
|
||||
#=============================================================================
|
||||
# Check if target is immune to the move because of its ability
|
||||
#=============================================================================
|
||||
def pbImmunityByAbility(user,target)
|
||||
return false if @battle.moldBreaker
|
||||
ret = false
|
||||
if target.abilityActive?
|
||||
ret = BattleHandlers.triggerMoveImmunityTargetAbility(target.ability,
|
||||
user,target,self,@calcType,@battle)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Move failure checks
|
||||
#=============================================================================
|
||||
# Check whether the move fails completely due to move-specific requirements.
|
||||
def pbMoveFailed?(user,targets); return false; end
|
||||
# Checks whether the move will be ineffective against the target.
|
||||
def pbFailsAgainstTarget?(user,target); return false; end
|
||||
|
||||
def pbMoveFailedLastInRound?(user)
|
||||
unmoved = false
|
||||
@battle.eachBattler do |b|
|
||||
next if b.index==user.index
|
||||
next if @battle.choices[b.index][0]!=:UseMove && @battle.choices[b.index][0]!=:Shift
|
||||
next if b.movedThisRound?
|
||||
unmoved = true
|
||||
break
|
||||
end
|
||||
if !unmoved
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def pbMoveFailedTargetAlreadyMoved?(target)
|
||||
if (@battle.choices[target.index][0]!=:UseMove &&
|
||||
@battle.choices[target.index][0]!=:Shift) || target.movedThisRound?
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def pbMoveFailedAromaVeil?(user,target,showMessage=true)
|
||||
return false if @battle.moldBreaker
|
||||
if target.hasActiveAbility?(:AROMAVEIL)
|
||||
if showMessage
|
||||
@battle.pbShowAbilitySplash(target)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected because of its {2}!",
|
||||
target.pbThis,target.abilityName))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(target)
|
||||
end
|
||||
return true
|
||||
end
|
||||
target.eachAlly do |b|
|
||||
next if !b.hasActiveAbility?(:AROMAVEIL)
|
||||
if showMessage
|
||||
@battle.pbShowAbilitySplash(target)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected!",target.pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1} is unaffected because of {2}'s {3}!",
|
||||
target.pbThis,b.pbThis(true),b.abilityName))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(target)
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Weaken the damage dealt (doesn't actually change a battler's HP)
|
||||
#=============================================================================
|
||||
def pbCheckDamageAbsorption(user,target)
|
||||
# Substitute will take the damage
|
||||
if target.effects[PBEffects::Substitute]>0 && !ignoresSubstitute?(user) &&
|
||||
(!user || user.index!=target.index)
|
||||
target.damageState.substitute = true
|
||||
return
|
||||
end
|
||||
# Disguise will take the damage
|
||||
if !@battle.moldBreaker && isConst?(target.species,PBSpecies,:MIMIKYU) &&
|
||||
target.form==0 && isConst?(target.ability,PBAbilities,:DISGUISE)
|
||||
target.damageState.disguise = true
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def pbReduceDamage(user,target)
|
||||
damage = target.damageState.calcDamage
|
||||
# Substitute takes the damage
|
||||
if target.damageState.substitute
|
||||
damage = target.effects[PBEffects::Substitute] if damage>target.effects[PBEffects::Substitute]
|
||||
target.damageState.hpLost = damage
|
||||
target.damageState.totalHPLost += damage
|
||||
return
|
||||
end
|
||||
# Disguise takes the damage
|
||||
return if target.damageState.disguise
|
||||
# Target takes the damage
|
||||
if damage>=target.hp
|
||||
damage = target.hp
|
||||
# Survive a lethal hit with 1 HP effects
|
||||
if nonLethal?(user,target)
|
||||
damage -= 1
|
||||
elsif target.effects[PBEffects::Endure]
|
||||
target.damageState.endured = true
|
||||
damage -= 1
|
||||
elsif damage==target.totalhp
|
||||
if target.hasActiveAbility?(:STURDY) && !@battle.moldBreaker
|
||||
target.damageState.sturdy = true
|
||||
damage -= 1
|
||||
elsif target.hasActiveItem?(:FOCUSSASH) && target.hp==target.totalhp
|
||||
target.damageState.focusSash = true
|
||||
damage -= 1
|
||||
elsif target.hasActiveItem?(:FOCUSBAND) && @battle.pbRandom(100)<10
|
||||
target.damageState.focusBand = true
|
||||
damage -= 1
|
||||
end
|
||||
end
|
||||
end
|
||||
damage = 0 if damage<0
|
||||
target.damageState.hpLost = damage
|
||||
target.damageState.totalHPLost += damage
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Change the target's HP by the amount calculated above
|
||||
#=============================================================================
|
||||
def pbInflictHPDamage(target)
|
||||
if target.damageState.substitute
|
||||
target.effects[PBEffects::Substitute] -= target.damageState.hpLost
|
||||
else
|
||||
target.hp -= target.damageState.hpLost
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Animate the damage dealt, including lowering the HP
|
||||
#=============================================================================
|
||||
# Animate being damaged and losing HP (by a move)
|
||||
def pbAnimateHitAndHPLost(user,targets)
|
||||
# Animate allies first, then foes
|
||||
animArray = []
|
||||
for side in 0...2 # side here means "allies first, then foes"
|
||||
targets.each do |b|
|
||||
next if b.damageState.unaffected || b.damageState.hpLost==0
|
||||
next if (side==0 && b.opposes?(user)) || (side==1 && !b.opposes?(user))
|
||||
oldHP = b.hp+b.damageState.hpLost
|
||||
PBDebug.log("[Move damage] #{b.pbThis} lost #{b.damageState.hpLost} HP (#{oldHP}=>#{b.hp})")
|
||||
effectiveness = 0
|
||||
if PBTypes.resistant?(b.damageState.typeMod); effectiveness = 1
|
||||
elsif PBTypes.superEffective?(b.damageState.typeMod); effectiveness = 2
|
||||
end
|
||||
animArray.push([b,oldHP,effectiveness])
|
||||
end
|
||||
if animArray.length>0
|
||||
@battle.scene.pbHitAndHPLossAnimation(animArray)
|
||||
animArray.clear
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Messages upon being hit
|
||||
#=============================================================================
|
||||
def pbEffectivenessMessage(user,target,numTargets=1)
|
||||
return if target.damageState.disguise
|
||||
if PBTypes.superEffective?(target.damageState.typeMod)
|
||||
if numTargets>1
|
||||
@battle.pbDisplay(_INTL("It's super effective on {1}!",target.pbThis(true)))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("It's super effective!"))
|
||||
end
|
||||
elsif PBTypes.notVeryEffective?(target.damageState.typeMod)
|
||||
if numTargets>1
|
||||
@battle.pbDisplay(_INTL("It's not very effective on {1}...",target.pbThis(true)))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("It's not very effective..."))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbHitEffectivenessMessages(user,target,numTargets=1)
|
||||
return if target.damageState.disguise
|
||||
if target.damageState.substitute
|
||||
@battle.pbDisplay(_INTL("The substitute took damage for {1}!",target.pbThis(true)))
|
||||
end
|
||||
if target.damageState.critical
|
||||
if numTargets>1
|
||||
@battle.pbDisplay(_INTL("A critical hit on {1}!",target.pbThis(true)))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("A critical hit!"))
|
||||
end
|
||||
end
|
||||
# Effectiveness message, for moves with 1 hit
|
||||
if !multiHitMove? && user.effects[PBEffects::ParentalBond]==0
|
||||
pbEffectivenessMessage(user,target,numTargets)
|
||||
end
|
||||
if target.damageState.substitute && target.effects[PBEffects::Substitute]==0
|
||||
target.effects[PBEffects::Substitute] = 0
|
||||
@battle.pbDisplay(_INTL("{1}'s substitute faded!",target.pbThis))
|
||||
end
|
||||
end
|
||||
|
||||
def pbEndureKOMessage(target)
|
||||
if target.damageState.disguise
|
||||
@battle.pbShowAbilitySplash(target)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("Its disguise served it as a decoy!"))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1}'s disguise served it as a decoy!",target.pbThis))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(target)
|
||||
target.pbChangeForm(1,_INTL("{1}'s disguise was busted!",target.pbThis))
|
||||
elsif target.damageState.endured
|
||||
@battle.pbDisplay(_INTL("{1} endured the hit!",target.pbThis))
|
||||
elsif target.damageState.sturdy
|
||||
@battle.pbShowAbilitySplash(target)
|
||||
if PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@battle.pbDisplay(_INTL("{1} endured the hit!",target.pbThis))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1} hung on with Sturdy!",target.pbThis))
|
||||
end
|
||||
@battle.pbHideAbilitySplash(target)
|
||||
elsif target.damageState.focusSash
|
||||
@battle.pbCommonAnimation("UseItem",target)
|
||||
@battle.pbDisplay(_INTL("{1} hung on using its Focus Sash!",target.pbThis))
|
||||
target.pbConsumeItem
|
||||
elsif target.damageState.focusBand
|
||||
@battle.pbCommonAnimation("UseItem",target)
|
||||
@battle.pbDisplay(_INTL("{1} hung on using its Focus Band!",target.pbThis))
|
||||
end
|
||||
end
|
||||
|
||||
# Used by Counter/Mirror Coat/Metal Burst/Revenge/Focus Punch/Bide/Assurance.
|
||||
def pbRecordDamageLost(user,target)
|
||||
damage = target.damageState.hpLost
|
||||
# NOTE: In Gen 3 where a move's category depends on its type, Hidden Power
|
||||
# is for some reason countered by Counter rather than Mirror Coat,
|
||||
# regardless of its calculated type. Hence the following two lines of
|
||||
# code.
|
||||
moveType = nil
|
||||
moveType = getID(PBTypes,:NORMAL) if @function=="090" # Hidden Power
|
||||
if physicalMove?(moveType)
|
||||
target.effects[PBEffects::Counter] = damage
|
||||
target.effects[PBEffects::CounterTarget] = user.index
|
||||
elsif specialMove?(moveType)
|
||||
target.effects[PBEffects::MirrorCoat] = damage
|
||||
target.effects[PBEffects::MirrorCoatTarget] = user.index
|
||||
end
|
||||
if target.effects[PBEffects::Bide]>0
|
||||
target.effects[PBEffects::BideDamage] += damage
|
||||
target.effects[PBEffects::BideTarget] = user.index
|
||||
end
|
||||
target.damageState.fainted = true if target.fainted?
|
||||
target.lastHPLost = damage # For Focus Punch
|
||||
target.tookDamage = true if damage>0 # For Assurance
|
||||
target.lastAttacker.push(user.index) # For Revenge
|
||||
if target.opposes?(user)
|
||||
target.lastHPLostFromFoe = damage # For Metal Burst
|
||||
target.lastFoeAttacker.push(user.index) # For Metal Burst
|
||||
end
|
||||
end
|
||||
end
|
||||
491
Data/Scripts/011_Battle/002_Move/003_Move_Usage_Calculations.rb
Normal file
491
Data/Scripts/011_Battle/002_Move/003_Move_Usage_Calculations.rb
Normal file
@@ -0,0 +1,491 @@
|
||||
class PokeBattle_Move
|
||||
#=============================================================================
|
||||
# Move's type calculation
|
||||
#=============================================================================
|
||||
def pbBaseType(user)
|
||||
ret = @type
|
||||
return ret if !ret || ret<0
|
||||
if user.abilityActive?
|
||||
ret = BattleHandlers.triggerMoveBaseTypeModifierAbility(user.ability,user,self,ret)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbCalcType(user)
|
||||
@powerBoost = false
|
||||
ret = pbBaseType(user)
|
||||
return ret if !ret || ret<0
|
||||
if hasConst?(PBTypes,:ELECTRIC)
|
||||
if @battle.field.effects[PBEffects::IonDeluge] && isConst?(ret,PBTypes,:NORMAL)
|
||||
ret = getConst(PBTypes,:ELECTRIC)
|
||||
@powerBoost = false
|
||||
end
|
||||
if user.effects[PBEffects::Electrify]
|
||||
ret = getConst(PBTypes,:ELECTRIC)
|
||||
@powerBoost = false
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Type effectiveness calculation
|
||||
#=============================================================================
|
||||
def pbCalcTypeModSingle(moveType,defType,user,target)
|
||||
ret = PBTypes.getEffectiveness(moveType,defType)
|
||||
# Ring Target
|
||||
if target.hasActiveItem?(:RINGTARGET)
|
||||
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if PBTypes.ineffective?(moveType,defType)
|
||||
end
|
||||
# Foresight
|
||||
if user.hasActiveAbility?(:SCRAPPY) || target.effects[PBEffects::Foresight]
|
||||
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if isConst?(defType,PBTypes,:GHOST) &&
|
||||
PBTypes.ineffective?(moveType,defType)
|
||||
end
|
||||
# Miracle Eye
|
||||
if target.effects[PBEffects::MiracleEye]
|
||||
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if isConst?(defType,PBTypes,:DARK) &&
|
||||
PBTypes.ineffective?(moveType,defType)
|
||||
end
|
||||
# Delta Stream's weather
|
||||
if @battle.pbWeather==PBWeather::StrongWinds
|
||||
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if isConst?(defType,PBTypes,:FLYING) &&
|
||||
PBTypes.superEffective?(moveType,defType)
|
||||
end
|
||||
# Grounded Flying-type Pokémon become susceptible to Ground moves
|
||||
if !target.airborne?
|
||||
ret = PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE if isConst?(defType,PBTypes,:FLYING) &&
|
||||
isConst?(moveType,PBTypes,:GROUND)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbCalcTypeMod(moveType,user,target)
|
||||
return PBTypeEffectiveness::NORMAL_EFFECTIVE if moveType<0
|
||||
return PBTypeEffectiveness::NORMAL_EFFECTIVE if isConst?(moveType,PBTypes,:GROUND) &&
|
||||
target.pbHasType?(:FLYING) && target.hasActiveItem?(:IRONBALL)
|
||||
# Determine types
|
||||
tTypes = target.pbTypes(true)
|
||||
# Get effectivenesses
|
||||
typeMods = [PBTypeEffectiveness::NORMAL_EFFECTIVE_ONE] * 3 # 3 types max
|
||||
tTypes.each_with_index do |type,i|
|
||||
typeMods[i] = pbCalcTypeModSingle(moveType,type,user,target)
|
||||
end
|
||||
# Multiply all effectivenesses together
|
||||
ret = 1
|
||||
typeMods.each { |m| ret *= m }
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Accuracy check
|
||||
#=============================================================================
|
||||
def pbBaseAccuracy(user,target); return @accuracy; end
|
||||
|
||||
# Accuracy calculations for one-hit KO moves and "always hit" moves are
|
||||
# handled elsewhere.
|
||||
def pbAccuracyCheck(user,target)
|
||||
# "Always hit" effects and "always hit" accuracy
|
||||
return true if target.effects[PBEffects::Telekinesis]>0
|
||||
return true if target.effects[PBEffects::Minimize] && tramplesMinimize?(1)
|
||||
baseAcc = pbBaseAccuracy(user,target)
|
||||
return true if baseAcc==0
|
||||
# Calculate all multiplier effects
|
||||
modifiers = []
|
||||
modifiers[BASE_ACC] = baseAcc
|
||||
modifiers[ACC_STAGE] = user.stages[PBStats::ACCURACY]
|
||||
modifiers[EVA_STAGE] = target.stages[PBStats::EVASION]
|
||||
modifiers[ACC_MULT] = 0x1000
|
||||
modifiers[EVA_MULT] = 0x1000
|
||||
pbCalcAccuracyModifiers(user,target,modifiers)
|
||||
# Check if move can't miss
|
||||
return true if modifiers[BASE_ACC]==0
|
||||
# Calculation
|
||||
accStage = [[modifiers[ACC_STAGE],-6].max,6].min + 6
|
||||
evaStage = [[modifiers[EVA_STAGE],-6].max,6].min + 6
|
||||
stageMul = [3,3,3,3,3,3, 3, 4,5,6,7,8,9]
|
||||
stageDiv = [9,8,7,6,5,4, 3, 3,3,3,3,3,3]
|
||||
accuracy = 100.0 * stageMul[accStage] / stageDiv[accStage]
|
||||
evasion = 100.0 * stageMul[evaStage] / stageDiv[evaStage]
|
||||
accuracy = (accuracy * modifiers[ACC_MULT] / 0x1000).round
|
||||
evasion = (evasion * modifiers[EVA_MULT] / 0x1000).round
|
||||
evasion = 1 if evasion<1
|
||||
# Calculation
|
||||
return @battle.pbRandom(100) < modifiers[BASE_ACC] * accuracy / evasion
|
||||
end
|
||||
|
||||
def pbCalcAccuracyModifiers(user,target,modifiers)
|
||||
# Ability effects that alter accuracy calculation
|
||||
if user.abilityActive?
|
||||
BattleHandlers.triggerAccuracyCalcUserAbility(user.ability,
|
||||
modifiers,user,target,self,@calcType)
|
||||
end
|
||||
user.eachAlly do |b|
|
||||
next if !b.abilityActive?
|
||||
BattleHandlers.triggerAccuracyCalcUserAllyAbility(b.ability,
|
||||
modifiers,user,target,self,@calcType)
|
||||
end
|
||||
if target.abilityActive? && !@battle.moldBreaker
|
||||
BattleHandlers.triggerAccuracyCalcTargetAbility(target.ability,
|
||||
modifiers,user,target,self,@calcType)
|
||||
end
|
||||
# Item effects that alter accuracy calculation
|
||||
if user.itemActive?
|
||||
BattleHandlers.triggerAccuracyCalcUserItem(user.item,
|
||||
modifiers,user,target,self,@calcType)
|
||||
end
|
||||
if target.itemActive?
|
||||
BattleHandlers.triggerAccuracyCalcTargetItem(target.item,
|
||||
modifiers,user,target,self,@calcType)
|
||||
end
|
||||
# Other effects, inc. ones that set ACC_MULT or EVA_STAGE to specific values
|
||||
if @battle.field.effects[PBEffects::Gravity]>0
|
||||
modifiers[ACC_MULT] = (modifiers[ACC_MULT]*5/3).round
|
||||
end
|
||||
if user.effects[PBEffects::MicleBerry]
|
||||
user.effects[PBEffects::MicleBerry] = false
|
||||
modifiers[ACC_MULT] = (modifiers[ACC_MULT]*1.2).round
|
||||
end
|
||||
modifiers[EVA_STAGE] = 0 if target.effects[PBEffects::Foresight] && modifiers[EVA_STAGE]>0
|
||||
modifiers[EVA_STAGE] = 0 if target.effects[PBEffects::MiracleEye] && modifiers[EVA_STAGE]>0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Critical hit check
|
||||
#=============================================================================
|
||||
# Return values:
|
||||
# -1: Never a critical hit.
|
||||
# 0: Calculate normally.
|
||||
# 1: Always a critical hit.
|
||||
def pbCritialOverride(user,target); return 0; end
|
||||
|
||||
# Returns whether the move will be a critical hit.
|
||||
def pbIsCritical?(user,target)
|
||||
return false if target.pbOwnSide.effects[PBEffects::LuckyChant]>0
|
||||
# Set up the critical hit ratios
|
||||
ratios = (NEWEST_BATTLE_MECHANICS) ? [24,8,2,1] : [16,8,4,3,2]
|
||||
c = 0
|
||||
# Ability effects that alter critical hit rate
|
||||
if c>=0 && user.abilityActive?
|
||||
c = BattleHandlers.triggerCriticalCalcUserAbility(user.ability,user,target,c)
|
||||
end
|
||||
if c>=0 && target.abilityActive? && !@battle.moldBreaker
|
||||
c = BattleHandlers.triggerCriticalCalcTargetAbility(target.ability,user,target,c)
|
||||
end
|
||||
# Item effects that alter critical hit rate
|
||||
if c>=0 && user.itemActive?
|
||||
c = BattleHandlers.triggerCriticalCalcUserItem(user.item,user,target,c)
|
||||
end
|
||||
if c>=0 && target.itemActive?
|
||||
c = BattleHandlers.triggerCriticalCalcTargetItem(target.item,user,target,c)
|
||||
end
|
||||
return false if c<0
|
||||
# Move-specific "always/never a critical hit" effects
|
||||
case pbCritialOverride(user,target)
|
||||
when 1; return true
|
||||
when -1; return false
|
||||
end
|
||||
# Other effects
|
||||
return true if c>50 # Merciless
|
||||
return true if user.effects[PBEffects::LaserFocus]>0
|
||||
c += 1 if highCriticalRate?
|
||||
c += user.effects[PBEffects::FocusEnergy]
|
||||
c += 1 if user.inHyperMode? && isConst?(@type,PBTypes,:SHADOW)
|
||||
c = ratios.length-1 if c>=ratios.length
|
||||
# Calculation
|
||||
return @battle.pbRandom(ratios[c])==0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Damage calculation
|
||||
#=============================================================================
|
||||
def pbBaseDamage(baseDmg,user,target); return baseDmg; end
|
||||
def pbBaseDamageMultiplier(damageMult,user,target); return damageMult; end
|
||||
def pbModifyDamage(damageMult,user,target); return damageMult; end
|
||||
|
||||
def pbGetAttackStats(user,target)
|
||||
if specialMove?
|
||||
return user.spatk, user.stages[PBStats::SPATK]+6
|
||||
end
|
||||
return user.attack, user.stages[PBStats::ATTACK]+6
|
||||
end
|
||||
|
||||
def pbGetDefenseStats(user,target)
|
||||
if specialMove?
|
||||
return target.spdef, target.stages[PBStats::SPDEF]+6
|
||||
end
|
||||
return target.defense, target.stages[PBStats::DEFENSE]+6
|
||||
end
|
||||
|
||||
def pbCalcDamage(user,target,numTargets=1)
|
||||
return if statusMove?
|
||||
if target.damageState.disguise
|
||||
target.damageState.calcDamage = 1
|
||||
return
|
||||
end
|
||||
stageMul = [2,2,2,2,2,2, 2, 3,4,5,6,7,8]
|
||||
stageDiv = [8,7,6,5,4,3, 2, 2,2,2,2,2,2]
|
||||
# Get the move's type
|
||||
type = @calcType # -1 is treated as physical
|
||||
# Calculate whether this hit deals critical damage
|
||||
target.damageState.critical = pbIsCritical?(user,target)
|
||||
# Calcuate base power of move
|
||||
baseDmg = pbBaseDamage(@baseDamage,user,target)
|
||||
# Calculate user's attack stat
|
||||
atk, atkStage = pbGetAttackStats(user,target)
|
||||
if !target.hasActiveAbility?(:UNAWARE) || @battle.moldBreaker
|
||||
atkStage = 6 if target.damageState.critical && atkStage<6
|
||||
atk = (atk.to_f*stageMul[atkStage]/stageDiv[atkStage]).floor
|
||||
end
|
||||
# Calculate target's defense stat
|
||||
defense, defStage = pbGetDefenseStats(user,target)
|
||||
if !user.hasActiveAbility?(:UNAWARE)
|
||||
defStage = 6 if target.damageState.critical && defStage>6
|
||||
defense = (defense.to_f*stageMul[defStage]/stageDiv[defStage]).floor
|
||||
end
|
||||
# Calculate all multiplier effects
|
||||
multipliers = [0x1000,0x1000,0x1000,0x1000]
|
||||
pbCalcDamageMultipliers(user,target,numTargets,type,baseDmg,multipliers)
|
||||
# Main damage calculation
|
||||
baseDmg = [(baseDmg * multipliers[BASE_DMG_MULT] / 0x1000).round,1].max
|
||||
atk = [(atk * multipliers[ATK_MULT] / 0x1000).round,1].max
|
||||
defense = [(defense * multipliers[DEF_MULT] / 0x1000).round,1].max
|
||||
damage = (((2.0*user.level/5+2).floor*baseDmg*atk/defense).floor/50).floor+2
|
||||
damage = [(damage * multipliers[FINAL_DMG_MULT] / 0x1000).round,1].max
|
||||
target.damageState.calcDamage = damage
|
||||
end
|
||||
|
||||
def pbCalcDamageMultipliers(user,target,numTargets,baseDmg,type,multipliers)
|
||||
# Global abilities
|
||||
if (@battle.pbCheckGlobalAbility(:DARKAURA) && isConst?(type,PBTypes,:DARK)) ||
|
||||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && isConst?(type,PBTypes,:FAIRY))
|
||||
if @battle.pbCheckGlobalAbility(:AURABREAK)
|
||||
multipliers[BASE_DMG_MULT] *= 2/3.0
|
||||
else
|
||||
multipliers[BASE_DMG_MULT] *= 4/3.0
|
||||
end
|
||||
end
|
||||
# Ability effects that alter damage
|
||||
if user.abilityActive?
|
||||
BattleHandlers.triggerDamageCalcUserAbility(user.ability,
|
||||
user,target,self,multipliers,baseDmg,type)
|
||||
end
|
||||
if !@battle.moldBreaker
|
||||
# NOTE: It's odd that the user's Mold Breaker prevents its partner's
|
||||
# beneficial abilities (i.e. Flower Gift boosting Atk), but that's
|
||||
# how it works.
|
||||
user.eachAlly do |b|
|
||||
next if !b.abilityActive?
|
||||
BattleHandlers.triggerDamageCalcUserAllyAbility(b.ability,
|
||||
user,target,self,multipliers,baseDmg,type)
|
||||
end
|
||||
if target.abilityActive?
|
||||
BattleHandlers.triggerDamageCalcTargetAbility(target.ability,
|
||||
user,target,self,multipliers,baseDmg,type) if !@battle.moldBreaker
|
||||
BattleHandlers.triggerDamageCalcTargetAbilityNonIgnorable(target.ability,
|
||||
user,target,self,multipliers,baseDmg,type)
|
||||
end
|
||||
target.eachAlly do |b|
|
||||
next if !b.abilityActive?
|
||||
BattleHandlers.triggerDamageCalcTargetAllyAbility(b.ability,
|
||||
user,target,self,multipliers,baseDmg,type)
|
||||
end
|
||||
end
|
||||
# Item effects that alter damage
|
||||
if user.itemActive?
|
||||
BattleHandlers.triggerDamageCalcUserItem(user.item,
|
||||
user,target,self,multipliers,baseDmg,type)
|
||||
end
|
||||
if target.itemActive?
|
||||
BattleHandlers.triggerDamageCalcTargetItem(target.item,
|
||||
user,target,self,multipliers,baseDmg,type)
|
||||
end
|
||||
# Parental Bond's second attack
|
||||
if user.effects[PBEffects::ParentalBond]==1
|
||||
multipliers[BASE_DMG_MULT] /= 4
|
||||
end
|
||||
# Other
|
||||
if user.effects[PBEffects::MeFirst]
|
||||
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
|
||||
end
|
||||
if user.effects[PBEffects::HelpingHand] && !self.is_a?(PokeBattle_Confusion)
|
||||
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
|
||||
end
|
||||
if user.effects[PBEffects::Charge]>0 && isConst?(type,PBTypes,:ELECTRIC)
|
||||
multipliers[BASE_DMG_MULT] *= 2
|
||||
end
|
||||
# Mud Sport
|
||||
if isConst?(type,PBTypes,:ELECTRIC)
|
||||
@battle.eachBattler do |b|
|
||||
next if !b.effects[PBEffects::MudSport]
|
||||
multipliers[BASE_DMG_MULT] /= 3
|
||||
break
|
||||
end
|
||||
if @battle.field.effects[PBEffects::MudSportField]>0
|
||||
multipliers[BASE_DMG_MULT] /= 3
|
||||
end
|
||||
end
|
||||
# Water Sport
|
||||
if isConst?(type,PBTypes,:FIRE)
|
||||
@battle.eachBattler do |b|
|
||||
next if !b.effects[PBEffects::WaterSport]
|
||||
multipliers[BASE_DMG_MULT] /= 3
|
||||
break
|
||||
end
|
||||
if @battle.field.effects[PBEffects::WaterSportField]>0
|
||||
multipliers[BASE_DMG_MULT] /= 3
|
||||
end
|
||||
end
|
||||
# Terrain moves
|
||||
if user.affectedByTerrain?
|
||||
case @battle.field.terrain
|
||||
when PBBattleTerrains::Electric
|
||||
if isConst?(type,PBTypes,:ELECTRIC)
|
||||
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
|
||||
end
|
||||
when PBBattleTerrains::Grassy
|
||||
if isConst?(type,PBTypes,:GRASS)
|
||||
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
|
||||
end
|
||||
when PBBattleTerrains::Psychic
|
||||
if isConst?(type,PBTypes,:PSYCHIC)
|
||||
multipliers[BASE_DMG_MULT] = (multipliers[BASE_DMG_MULT]*1.5).round
|
||||
end
|
||||
end
|
||||
end
|
||||
if @battle.field.terrain==PBBattleTerrains::Misty && target.affectedByTerrain? &&
|
||||
isConst?(type,PBTypes,:DRAGON)
|
||||
multipliers[BASE_DMG_MULT] /= 2
|
||||
end
|
||||
# Badge multipliers
|
||||
if @battle.internalBattle
|
||||
if user.pbOwnedByPlayer?
|
||||
if physicalMove? && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_ATTACK
|
||||
multipliers[ATK_MULT] = (multipliers[ATK_MULT]*1.1).round
|
||||
elsif specialMove? && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_SPATK
|
||||
multipliers[ATK_MULT] = (multipliers[ATK_MULT]*1.1).round
|
||||
end
|
||||
end
|
||||
if target.pbOwnedByPlayer?
|
||||
if physicalMove? && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_DEFENSE
|
||||
multipliers[DEF_MULT] = (multipliers[DEF_MULT]*1.1).round
|
||||
elsif specialMove? && @battle.pbPlayer.numbadges>=NUM_BADGES_BOOST_SPDEF
|
||||
multipliers[DEF_MULT] = (multipliers[DEF_MULT]*1.1).round
|
||||
end
|
||||
end
|
||||
end
|
||||
# Multi-targeting attacks
|
||||
if numTargets>1
|
||||
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*0.75).round
|
||||
end
|
||||
# Weather
|
||||
case @battle.pbWeather
|
||||
when PBWeather::Sun, PBWeather::HarshSun
|
||||
if isConst?(type,PBTypes,:FIRE)
|
||||
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
|
||||
elsif isConst?(type,PBTypes,:WATER)
|
||||
multipliers[FINAL_DMG_MULT] /= 2
|
||||
end
|
||||
when PBWeather::Rain, PBWeather::HeavyRain
|
||||
if isConst?(type,PBTypes,:FIRE)
|
||||
multipliers[FINAL_DMG_MULT] /= 2
|
||||
elsif isConst?(type,PBTypes,:WATER)
|
||||
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
|
||||
end
|
||||
when PBWeather::Sandstorm
|
||||
if target.pbHasType?(:ROCK) && specialMove? && @function!="122" # Psyshock
|
||||
multipliers[DEF_MULT] = (multipliers[DEF_MULT]*1.5).round
|
||||
end
|
||||
end
|
||||
# Critical hits
|
||||
if target.damageState.critical
|
||||
if NEWEST_BATTLE_MECHANICS
|
||||
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
|
||||
else
|
||||
multipliers[FINAL_DMG_MULT] *= 2
|
||||
end
|
||||
end
|
||||
# Random variance
|
||||
if !self.is_a?(PokeBattle_Confusion)
|
||||
random = 85+@battle.pbRandom(16)
|
||||
multipliers[FINAL_DMG_MULT] *= random/100.0
|
||||
end
|
||||
# STAB
|
||||
if type>=0 && user.pbHasType?(type)
|
||||
if user.hasActiveAbility?(:ADAPTABILITY)
|
||||
multipliers[FINAL_DMG_MULT] *= 2
|
||||
else
|
||||
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*1.5).round
|
||||
end
|
||||
end
|
||||
# Type effectiveness
|
||||
multipliers[FINAL_DMG_MULT] *= target.damageState.typeMod.to_f/PBTypeEffectiveness::NORMAL_EFFECTIVE
|
||||
multipliers[FINAL_DMG_MULT] = multipliers[FINAL_DMG_MULT].round
|
||||
# Burn
|
||||
if user.status==PBStatuses::BURN && physicalMove? && damageReducedByBurn? &&
|
||||
!user.hasActiveAbility?(:GUTS)
|
||||
multipliers[FINAL_DMG_MULT] /= 2
|
||||
end
|
||||
# Aurora Veil, Reflect, Light Screen
|
||||
if !ignoresReflect? && !target.damageState.critical &&
|
||||
!user.hasActiveAbility?(:INFILTRATOR)
|
||||
if target.pbOwnSide.effects[PBEffects::AuroraVeil]>0
|
||||
if @battle.pbSideBattlerCount(target)>1
|
||||
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*2/3).round
|
||||
else
|
||||
multipliers[FINAL_DMG_MULT] /= 2
|
||||
end
|
||||
elsif target.pbOwnSide.effects[PBEffects::Reflect]>0 && physicalMove?
|
||||
if @battle.pbSideBattlerCount(target)>1
|
||||
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*2/3).round
|
||||
else
|
||||
multipliers[FINAL_DMG_MULT] /= 2
|
||||
end
|
||||
elsif target.pbOwnSide.effects[PBEffects::LightScreen]>0 && specialMove?
|
||||
if @battle.pbSideBattlerCount(target)>1
|
||||
multipliers[FINAL_DMG_MULT] = (multipliers[FINAL_DMG_MULT]*2/3).round
|
||||
else
|
||||
multipliers[FINAL_DMG_MULT] /= 2
|
||||
end
|
||||
end
|
||||
end
|
||||
# Minimize
|
||||
if target.effects[PBEffects::Minimize] && tramplesMinimize?(2)
|
||||
multipliers[FINAL_DMG_MULT] *= 2
|
||||
end
|
||||
# Move-specific base damage modifiers
|
||||
multipliers[BASE_DMG_MULT] = pbBaseDamageMultiplier(multipliers[BASE_DMG_MULT],user,target)
|
||||
# Move-specific final damage modifiers
|
||||
multipliers[FINAL_DMG_MULT] = pbModifyDamage(multipliers[FINAL_DMG_MULT],user,target)
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Additional effect chance
|
||||
#=============================================================================
|
||||
def pbAdditionalEffectChance(user,target,effectChance=0)
|
||||
return 0 if target.hasActiveAbility?(:SHIELDDUST) && !@battle.moldBreaker
|
||||
ret = (effectChance>0) ? effectChance : @addlEffect
|
||||
if NEWEST_BATTLE_MECHANICS || @function!="0A4" # Secret Power
|
||||
ret *= 2 if user.hasActiveAbility?(:SERENEGRACE) ||
|
||||
user.pbOwnSide.effects[PBEffects::Rainbow]>0
|
||||
end
|
||||
ret = 100 if $DEBUG && Input.press?(Input::CTRL)
|
||||
return ret
|
||||
end
|
||||
|
||||
# NOTE: Flinching caused by a move's effect is applied in that move's code,
|
||||
# not here.
|
||||
def pbFlinchChance(user,target)
|
||||
return 0 if flinchingMove?
|
||||
return 0 if target.hasActiveAbility?(:SHIELDDUST) && !@battle.moldBreaker
|
||||
ret = 0
|
||||
if user.hasActiveAbility?(:STENCH,true)
|
||||
ret = 10
|
||||
elsif user.hasActiveItem?([:KINGSROCK,:RAZORFANG],true)
|
||||
ret = 10
|
||||
end
|
||||
ret *= 2 if user.hasActiveAbility?(:SERENEGRACE) ||
|
||||
user.pbOwnSide.effects[PBEffects::Rainbow]>0
|
||||
return ret
|
||||
end
|
||||
end
|
||||
714
Data/Scripts/011_Battle/002_Move/004_Move_Effects_Generic.rb
Normal file
714
Data/Scripts/011_Battle/002_Move/004_Move_Effects_Generic.rb
Normal file
@@ -0,0 +1,714 @@
|
||||
#===============================================================================
|
||||
# Superclass that handles moves using a non-existent function code.
|
||||
# Damaging moves just do damage with no additional effect.
|
||||
# Status moves always fail.
|
||||
#===============================================================================
|
||||
class PokeBattle_UnimplementedMove < PokeBattle_Move
|
||||
def pbMoveFailed?(user,targets)
|
||||
if statusMove?
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Pseudomove for confusion damage.
|
||||
#===============================================================================
|
||||
class PokeBattle_Confusion < PokeBattle_Move
|
||||
def initialize(battle,move)
|
||||
@battle = battle
|
||||
@realMove = move
|
||||
@id = 0
|
||||
@name = ""
|
||||
@function = "000"
|
||||
@baseDamage = 40
|
||||
@type = -1
|
||||
@category = 0
|
||||
@accuracy = 100
|
||||
@pp = -1
|
||||
@target = 0
|
||||
@priority = 0
|
||||
@flags = ""
|
||||
@addlEffect = 0
|
||||
@calcType = -1
|
||||
@powerBoost = false
|
||||
@snatched = false
|
||||
end
|
||||
|
||||
def physicalMove?(thisType=nil); return true; end
|
||||
def specialMove?(thisType=nil); return false; end
|
||||
def pbCritialOverride(user,target); return -1; end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Implements the move Struggle.
|
||||
# For cases where the real move named Struggle is not defined.
|
||||
#===============================================================================
|
||||
class PokeBattle_Struggle < PokeBattle_Move
|
||||
def initialize(battle,move)
|
||||
@battle = battle
|
||||
@realMove = nil # Not associated with a move
|
||||
@id = (move) ? move.id : -1 # Doesn't work if 0
|
||||
@name = (move) ? PBMoves.getName(@id) : _INTL("Struggle")
|
||||
@function = "002"
|
||||
@baseDamage = 50
|
||||
@type = -1
|
||||
@category = 0
|
||||
@accuracy = 0
|
||||
@pp = -1
|
||||
@target = 0
|
||||
@priority = 0
|
||||
@flags = ""
|
||||
@addlEffect = 0
|
||||
@calcType = -1
|
||||
@powerBoost = false
|
||||
@snatched = false
|
||||
end
|
||||
|
||||
def physicalMove?(thisType=nil); return true; end
|
||||
def specialMove?(thisType=nil); return false; end
|
||||
|
||||
def pbEffectAfterAllHits(user,target)
|
||||
return if target.damageState.unaffected
|
||||
user.pbReduceHP((user.totalhp/4.0).round,false)
|
||||
@battle.pbDisplay(_INTL("{1} is damaged by recoil!",user.pbThis))
|
||||
user.pbItemHPHealCheck
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Generic status problem-inflicting classes.
|
||||
#===============================================================================
|
||||
class PokeBattle_SleepMove < PokeBattle_Move
|
||||
def pbFailsAgainstTarget?(user,target)
|
||||
return false if damagingMove?
|
||||
return !target.pbCanSleep?(user,true,self)
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
target.pbSleep
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
target.pbSleep if target.pbCanSleep?(user,false,self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PokeBattle_PoisonMove < PokeBattle_Move
|
||||
def initialize(battle,move)
|
||||
super
|
||||
@toxic = false
|
||||
end
|
||||
|
||||
def pbFailsAgainstTarget?(user,target)
|
||||
return false if damagingMove?
|
||||
return !target.pbCanPoison?(user,true,self)
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
target.pbPoison(user,nil,@toxic)
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
target.pbPoison(user,nil,@toxic) if target.pbCanPoison?(user,false,self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PokeBattle_ParalysisMove < PokeBattle_Move
|
||||
def pbFailsAgainstTarget?(user,target)
|
||||
return false if damagingMove?
|
||||
return !target.pbCanParalyze?(user,true,self)
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
target.pbParalyze(user)
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
target.pbParalyze(user) if target.pbCanParalyze?(user,false,self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PokeBattle_BurnMove < PokeBattle_Move
|
||||
def pbFailsAgainstTarget?(user,target)
|
||||
return false if damagingMove?
|
||||
return !target.pbCanBurn?(user,true,self)
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
target.pbBurn(user)
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
target.pbBurn(user) if target.pbCanBurn?(user,false,self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PokeBattle_FreezeMove < PokeBattle_Move
|
||||
def pbFailsAgainstTarget?(user,target)
|
||||
return false if damagingMove?
|
||||
return !target.pbCanFreeze?(user,true,self)
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
target.pbFreeze
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
target.pbFreeze if target.pbCanFreeze?(user,false,self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Other problem-causing classes.
|
||||
#===============================================================================
|
||||
class PokeBattle_FlinchMove < PokeBattle_Move
|
||||
def flinchingMove?; return true; end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
target.pbFlinch(user)
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
target.pbFlinch(user)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PokeBattle_ConfuseMove < PokeBattle_Move
|
||||
def pbFailsAgainstTarget?(user,target)
|
||||
return false if damagingMove?
|
||||
return !target.pbCanConfuse?(user,true,self)
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
target.pbConfuse
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
return if !target.pbCanConfuse?(user,false,self)
|
||||
target.pbConfuse
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Generic user's stat increase/decrease classes.
|
||||
#===============================================================================
|
||||
class PokeBattle_StatUpMove < PokeBattle_Move
|
||||
def pbMoveFailed?(user,targets)
|
||||
return false if damagingMove?
|
||||
return !user.pbCanRaiseStatStage?(@statUp[0],user,self,true)
|
||||
end
|
||||
|
||||
def pbEffectGeneral(user)
|
||||
return if damagingMove?
|
||||
user.pbRaiseStatStage(@statUp[0],@statUp[1],user)
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
if user.pbCanRaiseStatStage?(@statUp[0],user,self)
|
||||
user.pbRaiseStatStage(@statUp[0],@statUp[1],user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PokeBattle_MultiStatUpMove < PokeBattle_Move
|
||||
def pbMoveFailed?(user,targets)
|
||||
return false if damagingMove?
|
||||
failed = true
|
||||
for i in 0...@statUp.length/2
|
||||
next if !user.pbCanRaiseStatStage?(@statUp[i*2],user,self)
|
||||
failed = false
|
||||
break
|
||||
end
|
||||
if failed
|
||||
@battle.pbDisplay(_INTL("{1}'s stats won't go any higher!",user.pbThis))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def pbEffectGeneral(user)
|
||||
return if damagingMove?
|
||||
showAnim = true
|
||||
for i in 0...@statUp.length/2
|
||||
next if !user.pbCanRaiseStatStage?(@statUp[i*2],user,self)
|
||||
if user.pbRaiseStatStage(@statUp[i*2],@statUp[i*2+1],user,showAnim)
|
||||
showAnim = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
showAnim = true
|
||||
for i in 0...@statUp.length/2
|
||||
next if !user.pbCanRaiseStatStage?(@statUp[i*2],user,self)
|
||||
if user.pbRaiseStatStage(@statUp[i*2],@statUp[i*2+1],user,showAnim)
|
||||
showAnim = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PokeBattle_StatDownMove < PokeBattle_Move
|
||||
def pbEffectWhenDealingDamage(user,target)
|
||||
return if @battle.pbAllFainted?(target.idxOwnSide)
|
||||
showAnim = true
|
||||
for i in 0...@statDown.length/2
|
||||
next if !user.pbCanLowerStatStage?(@statDown[i*2],user,self)
|
||||
if user.pbLowerStatStage(@statDown[i*2],@statDown[i*2+1],user,showAnim)
|
||||
showAnim = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Generic target's stat increase/decrease classes.
|
||||
#===============================================================================
|
||||
class PokeBattle_TargetStatDownMove < PokeBattle_Move
|
||||
def pbFailsAgainstTarget?(user,target)
|
||||
return false if damagingMove?
|
||||
return !target.pbCanLowerStatStage?(@statDown[0],user,self,true)
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
target.pbLowerStatStage(@statDown[0],@statDown[1],user)
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
return if !target.pbCanLowerStatStage?(@statDown[0],user,self)
|
||||
target.pbLowerStatStage(@statDown[0],@statDown[1],user)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
class PokeBattle_TargetMultiStatDownMove < PokeBattle_Move
|
||||
def pbFailsAgainstTarget?(user,target)
|
||||
return false if damagingMove?
|
||||
failed = true
|
||||
for i in 0...@statDown.length/2
|
||||
next if !target.pbCanLowerStatStage?(@statDown[i*2],user,self)
|
||||
failed = false
|
||||
break
|
||||
end
|
||||
if failed
|
||||
# NOTE: It's a bit of a faff to make sure the appropriate failure message
|
||||
# is shown here, I know.
|
||||
canLower = false
|
||||
if target.hasActiveAbility?(:CONTRARY) && !@battle.moldBreaker
|
||||
for i in 0...@statDown.length/2
|
||||
next if target.statStageAtMax?(@statDown[i*2])
|
||||
canLower = true
|
||||
break
|
||||
end
|
||||
@battle.pbDisplay(_INTL("{1}'s stats won't go any higher!",user.pbThis)) if !canLower
|
||||
else
|
||||
for i in 0...@statDown.length/2
|
||||
next if target.statStageAtMin?(@statDown[i*2])
|
||||
canLower = true
|
||||
break
|
||||
end
|
||||
@battle.pbDisplay(_INTL("{1}'s stats won't go any lower!",user.pbThis)) if !canLower
|
||||
end
|
||||
if canLower
|
||||
target.pbCanLowerStatStage?(@statDown[0],user,self,true)
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
return if damagingMove?
|
||||
showAnim = true
|
||||
for i in 0...@statDown.length/2
|
||||
next if !target.pbCanLowerStatStage?(@statDown[i*2],user,self)
|
||||
if target.pbLowerStatStage(@statDown[i*2],@statDown[i*2+1],user,showAnim)
|
||||
showAnim = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbAdditionalEffect(user,target)
|
||||
return if target.damageState.substitute
|
||||
showAnim = true
|
||||
for i in 0...@statDown.length/2
|
||||
next if !target.pbCanLowerStatStage?(@statDown[i*2],user,self)
|
||||
if target.pbLowerStatStage(@statDown[i*2],@statDown[i*2+1],user,showAnim)
|
||||
showAnim = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Fixed damage-inflicting move.
|
||||
#===============================================================================
|
||||
class PokeBattle_FixedDamageMove < PokeBattle_Move
|
||||
def pbFixedDamage(user,target); return 1; end
|
||||
|
||||
def pbCalcDamage(user,target,numTargets=1)
|
||||
target.damageState.critical = false
|
||||
target.damageState.calcDamage = pbFixedDamage(user,target)
|
||||
target.damageState.calcDamage = 1 if target.damageState.calcDamage<1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Two turn move.
|
||||
#===============================================================================
|
||||
class PokeBattle_TwoTurnMove < PokeBattle_Move
|
||||
def chargingTurnMove?; return true; end
|
||||
|
||||
# user.effects[PBEffects::TwoTurnAttack] is set to the move's ID if this
|
||||
# method returns true, or 0 if false.
|
||||
# Non-zero means the charging turn. 0 means the attacking turn.
|
||||
def pbIsChargingTurn?(user)
|
||||
@powerHerb = false
|
||||
@chargingTurn = false # Assume damaging turn by default
|
||||
@damagingTurn = true
|
||||
# 0 at start of charging turn, move's ID at start of damaging turn
|
||||
if user.effects[PBEffects::TwoTurnAttack]==0
|
||||
@powerHerb = user.hasActiveItem?(:POWERHERB)
|
||||
@chargingTurn = true
|
||||
@damagingTurn = @powerHerb
|
||||
end
|
||||
return !@damagingTurn # Deliberately not "return @chargingTurn"
|
||||
end
|
||||
|
||||
def pbDamagingMove? # Stops damage being dealt in the first (charging) turn
|
||||
return false if !@damagingTurn
|
||||
return super
|
||||
end
|
||||
|
||||
def pbAccuracyCheck(user,target)
|
||||
return true if !@damagingTurn
|
||||
return super
|
||||
end
|
||||
|
||||
def pbInitialEffect(user,targets,hitNum)
|
||||
pbChargingTurnMessage(user,targets) if @chargingTurn
|
||||
if @chargingTurn && @damagingTurn # Move only takes one turn to use
|
||||
pbShowAnimation(@id,user,targets,1) # Charging anim
|
||||
targets.each { |b| pbChargingTurnEffect(user,b) }
|
||||
if @powerHerb
|
||||
# Moves that would make the user semi-invulnerable will hide the user
|
||||
# after the charging animation, so the "UseItem" animation shouldn't show
|
||||
# for it
|
||||
if !["0C9","0CA","0CB","0CC","0CD","0CE","14D"].include?(@function)
|
||||
@battle.pbCommonAnimation("UseItem",user)
|
||||
end
|
||||
@battle.pbDisplay(_INTL("{1} became fully charged due to its Power Herb!",user.pbThis))
|
||||
user.pbConsumeItem
|
||||
end
|
||||
end
|
||||
pbAttackingTurnMessage(user,targets) if @damagingTurn
|
||||
end
|
||||
|
||||
def pbChargingTurnMessage(user,targets)
|
||||
@battle.pbDisplay(_INTL("{1} began charging up!",user.pbThis))
|
||||
end
|
||||
|
||||
def pbAttackingTurnMessage(user,targets)
|
||||
end
|
||||
|
||||
def pbChargingTurnEffect(user,target)
|
||||
# Skull Bash/Sky Drop are the only two-turn moves with an effect here, and
|
||||
# the latter just records the target is being Sky Dropped
|
||||
end
|
||||
|
||||
def pbAttackingTurnEffect(user,target)
|
||||
end
|
||||
|
||||
def pbEffectAgainstTarget(user,target)
|
||||
if @damagingTurn; pbAttackingTurnEffect(user,target)
|
||||
elsif @chargingTurn; pbChargingTurnEffect(user,target)
|
||||
end
|
||||
end
|
||||
|
||||
def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true)
|
||||
hitNum = 1 if @chargingTurn && !@damagingTurn # Charging anim
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Healing move.
|
||||
#===============================================================================
|
||||
class PokeBattle_HealingMove < PokeBattle_Move
|
||||
def healingMove?; return true; end
|
||||
def pbHealAmount(user); return 1; end
|
||||
|
||||
def pbMoveFailed?(user,targets)
|
||||
if user.hp==user.totalhp
|
||||
@battle.pbDisplay(_INTL("{1}'s HP is full!",user.pbThis))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def pbEffectGeneral(user)
|
||||
amt = pbHealAmount(user)
|
||||
user.pbRecoverHP(amt)
|
||||
@battle.pbDisplay(_INTL("{1}'s HP was restored.",user.pbThis))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Recoil move.
|
||||
#===============================================================================
|
||||
class PokeBattle_RecoilMove < PokeBattle_Move
|
||||
def recoilMove?; return true; end
|
||||
def pbRecoilDamage(user,target); return 1; end
|
||||
|
||||
def pbEffectAfterAllHits(user,target)
|
||||
return if target.damageState.unaffected
|
||||
return if !user.takesIndirectDamage?
|
||||
return if user.hasActiveAbility?(:ROCKHEAD)
|
||||
amt = pbRecoilDamage(user,target)
|
||||
amt = 1 if amt<1
|
||||
user.pbReduceHP(amt,false)
|
||||
@battle.pbDisplay(_INTL("{1} is damaged by recoil!",user.pbThis))
|
||||
user.pbItemHPHealCheck
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Protect move.
|
||||
#===============================================================================
|
||||
class PokeBattle_ProtectMove < PokeBattle_Move
|
||||
def initialize(battle,move)
|
||||
super
|
||||
@sidedEffect = false
|
||||
end
|
||||
|
||||
def pbChangeUsageCounters(user,specialUsage)
|
||||
oldVal = user.effects[PBEffects::ProtectRate]
|
||||
super
|
||||
user.effects[PBEffects::ProtectRate] = oldVal
|
||||
end
|
||||
|
||||
def pbMoveFailed?(user,targets)
|
||||
if @sidedEffect
|
||||
if user.pbOwnSide.effects[@effect]
|
||||
user.effects[PBEffects::ProtectRate] = 1
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
end
|
||||
elsif user.effects[@effect]
|
||||
user.effects[PBEffects::ProtectRate] = 1
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
end
|
||||
if !(@sidedEffect && NEWEST_BATTLE_MECHANICS) &&
|
||||
user.effects[PBEffects::ProtectRate]>1 &&
|
||||
@battle.pbRandom(user.effects[PBEffects::ProtectRate])!=0
|
||||
user.effects[PBEffects::ProtectRate] = 1
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
end
|
||||
if pbMoveFailedLastInRound?(user)
|
||||
user.effects[PBEffects::ProtectRate] = 1
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def pbEffectGeneral(user)
|
||||
if @sidedEffect
|
||||
user.pbOwnSide.effects[@effect] = true
|
||||
else
|
||||
user.effects[@effect] = true
|
||||
end
|
||||
user.effects[PBEffects::ProtectRate] *= (NEWEST_BATTLE_MECHANICS) ? 3 : 2
|
||||
pbProtectMessage(user)
|
||||
end
|
||||
|
||||
def pbProtectMessage(user)
|
||||
if @sidedEffect
|
||||
@battle.pbDisplay(_INTL("{1} protected {2}!",@name,user.pbTeam(true)))
|
||||
else
|
||||
@battle.pbDisplay(_INTL("{1} protected itself!",user.pbThis))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Weather-inducing move.
|
||||
#===============================================================================
|
||||
class PokeBattle_WeatherMove < PokeBattle_Move
|
||||
def initialize(battle,move)
|
||||
super
|
||||
@weatherType = PBWeather::None
|
||||
end
|
||||
def pbMoveFailed?(user,targets)
|
||||
case @battle.field.weather
|
||||
when PBWeather::HarshSun
|
||||
@battle.pbDisplay(_INTL("The extremely harsh sunlight was not lessened at all!"))
|
||||
return true
|
||||
when PBWeather::HeavyRain
|
||||
@battle.pbDisplay(_INTL("There is no relief from this heavy rain!"))
|
||||
return true
|
||||
when PBWeather::StrongWinds
|
||||
@battle.pbDisplay(_INTL("The mysterious air current blows on regardless!"))
|
||||
return true
|
||||
when @weatherType
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
def pbEffectGeneral(user)
|
||||
@battle.pbStartWeather(user,@weatherType,true,false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# Pledge move.
|
||||
#===============================================================================
|
||||
class PokeBattle_PledgeMove < PokeBattle_Move
|
||||
def pbOnStartUse(user,targets)
|
||||
@pledgeSetup = false; @pledgeCombo = false; @pledgeOtherUser = nil
|
||||
@comboEffect = nil; @overrideType = nil; @overrideAnim = nil
|
||||
# Check whether this is the use of a combo move
|
||||
@combos.each do |i|
|
||||
next if i[0]!=user.effects[PBEffects::FirstPledge]
|
||||
@battle.pbDisplay(_INTL("The two moves have become one! It's a combined move!"))
|
||||
@pledgeCombo = true
|
||||
@comboEffect = i[1]; @overrideType = i[2]; @overrideAnim = i[3]
|
||||
break
|
||||
end
|
||||
return if @pledgeCombo
|
||||
# Check whether this is the setup of a combo move
|
||||
user.eachAlly do |b|
|
||||
next if @battle.choices[b.index][0]!=:UseMove || b.movedThisRound?
|
||||
move = @battle.choices[b.index][2]
|
||||
next if !move || move.id<=0
|
||||
@combos.each do |i|
|
||||
next if i[0]!=move.function
|
||||
@pledgeSetup = true
|
||||
@pledgeOtherUser = b
|
||||
break
|
||||
end
|
||||
break if @pledgeSetup
|
||||
end
|
||||
end
|
||||
|
||||
def pbDamagingMove?
|
||||
return false if @pledgeSetup
|
||||
return super
|
||||
end
|
||||
|
||||
def pbBaseType(user)
|
||||
return @overrideType if @overrideType!=nil
|
||||
return super
|
||||
end
|
||||
|
||||
def pbBaseDamage(baseDmg,user,target)
|
||||
baseDmg *= 2 if @pledgeCombo
|
||||
return baseDmg
|
||||
end
|
||||
|
||||
def pbEffectGeneral(user)
|
||||
user.effects[PBEffects::FirstPledge] = 0
|
||||
return if !@pledgeSetup
|
||||
@battle.pbDisplay(_INTL("{1} is waiting for {2}'s move...",
|
||||
user.pbThis,@pledgeOtherUser.pbThis(true)))
|
||||
@pledgeOtherUser.effects[PBEffects::FirstPledge] = @function
|
||||
@pledgeOtherUser.effects[PBEffects::MoveNext] = true
|
||||
user.lastMoveFailed = true # Treated as a failure for Stomping Tantrum
|
||||
end
|
||||
|
||||
def pbEffectAfterAllHits(user,target)
|
||||
return if !@pledgeCombo
|
||||
msg = nil; animName = nil
|
||||
case @comboEffect
|
||||
when :SeaOfFire # Grass + Fire
|
||||
if user.pbOpposingSide.effects[PBEffects::SeaOfFire]==0
|
||||
user.pbOpposingSide.effects[PBEffects::SeaOfFire] = 4
|
||||
msg = _INTL("A sea of fire enveloped {1}!",user.pbOpposingTeam(true))
|
||||
animName = (user.opposes?) ? "SeaOfFire" : "SeaOfFireOpp"
|
||||
end
|
||||
when :Rainbow # Fire + Water
|
||||
if user.pbOwnSide.effects[PBEffects::Rainbow]==0
|
||||
user.pbOwnSide.effects[PBEffects::Rainbow] = 4
|
||||
msg = _INTL("A rainbow appeared in the sky on {1}'s side!",user.pbTeam(true))
|
||||
animName = (user.opposes?) ? "RainbowOpp" : "Rainbow"
|
||||
end
|
||||
when :Swamp # Water + Grass
|
||||
if user.pbOpposingSide.effects[PBEffects::Swamp]==0
|
||||
user.pbOpposingSide.effects[PBEffects::Swamp] = 4
|
||||
msg = _INTL("A swamp enveloped {1}!",user.pbOpposingTeam(true))
|
||||
animName = (user.opposes?) ? "Swamp" : "SwampOpp"
|
||||
end
|
||||
end
|
||||
@battle.pbDisplay(msg) if msg
|
||||
@battle.pbCommonAnimation(animName) if animName
|
||||
end
|
||||
|
||||
def pbShowAnimation(id,user,targets,hitNum=0,showAnimation=true)
|
||||
return if @pledgeSetup # No animation for setting up
|
||||
id = @overrideAnim if @overrideAnim!=nil
|
||||
return super
|
||||
end
|
||||
end
|
||||
2924
Data/Scripts/011_Battle/002_Move/005_Move_Effects_000-07F.rb
Normal file
2924
Data/Scripts/011_Battle/002_Move/005_Move_Effects_000-07F.rb
Normal file
File diff suppressed because it is too large
Load Diff
3746
Data/Scripts/011_Battle/002_Move/006_Move_Effects_080-0FF.rb
Normal file
3746
Data/Scripts/011_Battle/002_Move/006_Move_Effects_080-0FF.rb
Normal file
File diff suppressed because it is too large
Load Diff
2626
Data/Scripts/011_Battle/002_Move/007_Move_Effects_100-17F.rb
Normal file
2626
Data/Scripts/011_Battle/002_Move/007_Move_Effects_100-17F.rb
Normal file
File diff suppressed because it is too large
Load Diff
32
Data/Scripts/011_Battle/002_PBWeather.rb
Normal file
32
Data/Scripts/011_Battle/002_PBWeather.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
begin
|
||||
module PBWeather
|
||||
None = 0
|
||||
Sun = 1
|
||||
Rain = 2
|
||||
Sandstorm = 3
|
||||
Hail = 4
|
||||
HarshSun = 5
|
||||
HeavyRain = 6
|
||||
StrongWinds = 7
|
||||
ShadowSky = 8
|
||||
|
||||
def self.animationName(weather)
|
||||
case weather
|
||||
when Sun; return "Sun"
|
||||
when Rain; return "Rain"
|
||||
when Sandstorm; return "Sandstorm"
|
||||
when Hail; return "Hail"
|
||||
when HarshSun; return "HarshSun"
|
||||
when HeavyRain; return "HeavyRain"
|
||||
when StrongWinds; return "StrongWinds"
|
||||
when ShadowSky; return "ShadowSky"
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
rescue Exception
|
||||
if $!.is_a?(SystemExit) || "#{$!.class}"=="Reset"
|
||||
raise $!
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
781
Data/Scripts/011_Battle/003_Battle/002_PokeBattle_Battle.rb
Normal file
781
Data/Scripts/011_Battle/003_Battle/002_PokeBattle_Battle.rb
Normal file
@@ -0,0 +1,781 @@
|
||||
# Results of battle:
|
||||
# 0 - Undecided or aborted
|
||||
# 1 - Player won
|
||||
# 2 - Player lost
|
||||
# 3 - Player or wild Pokémon ran from battle, or player forfeited the match
|
||||
# 4 - Wild Pokémon was caught
|
||||
# 5 - Draw
|
||||
# Possible actions a battler can take in a round:
|
||||
# :None
|
||||
# :UseMove
|
||||
# :SwitchOut
|
||||
# :UseItem
|
||||
# :Call
|
||||
# :Run
|
||||
# :Shift
|
||||
# NOTE: If you want to have more than 3 Pokémon on a side at once, you will need
|
||||
# to edit some code. Mainly this is to change/add coordinates for the
|
||||
# sprites, describe the relationships between Pokémon and trainers, and to
|
||||
# change messages. The methods that will need editing are as follows:
|
||||
# class PokeBattle_Battle
|
||||
# def setBattleMode
|
||||
# def pbGetOwnerIndexFromBattlerIndex
|
||||
# def pbGetOpposingIndicesInOrder
|
||||
# def nearBattlers?
|
||||
# def pbStartBattleSendOut
|
||||
# def pbEORShiftDistantBattlers
|
||||
# def pbCanShift?
|
||||
# def pbEndOfRoundPhase
|
||||
# class TargetMenuDisplay
|
||||
# def initialize
|
||||
# class PokemonDataBox
|
||||
# def initializeDataBoxGraphic
|
||||
# module PokeBattle_SceneConstants
|
||||
# def self.pbBattlerPosition
|
||||
# def self.pbTrainerPosition
|
||||
# class PokemonTemp
|
||||
# def recordBattleRule
|
||||
# (There is no guarantee that this list is complete.)
|
||||
|
||||
class PokeBattle_Battle
|
||||
attr_reader :scene # Scene object for this battle
|
||||
attr_reader :peer
|
||||
attr_reader :field # Effects common to the whole of a battle
|
||||
attr_reader :sides # Effects common to each side of a battle
|
||||
attr_reader :positions # Effects that apply to a battler position
|
||||
attr_reader :battlers # Currently active Pokémon
|
||||
attr_reader :sideSizes # Array of number of battlers per side
|
||||
attr_accessor :backdrop # Filename fragment used for background graphics
|
||||
attr_accessor :backdropBase # Filename fragment used for base graphics
|
||||
attr_accessor :time # Time of day (0=day, 1=eve, 2=night)
|
||||
attr_accessor :environment # Battle surroundings (for mechanics purposes)
|
||||
attr_reader :turnCount
|
||||
attr_accessor :decision # Decision: 0=undecided; 1=win; 2=loss; 3=escaped; 4=caught
|
||||
attr_reader :player # Player trainer (or array of trainers)
|
||||
attr_reader :opponent # Opponent trainer (or array of trainers)
|
||||
attr_accessor :items # Items held by opponents
|
||||
attr_accessor :endSpeeches
|
||||
attr_accessor :endSpeechesWin
|
||||
attr_accessor :party1starts # Array of start indexes for each player-side trainer's party
|
||||
attr_accessor :party2starts # Array of start indexes for each opponent-side trainer's party
|
||||
attr_accessor :internalBattle # Internal battle flag
|
||||
attr_accessor :debug # Debug flag
|
||||
attr_accessor :canRun # True if player can run from battle
|
||||
attr_accessor :canLose # True if player won't black out if they lose
|
||||
attr_accessor :switchStyle # Switch/Set "battle style" option
|
||||
attr_accessor :showAnims # "Battle Effects" option
|
||||
attr_accessor :controlPlayer # Whether player's Pokémon are AI controlled
|
||||
attr_accessor :expGain # Whether Pokémon can gain Exp/EVs
|
||||
attr_accessor :moneyGain # Whether the player can gain/lose money
|
||||
attr_accessor :rules
|
||||
attr_accessor :choices # Choices made by each Pokémon this round
|
||||
attr_accessor :megaEvolution # Battle index of each trainer's Pokémon to Mega Evolve
|
||||
attr_reader :initialItems
|
||||
attr_reader :recycleItems
|
||||
attr_reader :belch
|
||||
attr_reader :battleBond
|
||||
attr_reader :usedInBattle # Whether each Pokémon was used in battle (for Burmy)
|
||||
attr_reader :successStates # Success states
|
||||
attr_accessor :lastMoveUsed # Last move used
|
||||
attr_accessor :lastMoveUser # Last move user
|
||||
attr_reader :switching # True if during the switching phase of the round
|
||||
attr_reader :futureSight # True if Future Sight is hitting
|
||||
attr_reader :endOfRound # True during the end of round
|
||||
attr_accessor :moldBreaker # True if Mold Breaker applies
|
||||
attr_reader :struggle # The Struggle move
|
||||
|
||||
include PokeBattle_BattleCommon
|
||||
|
||||
def pbRandom(x); return rand(x); end
|
||||
|
||||
#=============================================================================
|
||||
# Creating the battle class
|
||||
#=============================================================================
|
||||
def initialize(scene,p1,p2,player,opponent)
|
||||
if p1.length==0
|
||||
raise ArgumentError.new(_INTL("Party 1 has no Pokémon."))
|
||||
elsif p2.length==0
|
||||
raise ArgumentError.new(_INTL("Party 2 has no Pokémon."))
|
||||
end
|
||||
@scene = scene
|
||||
@peer = PokeBattle_BattlePeer.create
|
||||
@battleAI = PokeBattle_AI.new(self)
|
||||
@field = PokeBattle_ActiveField.new # Whole field (gravity/rooms)
|
||||
@sides = [PokeBattle_ActiveSide.new, # Player's side
|
||||
PokeBattle_ActiveSide.new] # Foe's side
|
||||
@positions = [] # Battler positions
|
||||
@battlers = []
|
||||
@sideSizes = [1,1] # Single battle, 1v1
|
||||
@backdrop = ""
|
||||
@backdropBase = nil
|
||||
@time = 0
|
||||
@environment = PBEnvironment::None # e.g. Tall grass, cave, still water
|
||||
@turnCount = 0
|
||||
@decision = 0
|
||||
@caughtPokemon = []
|
||||
player = [player] if !player.nil? && !player.is_a?(Array)
|
||||
opponent = [opponent] if !opponent.nil? && !opponent.is_a?(Array)
|
||||
@player = player # Array of PokeBattle_Trainer objects, or nil
|
||||
@opponent = opponent # Array of PokeBattle_Trainer objects, or nil
|
||||
@items = nil
|
||||
@endSpeeches = []
|
||||
@endSpeechesWin = []
|
||||
@party1 = p1
|
||||
@party2 = p2
|
||||
@party1order = Array.new(@party1.length) { |i| i }
|
||||
@party2order = Array.new(@party2.length) { |i| i }
|
||||
@party1starts = [0]
|
||||
@party2starts = [0]
|
||||
@internalBattle = true
|
||||
@debug = false
|
||||
@canRun = true
|
||||
@canLose = false
|
||||
@switchStyle = true
|
||||
@showAnims = true
|
||||
@controlPlayer = false
|
||||
@expGain = true
|
||||
@moneyGain = true
|
||||
@rules = {}
|
||||
@priority = []
|
||||
@priorityTrickRoom = false
|
||||
@choices = []
|
||||
@megaEvolution = [
|
||||
[-1] * (@player ? @player.length : 1),
|
||||
[-1] * (@opponent ? @opponent.length : 1)
|
||||
]
|
||||
@initialItems = [
|
||||
Array.new(@party1.length) { |i| (@party1[i]) ? @party1[i].item : 0 },
|
||||
Array.new(@party2.length) { |i| (@party2[i]) ? @party2[i].item : 0 }
|
||||
]
|
||||
@recycleItems = [Array.new(@party1.length,0),Array.new(@party2.length,0)]
|
||||
@belch = [Array.new(@party1.length,false),Array.new(@party2.length,false)]
|
||||
@battleBond = [Array.new(@party1.length,false),Array.new(@party2.length,false)]
|
||||
@usedInBattle = [Array.new(@party1.length,false),Array.new(@party2.length,false)]
|
||||
@successStates = []
|
||||
@lastMoveUsed = -1
|
||||
@lastMoveUser = -1
|
||||
@switching = false
|
||||
@futureSight = false
|
||||
@endOfRound = false
|
||||
@moldBreaker = false
|
||||
@runCommand = 0
|
||||
@nextPickupUse = 0
|
||||
if hasConst?(PBMoves,:STRUGGLE)
|
||||
@struggle = PokeBattle_Move.pbFromPBMove(self,PBMove.new(getConst(PBMoves,:STRUGGLE)))
|
||||
else
|
||||
@struggle = PokeBattle_Struggle.new(self,nil)
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Information about the type and size of the battle
|
||||
#=============================================================================
|
||||
def wildBattle?; return @opponent.nil?; end
|
||||
def trainerBattle?; return !@opponent.nil?; end
|
||||
|
||||
# Sets the number of battler slots on each side of the field independently.
|
||||
# For "1v2" names, the first number is for the player's side and the second
|
||||
# number is for the opposing side.
|
||||
def setBattleMode(mode)
|
||||
@sideSizes =
|
||||
case mode
|
||||
when "triple", "3v3"; [3,3]
|
||||
when "3v2"; [3,2]
|
||||
when "3v1"; [3,1]
|
||||
when "2v3"; [2,3]
|
||||
when "double", "2v2"; [2,2]
|
||||
when "2v1"; [2,1]
|
||||
when "1v3"; [1,3]
|
||||
when "1v2"; [1,2]
|
||||
else; [1,1] # Single, 1v1 (default)
|
||||
end
|
||||
end
|
||||
|
||||
def singleBattle?
|
||||
return pbSideSize(0)==1 && pbSideSize(1)==1
|
||||
end
|
||||
|
||||
def pbSideSize(index)
|
||||
return @sideSizes[index%2]
|
||||
end
|
||||
|
||||
def maxBattlerIndex
|
||||
return (pbSideSize(0)>pbSideSize(1)) ? (pbSideSize(0)-1)*2 : pbSideSize(1)*2-1
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Trainers and owner-related methods
|
||||
#=============================================================================
|
||||
def pbPlayer; return @player[0]; end
|
||||
|
||||
# Given a battler index, returns the index within @player/@opponent of the
|
||||
# trainer that controls that battler index.
|
||||
# NOTE: You shouldn't ever have more trainers on a side than there are battler
|
||||
# positions on that side. This method doesn't account for if you do.
|
||||
def pbGetOwnerIndexFromBattlerIndex(idxBattler)
|
||||
trainer = (opposes?(idxBattler)) ? @opponent : @player
|
||||
return 0 if !trainer
|
||||
case trainer.length
|
||||
when 2
|
||||
n = pbSideSize(idxBattler%2)
|
||||
return [0,0,1][idxBattler/2] if n==3
|
||||
return idxBattler/2 # Same as [0,1][idxBattler/2], i.e. 2 battler slots
|
||||
when 3; return idxBattler/2
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
def pbGetOwnerFromBattlerIndex(idxBattler)
|
||||
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
|
||||
return (opposes?(idxBattler)) ? @opponent[idxTrainer] : @player[idxTrainer]
|
||||
end
|
||||
|
||||
def pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
|
||||
ret = -1
|
||||
pbPartyStarts(idxBattler).each_with_index do |start,i|
|
||||
break if start>idxParty
|
||||
ret = i
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
# Only used for the purpose of an error message when one trainer tries to
|
||||
# switch another trainer's Pokémon.
|
||||
def pbGetOwnerFromPartyIndex(idxBattler,idxParty)
|
||||
idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
|
||||
return (opposes?(idxBattler)) ? @opponent[idxTrainer] : @player[idxTrainer]
|
||||
end
|
||||
|
||||
def pbGetOwnerName(idxBattler)
|
||||
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
|
||||
return @opponent[idxTrainer].fullname if opposes?(idxBattler) # Opponent
|
||||
return @player[idxTrainer].fullname if idxTrainer>0 # Ally trainer
|
||||
return @player[idxTrainer].name # Player
|
||||
end
|
||||
|
||||
def pbGetOwnerItems(idxBattler)
|
||||
return [] if !@items || !opposes?(idxBattler)
|
||||
return @items[pbGetOwnerIndexFromBattlerIndex(idxBattler)]
|
||||
end
|
||||
|
||||
# Returns whether the battler in position idxBattler is owned by the same
|
||||
# trainer that owns the Pokémon in party slot idxParty. This assumes that
|
||||
# both the battler position and the party slot are from the same side.
|
||||
def pbIsOwner?(idxBattler,idxParty)
|
||||
idxTrainer1 = pbGetOwnerIndexFromBattlerIndex(idxBattler)
|
||||
idxTrainer2 = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
|
||||
return idxTrainer1==idxTrainer2
|
||||
end
|
||||
|
||||
def pbOwnedByPlayer?(idxBattler)
|
||||
return false if opposes?(idxBattler)
|
||||
return pbGetOwnerIndexFromBattlerIndex(idxBattler)==0
|
||||
end
|
||||
|
||||
# Returns the number of Pokémon positions controlled by the given trainerIndex
|
||||
# on the given side of battle.
|
||||
def pbNumPositions(side,idxTrainer)
|
||||
ret = 0
|
||||
for i in 0...pbSideSize(side)
|
||||
t = pbGetOwnerIndexFromBattlerIndex(i*2+side)
|
||||
next if t!=idxTrainer
|
||||
ret += 1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Get party information (counts all teams on the same side)
|
||||
#=============================================================================
|
||||
def pbParty(idxBattler)
|
||||
return (opposes?(idxBattler)) ? @party2 : @party1
|
||||
end
|
||||
|
||||
def pbOpposingParty(idxBattler)
|
||||
return (opposes?(idxBattler)) ? @party1 : @party2
|
||||
end
|
||||
|
||||
def pbPartyOrder(idxBattler)
|
||||
return (opposes?(idxBattler)) ? @party2order : @party1order
|
||||
end
|
||||
|
||||
def pbPartyStarts(idxBattler)
|
||||
return (opposes?(idxBattler)) ? @party2starts : @party1starts
|
||||
end
|
||||
|
||||
# Returns the player's team in its display order. Used when showing the party
|
||||
# screen.
|
||||
def pbPlayerDisplayParty(idxBattler=0)
|
||||
partyOrders = pbPartyOrder(idxBattler)
|
||||
idxStart, idxEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
||||
ret = []
|
||||
eachInTeamFromBattlerIndex(idxBattler) { |pkmn,i| ret[partyOrders[i]-idxStart] = pkmn }
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbAbleCount(idxBattler=0)
|
||||
party = pbParty(idxBattler)
|
||||
count = 0
|
||||
party.each { |pkmn| count += 1 if pkmn && pkmn.able? }
|
||||
return count
|
||||
end
|
||||
|
||||
def pbAbleNonActiveCount(idxBattler=0)
|
||||
party = pbParty(idxBattler)
|
||||
inBattleIndices = []
|
||||
eachSameSideBattler(idxBattler) { |b| inBattleIndices.push(b.pokemonIndex) }
|
||||
count = 0
|
||||
party.each_with_index do |pkmn,idxParty|
|
||||
next if !pkmn || !pkmn.able?
|
||||
next if inBattleIndices.include?(idxParty)
|
||||
count += 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
def pbAllFainted?(idxBattler=0)
|
||||
return pbAbleCount(idxBattler)==0
|
||||
end
|
||||
|
||||
# For the given side of the field (0=player's, 1=opponent's), returns an array
|
||||
# containing the number of able Pokémon in each team.
|
||||
def pbAbleTeamCounts(side)
|
||||
party = pbParty(side)
|
||||
partyStarts = pbPartyStarts(side)
|
||||
ret = []
|
||||
idxTeam = -1
|
||||
nextStart = 0
|
||||
party.each_with_index do |pkmn,i|
|
||||
if i>=nextStart
|
||||
idxTeam += 1
|
||||
nextStart = (idxTeam<partyStarts.length-1) ? partyStarts[idxTeam+1] : party.length
|
||||
end
|
||||
next if !pkmn || !pkmn.able?
|
||||
ret[idxTeam] = 0 if !ret[idxTeam]
|
||||
ret[idxTeam] += 1
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Get team information (a team is only the Pokémon owned by a particular
|
||||
# trainer)
|
||||
#=============================================================================
|
||||
def pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
||||
partyStarts = pbPartyStarts(idxBattler)
|
||||
idxTrainer = pbGetOwnerIndexFromBattlerIndex(idxBattler)
|
||||
idxPartyStart = partyStarts[idxTrainer]
|
||||
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : pbParty(idxBattler).length
|
||||
return idxPartyStart, idxPartyEnd
|
||||
end
|
||||
|
||||
def pbTeamLengthFromBattlerIndex(idxBattler)
|
||||
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
||||
return idxPartyEnd-idxPartyStart
|
||||
end
|
||||
|
||||
def eachInTeamFromBattlerIndex(idxBattler)
|
||||
party = pbParty(idxBattler)
|
||||
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
||||
party.each_with_index { |pkmn,i| yield pkmn,i if pkmn && i>=idxPartyStart && i<idxPartyEnd }
|
||||
end
|
||||
|
||||
def eachInTeam(side,idxTrainer)
|
||||
party = pbParty(side)
|
||||
partyStarts = pbPartyStarts(side)
|
||||
idxPartyStart = partyStarts[idxTrainer]
|
||||
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : party.length
|
||||
party.each_with_index { |pkmn,i| yield pkmn,i if pkmn && i>=idxPartyStart && i<idxPartyEnd }
|
||||
end
|
||||
|
||||
# Used for Illusion.
|
||||
# NOTE: This cares about the temporary rearranged order of the team. That is,
|
||||
# if you do some switching, the last Pokémon in the team could change
|
||||
# and the Illusion could be a different Pokémon.
|
||||
def pbLastInTeam(idxBattler)
|
||||
party = pbParty(idxBattler)
|
||||
partyOrders = pbPartyOrder(idxBattler)
|
||||
idxPartyStart, idxPartyEnd = pbTeamIndexRangeFromBattlerIndex(idxBattler)
|
||||
ret = -1
|
||||
party.each_with_index do |pkmn,i|
|
||||
next if i<idxPartyStart || i>=idxPartyEnd # Check the team only
|
||||
next if !pkmn || !pkmn.able? # Can't copy a non-fainted Pokémon or egg
|
||||
ret = i if partyOrders[i]>partyOrders[ret]
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
# Used to calculate money gained/lost after winning/losing a battle.
|
||||
def pbMaxLevelInTeam(side,idxTrainer)
|
||||
ret = 1
|
||||
eachInTeam(side,idxTrainer) do |pkmn,i|
|
||||
ret = pkmn.level if pkmn.level>ret
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Iterate through battlers
|
||||
#=============================================================================
|
||||
def eachBattler
|
||||
@battlers.each { |b| yield b if b && !b.fainted? }
|
||||
end
|
||||
|
||||
def eachSameSideBattler(idxBattler=0)
|
||||
idxBattler = idxBattler.index if idxBattler.respond_to?("index")
|
||||
@battlers.each { |b| yield b if b && !b.fainted? && !b.opposes?(idxBattler) }
|
||||
end
|
||||
|
||||
def eachOtherSideBattler(idxBattler=0)
|
||||
idxBattler = idxBattler.index if idxBattler.respond_to?("index")
|
||||
@battlers.each { |b| yield b if b && !b.fainted? && b.opposes?(idxBattler) }
|
||||
end
|
||||
|
||||
def pbSideBattlerCount(idxBattler=0)
|
||||
ret = 0
|
||||
eachSameSideBattler(idxBattler) { |b| ret += 1 }
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbOpposingBattlerCount(idxBattler=0)
|
||||
ret = 0
|
||||
eachOtherSideBattler(idxBattler) { |b| ret += 1 }
|
||||
return ret
|
||||
end
|
||||
|
||||
# This method only counts the player's Pokémon, not a partner trainer's.
|
||||
def pbPlayerBattlerCount
|
||||
ret = 0
|
||||
eachSameSideBattler { |b| ret += 1 if b.pbOwnedByPlayer? }
|
||||
return ret
|
||||
end
|
||||
|
||||
def pbCheckGlobalAbility(abil)
|
||||
eachBattler { |b| return b if b.hasActiveAbility?(abil) }
|
||||
return nil
|
||||
end
|
||||
|
||||
def pbCheckOpposingAbility(abil,idxBattler=0,nearOnly=false)
|
||||
eachOtherSideBattler(idxBattler) do |b|
|
||||
next if nearOnly && !b.near?(idxBattler)
|
||||
return b if b.hasActiveAbility?(abil)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
# Given a battler index, and using battle side sizes, returns an array of
|
||||
# battler indices from the opposing side that are in order of most "opposite".
|
||||
# Used when choosing a target and pressing up/down to move the cursor to the
|
||||
# opposite side, and also when deciding which target to select first for some
|
||||
# moves.
|
||||
def pbGetOpposingIndicesInOrder(idxBattler)
|
||||
case pbSideSize(0)
|
||||
when 1
|
||||
case pbSideSize(1)
|
||||
when 1 # 1v1 single
|
||||
return [0] if opposes?(idxBattler)
|
||||
return [1]
|
||||
when 2 # 1v2
|
||||
return [0] if opposes?(idxBattler)
|
||||
return [3,1]
|
||||
when 3 # 1v3
|
||||
return [0] if opposes?(idxBattler)
|
||||
return [3,5,1]
|
||||
end
|
||||
when 2
|
||||
case pbSideSize(1)
|
||||
when 1 # 2v1
|
||||
return [0,2] if opposes?(idxBattler)
|
||||
return [1]
|
||||
when 2 # 2v2 double
|
||||
return [[3,1],[2,0],[1,3],[0,2]][idxBattler]
|
||||
when 3 # 2v3
|
||||
return [[5,3,1],[2,0],[3,1,5]][idxBattler] if idxBattler<3
|
||||
return [0,2]
|
||||
end
|
||||
when 3
|
||||
case pbSideSize(1)
|
||||
when 1 # 3v1
|
||||
return [2,0,4] if opposes?(idxBattler)
|
||||
return [1]
|
||||
when 2 # 3v2
|
||||
return [[3,1],[2,4,0],[3,1],[2,0,4],[1,3]][idxBattler]
|
||||
when 3 # 3v3 triple
|
||||
return [[5,3,1],[4,2,0],[3,5,1],[2,0,4],[1,3,5],[0,2,4]][idxBattler]
|
||||
end
|
||||
end
|
||||
return [idxBattler]
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Comparing the positions of two battlers
|
||||
#=============================================================================
|
||||
def opposes?(idxBattler1,idxBattler2=0)
|
||||
idxBattler1 = idxBattler1.index if idxBattler1.respond_to?("index")
|
||||
idxBattler2 = idxBattler2.index if idxBattler2.respond_to?("index")
|
||||
return (idxBattler1&1)!=(idxBattler2&1)
|
||||
end
|
||||
|
||||
def nearBattlers?(idxBattler1,idxBattler2)
|
||||
return false if idxBattler1==idxBattler2
|
||||
return true if pbSideSize(0)<=2 && pbSideSize(1)<=2
|
||||
# Get all pairs of battler positions that are not close to each other
|
||||
pairsArray = [[0,4],[1,5]] # Covers 3v1 and 1v3
|
||||
case pbSideSize(0)
|
||||
when 3
|
||||
case pbSideSize(1)
|
||||
when 3 # 3v3 (triple)
|
||||
pairsArray.push([0,1])
|
||||
pairsArray.push([4,5])
|
||||
when 2 # 3v2
|
||||
pairsArray.push([0,1])
|
||||
pairsArray.push([3,4])
|
||||
end
|
||||
when 2 # 2v3
|
||||
pairsArray.push([0,1])
|
||||
pairsArray.push([2,5])
|
||||
end
|
||||
# See if any pair matches the two battlers being assessed
|
||||
pairsArray.each do |pair|
|
||||
return false if pair.include?(idxBattler1) && pair.include?(idxBattler2)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Altering a party or rearranging battlers
|
||||
#=============================================================================
|
||||
def pbRemoveFromParty(idxBattler,idxParty)
|
||||
party = pbParty(idxBattler)
|
||||
# Erase the Pokémon from the party
|
||||
party[idxParty] = nil
|
||||
# Rearrange the display order of the team to place the erased Pokémon last
|
||||
# in it (to avoid gaps)
|
||||
partyOrders = pbPartyOrder(idxBattler)
|
||||
partyStarts = pbPartyStarts(idxBattler)
|
||||
idxTrainer = pbGetOwnerIndexFromPartyIndex(idxBattler,idxParty)
|
||||
idxPartyStart = partyStarts[idxTrainer]
|
||||
idxPartyEnd = (idxTrainer<partyStarts.length-1) ? partyStarts[idxTrainer+1] : party.length
|
||||
origPartyPos = partyOrders[idxParty] # Position of erased Pokémon initially
|
||||
partyOrders[idxParty] = idxPartyEnd # Put erased Pokémon last in the team
|
||||
party.each_with_index do |pkmn,i|
|
||||
next if i<idxPartyStart || i>=idxPartyEnd # Only check the team
|
||||
next if partyOrders[i]<origPartyPos # Appeared before erased Pokémon
|
||||
partyOrders[i] -= 1 # Appeared after erased Pokémon; bump it up by 1
|
||||
end
|
||||
end
|
||||
|
||||
def pbSwapBattlers(idxA,idxB)
|
||||
return false if !@battlers[idxA] || !@battlers[idxB]
|
||||
# Can't swap if battlers aren't owned by the same trainer
|
||||
return false if opposes?(idxA,idxB)
|
||||
return false if pbGetOwnerIndexFromBattlerIndex(idxA)!=pbGetOwnerIndexFromBattlerIndex(idxB)
|
||||
@battlers[idxA], @battlers[idxB] = @battlers[idxB], @battlers[idxA]
|
||||
@battlers[idxA].index, @battlers[idxB].index = @battlers[idxB].index, @battlers[idxA].index
|
||||
@choices[idxA], @choices[idxB] = @choices[idxB], @choices[idxA]
|
||||
@scene.pbSwapBattlerSprites(idxA,idxB)
|
||||
# Swap the target of any battlers' effects that point at either of the
|
||||
# swapped battlers, to ensure they still point at the correct target
|
||||
# NOTE: LeechSeed is not swapped, because drained HP goes to whichever
|
||||
# Pokémon is in the position that Leech Seed was used from.
|
||||
# NOTE: PerishSongUser doesn't need to change, as it's only used to
|
||||
# determine which side the Perish Song user was on, and a battler
|
||||
# can't change sides.
|
||||
effectsToSwap = [PBEffects::Attract,
|
||||
PBEffects::BideTarget,
|
||||
PBEffects::CounterTarget,
|
||||
PBEffects::LockOnPos,
|
||||
PBEffects::MeanLook,
|
||||
PBEffects::MirrorCoatTarget,
|
||||
PBEffects::SkyDrop,
|
||||
PBEffects::TrappingUser]
|
||||
eachBattler do |b|
|
||||
for i in effectsToSwap
|
||||
next if b.effects[i]!=idxA && b.effects[i]!=idxB
|
||||
b.effects[i] = (b.effects[i]==idxA) ? idxB : idxA
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
#
|
||||
#=============================================================================
|
||||
# Returns the battler representing the Pokémon at index idxParty in its party,
|
||||
# on the same side as a battler with battler index of idxBattlerOther.
|
||||
def pbFindBattler(idxParty,idxBattlerOther=0)
|
||||
eachSameSideBattler(idxBattlerOther) { |b| return b if b.pokemonIndex==idxParty }
|
||||
return nil
|
||||
end
|
||||
|
||||
# Only used for Wish, as the Wishing Pokémon will no longer be in battle.
|
||||
def pbThisEx(idxBattler,idxParty)
|
||||
party = pbParty(idxBattler)
|
||||
if opposes?(idxBattler)
|
||||
return _INTL("The opposing {1}",party[idxParty].name) if trainerBattle?
|
||||
return _INTL("The wild {1}",party[idxParty].name)
|
||||
end
|
||||
return _INTL("The ally {1}",party[idxParty].name) if !pbOwnedByPlayer?(idxBattler)
|
||||
return party[idxParty].name
|
||||
end
|
||||
|
||||
def pbSetSeen(battler)
|
||||
return if !battler || !@internalBattle
|
||||
pbPlayer.seen[battler.displaySpecies] = true
|
||||
pbSeenForm(battler.displaySpecies,battler.displayGender,battler.displayForm)
|
||||
end
|
||||
|
||||
def nextPickupUse
|
||||
@nextPickupUse += 1
|
||||
return @nextPickupUse
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Weather and terrain
|
||||
#=============================================================================
|
||||
def defaultWeather=(value)
|
||||
@field.defaultWeather = value
|
||||
@field.weather = value
|
||||
@field.weatherDuration = -1
|
||||
end
|
||||
|
||||
# Returns the effective weather (note that weather effects can be negated)
|
||||
def pbWeather
|
||||
eachBattler { |b| return PBWeather::None if b.hasActiveAbility?([:CLOUDNINE,:AIRLOCK]) }
|
||||
return @field.weather
|
||||
end
|
||||
|
||||
# Used for causing weather by a move or by an ability.
|
||||
def pbStartWeather(user,newWeather,fixedDuration=false,showAnim=true)
|
||||
return if @field.weather==newWeather
|
||||
@field.weather = newWeather
|
||||
duration = (fixedDuration) ? 5 : -1
|
||||
if duration>0 && user && user.itemActive?
|
||||
duration = BattleHandlers.triggerWeatherExtenderItem(user.item,
|
||||
@field.weather,duration,user,self)
|
||||
end
|
||||
@field.weatherDuration = duration
|
||||
pbCommonAnimation(PBWeather.animationName(@field.weather)) if showAnim
|
||||
pbHideAbilitySplash(user) if user
|
||||
case @field.weather
|
||||
when PBWeather::Sun; pbDisplay(_INTL("The sunlight turned harsh!"))
|
||||
when PBWeather::Rain; pbDisplay(_INTL("It started to rain!"))
|
||||
when PBWeather::Sandstorm; pbDisplay(_INTL("A sandstorm brewed!"))
|
||||
when PBWeather::Hail; pbDisplay(_INTL("It started to hail!"))
|
||||
when PBWeather::HarshSun; pbDisplay(_INTL("The sunlight turned extremely harsh!"))
|
||||
when PBWeather::HeavyRain; pbDisplay(_INTL("A heavy rain began to fall!"))
|
||||
when PBWeather::StrongWinds; pbDisplay(_INTL("Mysterious strong winds are protecting Flying-type Pokémon!"))
|
||||
when PBWeather::ShadowSky; pbDisplay(_INTL("A shadow sky appeared!"))
|
||||
end
|
||||
# Check for end of primordial weather, and weather-triggered form changes
|
||||
eachBattler { |b| b.pbCheckFormOnWeatherChange }
|
||||
pbEndPrimordialWeather
|
||||
end
|
||||
|
||||
def pbEndPrimordialWeather
|
||||
oldWeather = @field.weather
|
||||
# End Primordial Sea, Desolate Land, Delta Stream
|
||||
case @field.weather
|
||||
when PBWeather::HarshSun
|
||||
if !pbCheckGlobalAbility(:DESOLATELAND)
|
||||
@field.weather = PBWeather::None
|
||||
pbDisplay("The harsh sunlight faded!")
|
||||
end
|
||||
when PBWeather::HeavyRain
|
||||
if !pbCheckGlobalAbility(:PRIMORDIALSEA)
|
||||
@field.weather = PBWeather::None
|
||||
pbDisplay("The heavy rain has lifted!")
|
||||
end
|
||||
when PBWeather::StrongWinds
|
||||
if !pbCheckGlobalAbility(:DELTASTREAM)
|
||||
@field.weather = PBWeather::None
|
||||
pbDisplay("The mysterious air current has dissipated!")
|
||||
end
|
||||
end
|
||||
# Check for form changes caused by the weather changing
|
||||
if @field.weather!=oldWeather
|
||||
eachBattler { |b| b.pbCheckFormOnWeatherChange }
|
||||
end
|
||||
end
|
||||
|
||||
def defaultTerrain=(value)
|
||||
@field.defaultTerrain = value
|
||||
@field.terrain = value
|
||||
@field.terrainDuration = -1
|
||||
end
|
||||
|
||||
def pbStartTerrain(user,newTerrain,fixedDuration=true)
|
||||
return if @field.terrain==newTerrain
|
||||
@field.terrain = newTerrain
|
||||
duration = (fixedDuration) ? 5 : -1
|
||||
if duration>0 && user && user.itemActive?
|
||||
duration = BattleHandlers.triggerTerrainExtenderItem(user.item,
|
||||
newTerrain,duration,user,self)
|
||||
end
|
||||
@field.terrainDuration = duration
|
||||
pbCommonAnimation(PBBattleTerrains.animationName(@field.terrain))
|
||||
pbHideAbilitySplash(user) if user
|
||||
case @field.terrain
|
||||
when PBBattleTerrains::Electric
|
||||
pbDisplay(_INTL("An electric current runs across the battlefield!"))
|
||||
when PBBattleTerrains::Grassy
|
||||
pbDisplay(_INTL("Grass grew to cover the battlefield!"))
|
||||
when PBBattleTerrains::Misty
|
||||
pbDisplay(_INTL("Mist swirled about the battlefield!"))
|
||||
when PBBattleTerrains::Psychic
|
||||
pbDisplay(_INTL("The battlefield got weird!"))
|
||||
end
|
||||
# Check for terrain seeds that boost stats in a terrain
|
||||
eachBattler { |b| b.pbItemTerrainStatBoostCheck }
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Messages and animations
|
||||
#=============================================================================
|
||||
def pbDisplay(msg,&block)
|
||||
@scene.pbDisplayMessage(msg,&block)
|
||||
end
|
||||
|
||||
def pbDisplayBrief(msg)
|
||||
@scene.pbDisplayMessage(msg,true)
|
||||
end
|
||||
|
||||
def pbDisplayPaused(msg,&block)
|
||||
@scene.pbDisplayPausedMessage(msg,&block)
|
||||
end
|
||||
|
||||
def pbDisplayConfirm(msg)
|
||||
return @scene.pbDisplayConfirmMessage(msg)
|
||||
end
|
||||
|
||||
def pbShowCommands(msg,commands,canCancel=true)
|
||||
@scene.pbShowCommands(msg,commands,canCancel)
|
||||
end
|
||||
|
||||
def pbAnimation(move,user,targets,hitNum=0)
|
||||
@scene.pbAnimation(move,user,targets,hitNum) if @showAnims
|
||||
end
|
||||
|
||||
def pbCommonAnimation(name,user=nil,targets=nil,hitNum=0)
|
||||
@scene.pbCommonAnimation(name,user,targets,hitNum) if @showAnims
|
||||
end
|
||||
|
||||
def pbShowAbilitySplash(battler,delay=false,logTrigger=true)
|
||||
PBDebug.log("[Ability triggered] #{battler.pbThis}'s #{battler.abilityName}") if logTrigger
|
||||
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@scene.pbShowAbilitySplash(battler)
|
||||
if delay
|
||||
Graphics.frame_rate.times { @scene.pbUpdate } # 1 second
|
||||
end
|
||||
end
|
||||
|
||||
def pbHideAbilitySplash(battler)
|
||||
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@scene.pbHideAbilitySplash(battler)
|
||||
end
|
||||
|
||||
def pbReplaceAbilitySplash(battler)
|
||||
return if !PokeBattle_SceneConstants::USE_ABILITY_SPLASH
|
||||
@scene.pbReplaceAbilitySplash(battler)
|
||||
end
|
||||
end
|
||||
537
Data/Scripts/011_Battle/003_Battle/003_Battle_StartAndEnd.rb
Normal file
537
Data/Scripts/011_Battle/003_Battle/003_Battle_StartAndEnd.rb
Normal file
@@ -0,0 +1,537 @@
|
||||
class PokeBattle_Battle
|
||||
class BattleAbortedException < Exception; end
|
||||
|
||||
def pbAbort
|
||||
raise BattleAbortedException.new("Battle aborted")
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Makes sure all Pokémon exist that need to. Alter the type of battle if
|
||||
# necessary. Will never try to create battler positions, only delete them
|
||||
# (except for wild Pokémon whose number of positions are fixed). Reduces the
|
||||
# size of each side by 1 and tries again. If the side sizes are uneven, only
|
||||
# the larger side's size will be reduced by 1 each time, until both sides are
|
||||
# an equal size (then both sides will be reduced equally).
|
||||
#=============================================================================
|
||||
def pbEnsureParticipants
|
||||
# Prevent battles larger than 2v2 if both sides have multiple trainers
|
||||
# NOTE: This is necessary to ensure that battlers can never become unable to
|
||||
# hit each other due to being too far away. In such situations,
|
||||
# battlers will move to the centre position at the end of a round, but
|
||||
# because they cannot move into a position owned by a different
|
||||
# trainer, it's possible that battlers will be unable to move close
|
||||
# enough to hit each other if there are multiple trainers on each
|
||||
# side.
|
||||
if trainerBattle? && (@sideSizes[0]>2 || @sideSizes[1]>2) &&
|
||||
@player.length>1 && @opponent.length>1
|
||||
raise _INTL("Can't have battles larger than 2v2 where both sides have multiple trainers")
|
||||
end
|
||||
# Find out how many Pokémon each trainer has
|
||||
side1counts = pbAbleTeamCounts(0)
|
||||
side2counts = pbAbleTeamCounts(1)
|
||||
# Change the size of the battle depending on how many wild Pokémon there are
|
||||
if wildBattle? && side2counts[0]!=@sideSizes[1]
|
||||
if @sideSizes[0]==@sideSizes[1]
|
||||
# Even number of battlers per side, change both equally
|
||||
@sideSizes = [side2counts[0],side2counts[0]]
|
||||
else
|
||||
# Uneven number of battlers per side, just change wild side's size
|
||||
@sideSizes[1] = side2counts[0]
|
||||
end
|
||||
end
|
||||
# Check if battle is possible, including changing the number of battlers per
|
||||
# side if necessary
|
||||
loop do
|
||||
needsChanging = false
|
||||
for side in 0...2 # Each side in turn
|
||||
next if side==1 && wildBattle? # Wild side's size already checked above
|
||||
sideCounts = (side==0) ? side1counts : side2counts
|
||||
requireds = []
|
||||
# Find out how many Pokémon each trainer on side needs to have
|
||||
for i in 0...@sideSizes[side]
|
||||
idxTrainer = pbGetOwnerIndexFromBattlerIndex(i*2+side)
|
||||
requireds[idxTrainer] = 0 if requireds[idxTrainer].nil?
|
||||
requireds[idxTrainer] += 1
|
||||
end
|
||||
# Compare the have values with the need values
|
||||
if requireds.length>sideCounts.length
|
||||
raise _INTL("Error: def pbGetOwnerIndexFromBattlerIndex gives invalid owner index ({1} for battle type {2}v{3}, trainers {4}v{5})",
|
||||
requireds.length-1,@sideSizes[0],@sideSizes[1],side1counts.length,side2counts.length)
|
||||
end
|
||||
sideCounts.each_with_index do |count,i|
|
||||
if !requireds[i] || requireds[i]==0
|
||||
raise _INTL("Player-side trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)",
|
||||
i+1,@sideSizes[0],@sideSizes[1]) if side==0
|
||||
raise _INTL("Opposing trainer {1} has no battler position for their Pokémon to go (trying {2}v{3} battle)",
|
||||
i+1,@sideSizes[0],@sideSizes[1]) if side==1
|
||||
end
|
||||
next if requireds[i]<=sideCounts[i] # Trainer has enough Pokémon to fill their positions
|
||||
if requireds[i]==1
|
||||
raise _INTL("Player-side trainer {1} has no able Pokémon",i+1) if side==0
|
||||
raise _INTL("Opposing trainer {1} has no able Pokémon",i+1) if side==1
|
||||
end
|
||||
# Not enough Pokémon, try lowering the number of battler positions
|
||||
needsChanging = true
|
||||
break
|
||||
end
|
||||
break if needsChanging
|
||||
end
|
||||
break if !needsChanging
|
||||
# Reduce one or both side's sizes by 1 and try again
|
||||
if wildBattle?
|
||||
PBDebug.log("#{@sideSizes[0]}v#{@sideSizes[1]} battle isn't possible " +
|
||||
"(#{side1counts} player-side teams versus #{side2counts[0]} wild Pokémon)")
|
||||
newSize = @sideSizes[0]-1
|
||||
else
|
||||
PBDebug.log("#{@sideSizes[0]}v#{@sideSizes[1]} battle isn't possible " +
|
||||
"(#{side1counts} player-side teams versus #{side2counts} opposing teams)")
|
||||
newSize = @sideSizes.max-1
|
||||
end
|
||||
if newSize==0
|
||||
raise _INTL("Couldn't lower either side's size any further, battle isn't possible")
|
||||
end
|
||||
for side in 0...2
|
||||
next if side==1 && wildBattle? # Wild Pokémon's side size is fixed
|
||||
next if @sideSizes[side]==1 || newSize>@sideSizes[side]
|
||||
@sideSizes[side] = newSize
|
||||
end
|
||||
PBDebug.log("Trying #{@sideSizes[0]}v#{@sideSizes[1]} battle instead")
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Set up all battlers
|
||||
#=============================================================================
|
||||
def pbCreateBattler(idxBattler,pkmn,idxParty)
|
||||
if !@battlers[idxBattler].nil?
|
||||
raise _INTL("Battler index {1} already exists",idxBattler)
|
||||
end
|
||||
@battlers[idxBattler] = PokeBattle_Battler.new(self,idxBattler)
|
||||
@positions[idxBattler] = PokeBattle_ActivePosition.new
|
||||
pbClearChoice(idxBattler)
|
||||
@successStates[idxBattler] = PokeBattle_SuccessState.new
|
||||
@battlers[idxBattler].pbInitialize(pkmn,idxParty)
|
||||
end
|
||||
|
||||
def pbSetUpSides
|
||||
ret = [[],[]]
|
||||
for side in 0...2
|
||||
# Set up wild Pokémon
|
||||
if side==1 && wildBattle?
|
||||
pbParty(1).each_with_index do |pkmn,idxPkmn|
|
||||
pbCreateBattler(2*idxPkmn+side,pkmn,idxPkmn)
|
||||
# Changes the Pokémon's form upon entering battle (if it should)
|
||||
@peer.pbOnEnteringBattle(self,pkmn,true)
|
||||
pbSetSeen(@battlers[2*idxPkmn+side])
|
||||
@usedInBattle[side][idxPkmn] = true
|
||||
end
|
||||
next
|
||||
end
|
||||
# Set up player's Pokémon and trainers' Pokémon
|
||||
trainer = (side==0) ? @player : @opponent
|
||||
requireds = []
|
||||
# Find out how many Pokémon each trainer on side needs to have
|
||||
for i in 0...@sideSizes[side]
|
||||
idxTrainer = pbGetOwnerIndexFromBattlerIndex(i*2+side)
|
||||
requireds[idxTrainer] = 0 if requireds[idxTrainer].nil?
|
||||
requireds[idxTrainer] += 1
|
||||
end
|
||||
# For each trainer in turn, find the needed number of Pokémon for them to
|
||||
# send out, and initialize them
|
||||
battlerNumber = 0
|
||||
trainer.each_with_index do |t,idxTrainer|
|
||||
ret[side][idxTrainer] = []
|
||||
eachInTeam(side,idxTrainer) do |pkmn,idxPkmn|
|
||||
next if !pkmn.able?
|
||||
idxBattler = 2*battlerNumber+side
|
||||
pbCreateBattler(idxBattler,pkmn,idxPkmn)
|
||||
ret[side][idxTrainer].push(idxBattler)
|
||||
battlerNumber += 1
|
||||
break if ret[side][idxTrainer].length>=requireds[idxTrainer]
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Send out all battlers at the start of battle
|
||||
#=============================================================================
|
||||
def pbStartBattleSendOut(sendOuts)
|
||||
# "Want to battle" messages
|
||||
if wildBattle?
|
||||
foeParty = pbParty(1)
|
||||
case foeParty.length
|
||||
when 1
|
||||
pbDisplayPaused(_INTL("Oh! A wild {1} appeared!",foeParty[0].name))
|
||||
when 2
|
||||
pbDisplayPaused(_INTL("Oh! A wild {1} and {2} appeared!",foeParty[0].name,
|
||||
foeParty[1].name))
|
||||
when 3
|
||||
pbDisplayPaused(_INTL("Oh! A wild {1}, {2} and {3} appeared!",foeParty[0].name,
|
||||
foeParty[1].name,foeParty[2].name))
|
||||
end
|
||||
else # Trainer battle
|
||||
case @opponent.length
|
||||
when 1
|
||||
pbDisplayPaused(_INTL("You are challenged by {1}!",@opponent[0].fullname))
|
||||
when 2
|
||||
pbDisplayPaused(_INTL("You are challenged by {1} and {2}!",@opponent[0].fullname,
|
||||
@opponent[1].fullname))
|
||||
when 3
|
||||
pbDisplayPaused(_INTL("You are challenged by {1}, {2} and {3}!",
|
||||
@opponent[0].fullname,@opponent[1].fullname,@opponent[2].fullname))
|
||||
end
|
||||
end
|
||||
# Send out Pokémon (opposing trainers first)
|
||||
for side in [1,0]
|
||||
next if side==1 && wildBattle?
|
||||
msg = ""
|
||||
toSendOut = []
|
||||
trainers = (side==0) ? @player : @opponent
|
||||
# Opposing trainers and partner trainers's messages about sending out Pokémon
|
||||
trainers.each_with_index do |t,i|
|
||||
next if side==0 && i==0 # The player's message is shown last
|
||||
msg += "\r\n" if msg.length>0
|
||||
sent = sendOuts[side][i]
|
||||
case sent.length
|
||||
when 1
|
||||
msg += _INTL("{1} sent out {2}!",t.fullname,@battlers[sent[0]].name)
|
||||
when 2
|
||||
msg += _INTL("{1} sent out {2} and {3}!",t.fullname,
|
||||
@battlers[sent[0]].name,@battlers[sent[1]].name)
|
||||
when 3
|
||||
msg += _INTL("{1} sent out {2}, {3} and {4}!",t.fullname,
|
||||
@battlers[sent[0]].name,@battlers[sent[1]].name,@battlers[sent[2]].name)
|
||||
end
|
||||
toSendOut.concat(sent)
|
||||
end
|
||||
# The player's message about sending out Pokémon
|
||||
if side==0
|
||||
msg += "\r\n" if msg.length>0
|
||||
sent = sendOuts[side][0]
|
||||
case sent.length
|
||||
when 1
|
||||
msg += _INTL("Go! {1}!",@battlers[sent[0]].name)
|
||||
when 2
|
||||
msg += _INTL("Go! {1} and {2}!",@battlers[sent[0]].name,@battlers[sent[1]].name)
|
||||
when 3
|
||||
msg += _INTL("Go! {1}, {2} and {3}!",@battlers[sent[0]].name,
|
||||
@battlers[sent[1]].name,@battlers[sent[2]].name)
|
||||
end
|
||||
toSendOut.concat(sent)
|
||||
end
|
||||
pbDisplayBrief(msg) if msg.length>0
|
||||
# The actual sending out of Pokémon
|
||||
animSendOuts = []
|
||||
toSendOut.each do |idxBattler|
|
||||
animSendOuts.push([idxBattler,@battlers[idxBattler].pokemon])
|
||||
end
|
||||
pbSendOut(animSendOuts,true)
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Start a battle
|
||||
#=============================================================================
|
||||
def pbStartBattle
|
||||
PBDebug.log("")
|
||||
PBDebug.log("******************************************")
|
||||
logMsg = "[Started battle] "
|
||||
if @sideSizes[0]==1 && @sideSizes[1]==1
|
||||
logMsg += "Single "
|
||||
elsif @sideSizes[0]==2 && @sideSizes[1]==2
|
||||
logMsg += "Double "
|
||||
elsif @sideSizes[0]==3 && @sideSizes[1]==3
|
||||
logMsg += "Triple "
|
||||
else
|
||||
logMsg += "#{@sideSizes[0]}v#{@sideSizes[1]} "
|
||||
end
|
||||
logMsg += "wild " if wildBattle?
|
||||
logMsg += "trainer " if trainerBattle?
|
||||
logMsg += "battle (#{@player.length} trainer(s) vs. "
|
||||
logMsg += "#{pbParty(1).length} wild Pokémon)" if wildBattle?
|
||||
logMsg += "#{@opponent.length} trainer(s))" if trainerBattle?
|
||||
PBDebug.log(logMsg)
|
||||
pbEnsureParticipants
|
||||
begin
|
||||
pbStartBattleCore
|
||||
rescue BattleAbortedException
|
||||
@decision = 0
|
||||
@scene.pbEndBattle(@decision)
|
||||
end
|
||||
return @decision
|
||||
end
|
||||
|
||||
def pbStartBattleCore
|
||||
# Set up the battlers on each side
|
||||
sendOuts = pbSetUpSides
|
||||
# Create all the sprites and play the battle intro animation
|
||||
@scene.pbStartBattle(self)
|
||||
# Show trainers on both sides sending out Pokémon
|
||||
pbStartBattleSendOut(sendOuts)
|
||||
# Weather announcement
|
||||
pbCommonAnimation(PBWeather.animationName(@field.weather))
|
||||
case @field.weather
|
||||
when PBWeather::Sun; pbDisplay(_INTL("The sunlight is strong."))
|
||||
when PBWeather::Rain; pbDisplay(_INTL("It is raining."))
|
||||
when PBWeather::Sandstorm; pbDisplay(_INTL("A sandstorm is raging."))
|
||||
when PBWeather::Hail; pbDisplay(_INTL("Hail is falling."))
|
||||
when PBWeather::HarshSun; pbDisplay(_INTL("The sunlight is extremely harsh."))
|
||||
when PBWeather::HeavyRain; pbDisplay(_INTL("It is raining heavily."))
|
||||
when PBWeather::StrongWinds; pbDisplay(_INTL("The wind is strong."))
|
||||
when PBWeather::ShadowSky; pbDisplay(_INTL("The sky is shadowy."))
|
||||
end
|
||||
# Terrain announcement
|
||||
pbCommonAnimation(PBBattleTerrains.animationName(@field.terrain))
|
||||
case @field.terrain
|
||||
when PBBattleTerrains::Electric
|
||||
pbDisplay(_INTL("An electric current runs across the battlefield!"))
|
||||
when PBBattleTerrains::Grassy
|
||||
pbDisplay(_INTL("Grass is covering the battlefield!"))
|
||||
when PBBattleTerrains::Misty
|
||||
pbDisplay(_INTL("Mist swirls about the battlefield!"))
|
||||
when PBBattleTerrains::Psychic
|
||||
pbDisplay(_INTL("The battlefield is weird!"))
|
||||
end
|
||||
# Abilities upon entering battle
|
||||
pbOnActiveAll
|
||||
# Main battle loop
|
||||
pbBattleLoop
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Main battle loop
|
||||
#=============================================================================
|
||||
def pbBattleLoop
|
||||
@turnCount = 0
|
||||
loop do # Now begin the battle loop
|
||||
PBDebug.log("")
|
||||
PBDebug.log("***Round #{@turnCount+1}***")
|
||||
if @debug && @turnCount>=100
|
||||
@decision = pbDecisionOnTime
|
||||
PBDebug.log("")
|
||||
PBDebug.log("***Undecided after 100 rounds, aborting***")
|
||||
pbAbort
|
||||
break
|
||||
end
|
||||
PBDebug.log("")
|
||||
# Command phase
|
||||
PBDebug.logonerr { pbCommandPhase }
|
||||
break if @decision>0
|
||||
# Attack phase
|
||||
PBDebug.logonerr { pbAttackPhase }
|
||||
break if @decision>0
|
||||
# End of round phase
|
||||
PBDebug.logonerr { pbEndOfRoundPhase }
|
||||
break if @decision>0
|
||||
@turnCount += 1
|
||||
end
|
||||
pbEndOfBattle
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# End of battle
|
||||
#=============================================================================
|
||||
def pbGainMoney
|
||||
return if !@internalBattle || !@moneyGain
|
||||
# Money rewarded from opposing trainers
|
||||
if trainerBattle?
|
||||
tMoney = 0
|
||||
@opponent.each_with_index do |t,i|
|
||||
tMoney += pbMaxLevelInTeam(1,i)*t.moneyEarned
|
||||
end
|
||||
tMoney *= 2 if @field.effects[PBEffects::AmuletCoin]
|
||||
tMoney *= 2 if @field.effects[PBEffects::HappyHour]
|
||||
oldMoney = pbPlayer.money
|
||||
pbPlayer.money += tMoney
|
||||
moneyGained = pbPlayer.money-oldMoney
|
||||
if moneyGained>0
|
||||
pbDisplayPaused(_INTL("You got ${1} for winning!",moneyGained.to_s_formatted))
|
||||
end
|
||||
end
|
||||
# Pick up money scattered by Pay Day
|
||||
if @field.effects[PBEffects::PayDay]>0
|
||||
@field.effects[PBEffects::PayDay] *= 2 if @field.effects[PBEffects::AmuletCoin]
|
||||
@field.effects[PBEffects::PayDay] *= 2 if @field.effects[PBEffects::HappyHour]
|
||||
oldMoney = pbPlayer.money
|
||||
pbPlayer.money += @field.effects[PBEffects::PayDay]
|
||||
moneyGained = pbPlayer.money-oldMoney
|
||||
if moneyGained>0
|
||||
pbDisplayPaused(_INTL("You picked up ${1}!",moneyGained.to_s_formatted))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbLoseMoney
|
||||
return if !@internalBattle || !@moneyGain
|
||||
return if $game_switches[NO_MONEY_LOSS]
|
||||
maxLevel = pbMaxLevelInTeam(0,0) # Player's Pokémon only, not partner's
|
||||
multiplier = [8,16,24,36,48,64,80,100,120]
|
||||
idxMultiplier = [pbPlayer.numbadges,multiplier.length-1].min
|
||||
tMoney = maxLevel*multiplier[idxMultiplier]
|
||||
tMoney = pbPlayer.money if tMoney>pbPlayer.money
|
||||
oldMoney = pbPlayer.money
|
||||
pbPlayer.money -= tMoney
|
||||
moneyLost = oldMoney-pbPlayer.money
|
||||
if moneyLost>0
|
||||
if trainerBattle?
|
||||
pbDisplayPaused(_INTL("You gave ${1} to the winner...",moneyLost.to_s_formatted))
|
||||
else
|
||||
pbDisplayPaused(_INTL("You panicked and dropped ${1}...",moneyLost.to_s_formatted))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pbEndOfBattle
|
||||
oldDecision = @decision
|
||||
@decision = 4 if @decision==1 && wildBattle? && @caughtPokemon.length>0
|
||||
case oldDecision
|
||||
##### WIN #####
|
||||
when 1
|
||||
PBDebug.log("")
|
||||
PBDebug.log("***Player won***")
|
||||
if trainerBattle?
|
||||
@scene.pbTrainerBattleSuccess
|
||||
case @opponent.length
|
||||
when 1
|
||||
pbDisplayPaused(_INTL("You defeated {1}!",@opponent[0].fullname))
|
||||
when 2
|
||||
pbDisplayPaused(_INTL("You defeated {1} and {2}!",@opponent[0].fullname,
|
||||
@opponent[1].fullname))
|
||||
when 3
|
||||
pbDisplayPaused(_INTL("You defeated {1}, {2} and {3}!",@opponent[0].fullname,
|
||||
@opponent[1].fullname,@opponent[2].fullname))
|
||||
end
|
||||
@opponent.each_with_index do |t,i|
|
||||
@scene.pbShowOpponent(i)
|
||||
msg = (@endSpeeches[i] && @endSpeeches[i]!="") ? @endSpeeches[i] : "..."
|
||||
pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/,pbPlayer.name))
|
||||
end
|
||||
end
|
||||
# Gain money from winning a trainer battle, and from Pay Day
|
||||
pbGainMoney if @decision!=4
|
||||
# Hide remaining trainer
|
||||
@scene.pbShowOpponent(@opponent.length) if trainerBattle? && @caughtPokemon.length>0
|
||||
##### LOSE, DRAW #####
|
||||
when 2, 5
|
||||
PBDebug.log("")
|
||||
PBDebug.log("***Player lost***") if @decision==2
|
||||
PBDebug.log("***Player drew with opponent***") if @decision==5
|
||||
if @internalBattle
|
||||
pbDisplayPaused(_INTL("You have no more Pokémon that can fight!"))
|
||||
if trainerBattle?
|
||||
case @opponent.length
|
||||
when 1
|
||||
pbDisplayPaused(_INTL("You lost against {1}!",@opponent[0].fullname))
|
||||
when 2
|
||||
pbDisplayPaused(_INTL("You lost against {1} and {2}!",
|
||||
@opponent[0].fullname,@opponent[1].fullname))
|
||||
when 3
|
||||
pbDisplayPaused(_INTL("You lost against {1}, {2} and {3}!",
|
||||
@opponent[0].fullname,@opponent[1].fullname,@opponent[2].fullname))
|
||||
end
|
||||
end
|
||||
# Lose money from losing a battle
|
||||
pbLoseMoney
|
||||
pbDisplayPaused(_INTL("You blacked out!")) if !@canLose
|
||||
elsif @decision==2
|
||||
if @opponent
|
||||
@opponent.each_with_index do |t,i|
|
||||
@scene.pbShowOpponent(i)
|
||||
msg = (@endSpeechesWin[i] && @endSpeechesWin[i]!="") ? @endSpeechesWin[i] : "..."
|
||||
pbDisplayPaused(msg.gsub(/\\[Pp][Nn]/,pbPlayer.name))
|
||||
end
|
||||
end
|
||||
end
|
||||
##### CAUGHT WILD POKÉMON #####
|
||||
when 4
|
||||
@scene.pbWildBattleSuccess if !GAIN_EXP_FOR_CAPTURE
|
||||
end
|
||||
# Register captured Pokémon in the Pokédex, and store them
|
||||
pbRecordAndStoreCaughtPokemon
|
||||
# Collect Pay Day money in a wild battle that ended in a capture
|
||||
pbGainMoney if @decision==4
|
||||
# Pass on Pokérus within the party
|
||||
if @internalBattle
|
||||
infected = []
|
||||
$Trainer.party.each_with_index do |pkmn,i|
|
||||
infected.push(i) if pkmn.pokerusStage==1
|
||||
end
|
||||
infected.each do |idxParty|
|
||||
strain = $Trainer.party[idxParty].pokerusStrain
|
||||
if idxParty>0 && $Trainer.party[idxParty-1].pokerusStage==0
|
||||
$Trainer.party[idxParty-1].givePokerus(strain) if rand(3)==0 # 33%
|
||||
end
|
||||
if idxParty<$Trainer.party.length-1 && $Trainer.party[idxParty+1].pokerusStage==0
|
||||
$Trainer.party[idxParty+1].givePokerus(strain) if rand(3)==0 # 33%
|
||||
end
|
||||
end
|
||||
end
|
||||
# Clean up battle stuff
|
||||
@scene.pbEndBattle(@decision)
|
||||
@battlers.each do |b|
|
||||
next if !b
|
||||
pbCancelChoice(b.index) # Restore unused items to Bag
|
||||
BattleHandlers.triggerAbilityOnSwitchOut(b.ability,b,true) if b.abilityActive?
|
||||
end
|
||||
pbParty(0).each_with_index do |pkmn,i|
|
||||
next if !pkmn
|
||||
@peer.pbOnLeavingBattle(self,pkmn,@usedInBattle[0][i],true) # Reset form
|
||||
pkmn.setItem(@initialItems[0][i] || 0)
|
||||
end
|
||||
return @decision
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
# Judging
|
||||
#=============================================================================
|
||||
def pbJudgeCheckpoint(user,move=nil); end
|
||||
|
||||
def pbDecisionOnTime
|
||||
counts = [0,0]
|
||||
hpTotals = [0,0]
|
||||
for side in 0...2
|
||||
pbParty(side).each do |pkmn|
|
||||
next if !pkmn || !pkmn.able?
|
||||
counts[side] += 1
|
||||
hpTotals[side] += pkmn.hp
|
||||
end
|
||||
end
|
||||
return 1 if counts[0]>counts[1] # Win (player has more able Pokémon)
|
||||
return 2 if counts[0]<counts[1] # Loss (foe has more able Pokémon)
|
||||
return 1 if hpTotals[0]>hpTotals[1] # Win (player has more HP in total)
|
||||
return 2 if hpTotals[0]<hpTotals[1] # Loss (foe has more HP in total)
|
||||
return 5 # Draw
|
||||
end
|
||||
|
||||
# Unused
|
||||
def pbDecisionOnTime2
|
||||
counts = [0,0]
|
||||
hpTotals = [0,0]
|
||||
for side in 0...2
|
||||
pbParty(side).each do |pkmn|
|
||||
next if !pkmn || !pkmn.able?
|
||||
counts[side] += 1
|
||||
hpTotals[side] += 100*pkmn.hp/pkmn.totalhp
|
||||
end
|
||||
hpTotals[side] /= counts[side] if counts[side]>1
|
||||
end
|
||||
return 1 if counts[0]>counts[1] # Win (player has more able Pokémon)
|
||||
return 2 if counts[0]<counts[1] # Loss (foe has more able Pokémon)
|
||||
return 1 if hpTotals[0]>hpTotals[1] # Win (player has a bigger average HP %)
|
||||
return 2 if hpTotals[0]<hpTotals[1] # Loss (foe has a bigger average HP %)
|
||||
return 5 # Draw
|
||||
end
|
||||
|
||||
def pbDecisionOnDraw; return 5; end # Draw
|
||||
|
||||
def pbJudge
|
||||
fainted1 = pbAllFainted?(0)
|
||||
fainted2 = pbAllFainted?(1)
|
||||
if fainted1 && fainted2; @decision = pbDecisionOnDraw # Draw
|
||||
elsif fainted1; @decision = 2 # Loss
|
||||
elsif fainted2; @decision = 1 # Win
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user